Skip to content

Commit

Permalink
update deserializer
Browse files Browse the repository at this point in the history
  • Loading branch information
gavg-vaioni committed Sep 16, 2024
1 parent ffda7ca commit 0a7a7ca
Showing 1 changed file with 77 additions and 57 deletions.
134 changes: 77 additions & 57 deletions src/XmlDeserializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,90 +6,110 @@

use DOMDocument;
use DOMElement;
use DOMNode;
use DOMText;

class XmlDeserializer
class XmlSerializer
{
public static string $defaultClass = 'stdClass';
private string $targetNamespace;

public function __construct(
private DOMDocument $document = new DOMDocument('1.0', 'UTF-8'),
) {
$this->document->preserveWhiteSpace = false;
}
) {}

public function __invoke(string $targetNamespace, string $xml, string|null $class = null): array|bool|float|int|object|string|null
public function __invoke(mixed $var, string|null $tag = null): string
{
$this->targetNamespace = $targetNamespace;
$this->document->loadXML($xml);
// SOAP envelope element and attributes
$soap = $this->document->createElementNS(
'http://schemas.xmlsoap.org/soap/envelope/',
'soap:Envelope',
);
$this->document->appendChild($soap);

$node = $this->document->documentElement->nodeName === 'soap:Envelope'
? $this->document->documentElement->firstChild->firstChild
: $this->document->documentElement;
$soap->setAttributeNS(
'http://www.w3.org/2000/xmlns/',
'xmlns:soap',
'http://schemas.xmlsoap.org/soap/envelope/',
);

// SOAP body
$body = $this->document->createElementNS(
'http://schemas.xmlsoap.org/soap/envelope/',
'soap:Body',
);
$soap->appendChild($body);

return $this->deserializeElement($node, $class);
// XML
$element = $this->document->createElement(empty($tag) ? $this->getTag($var) : $tag);

$body->appendChild(
$this->serializeElement(
$var,
$element,
),
);

return $this->document->saveXML();
}

private function deserializeObject(DOMElement $node, string|null $class = null): object
private function serializeObject(object $var, DOMElement &$node): DOMElement
{
if (! class_exists($class)) {
$class = static::$defaultClass;
}
$getters = $var::getters();

$object = new $class();
foreach ($var::attributeMap() as $property => $name) {
$getter = $getters[$property];

if (method_exists($object, 'setters')) {
$setters = $class::setters();
$attributeMap = array_flip($class::attributeMap());
$value = $var->$getter();

foreach ($node->childNodes as $child) {
$attribute = $attributeMap[$child->nodeName];
$setter = $setters[$attribute] ?? reset($setters);
$object->$setter($this->deserializeElement($child));
if ($value === null) {
continue;
}

return $object;
}
$element = $this->document->createElement($name);

foreach ($node->childNodes as $child) {
$object->{$child->nodeName} = $this->deserializeElement($child);
$node->appendChild(
$this->serializeElement(
$value,
$element,
),
);
}

return $object;
return $node;
}

private function deserializeElement(
DOMNode $node,
string|null $class = null,
): array|bool|float|int|object|string {
if ($node instanceof DOMText || $node->childElementCount === 0) {
return $node->nodeValue;
}

if ($class !== null) {
// If a class is given, assume we need to deserialize an object
return $this->deserializeObject($node, $class);
}
private function serializeElement(mixed $var, DOMElement &$node): DOMElement
{
return match (gettype($var)) {
'object' => $this->serializeObject($var, $node),
'array' => $this->serializeArray($var, $node),
default => $this->serializeVar($var, $node),
};
}

// Guess the class name if none is given
$class = $this->targetNamespace . '\\' . $node->nodeName;
private function getTag(mixed $var): string
{
return is_object($var)
? (new $var())->getModelName()
: gettype($var);
}

if (class_exists($class)) {
return $this->deserializeObject($node, $class);
/** @param mixed[] $array */
private function serializeArray(array $array, DOMElement &$node): DOMElement
{
foreach ($array as $key => $value) {
$element = $node->ownerDocument->createElement('element');
$element->setAttribute('s:key', $key);
$node->appendChild($this->serializeElement($value, $element));
}

return $this->deserializeArray($node);
return $node;
}

/** @return mixed[] */
private function deserializeArray(DOMElement $node): array
private function serializeVar(mixed $var, DOMElement &$element): DOMElement
{
return array_reduce(
iterator_to_array($node->childNodes),
fn($result, $node) => $result + [$node->nodeName => $this->deserializeElement($node)],
[]
);
if (is_bool($var)) {
$element->nodeValue = $var ? 'true' : 'false';
} else {
$element->nodeValue = (string) $var;
}

return $element;
}
}

0 comments on commit 0a7a7ca

Please sign in to comment.