Skip to content

Commit

Permalink
Generate namespaced element on XmlList entries
Browse files Browse the repository at this point in the history
  • Loading branch information
goetas committed May 5, 2016
1 parent 2b16866 commit e5df8e3
Show file tree
Hide file tree
Showing 20 changed files with 370 additions and 67 deletions.
2 changes: 2 additions & 0 deletions doc/reference/annotations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,8 @@ Resulting XML:
</comment>
</post>
You can also specify the entry tag namespace using the ``namespace`` attribute (``@XmlList(inline = true, entry = "comment", namespace="http://www.example.com/ns")``).

@XmlMap
~~~~~~~
Similar to @XmlList, but the keys of the array are meaningful.
Expand Down
2 changes: 1 addition & 1 deletion doc/reference/xml_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ XML Reference
your type contains "<" or ">" characters. -->
<type><![CDATA[]]></type>
<xml-list inline="true" entry-name="foobar" />
<xml-map inline="true" key-attribute-name="foo" entry-name="bar" />
<xml-map inline="true" key-attribute-name="foo" entry-name="bar" namespace="http://www.w3.org/2005/Atom" />
<xml-element cdata="false" namespace="http://www.w3.org/2005/Atom"/>
</property>
<callback-method name="foo" type="pre-serialize" />
Expand Down
2 changes: 2 additions & 0 deletions doc/reference/yml_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,12 @@ YAML Reference
xml_list:
inline: true
entry_name: foo
namespace: http://www.w3.org/2005/Atom
xml_map:
inline: true
key_attribute_name: foo
entry_name: bar
namespace: http://www.w3.org/2005/Atom
xml_attribute_map: true
xml_element:
cdata: false
Expand Down
5 changes: 5 additions & 0 deletions src/JMS/Serializer/Annotation/XmlCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,9 @@ abstract class XmlCollection
* @var boolean
*/
public $inline = false;

/**
* @var string
*/
public $namespace;
}
2 changes: 2 additions & 0 deletions src/JMS/Serializer/Metadata/Driver/AnnotationDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,12 @@ public function loadMetadataForClass(\ReflectionClass $class)
$propertyMetadata->xmlCollection = true;
$propertyMetadata->xmlCollectionInline = $annot->inline;
$propertyMetadata->xmlEntryName = $annot->entry;
$propertyMetadata->xmlEntryNamespace = $annot->namespace;
} elseif ($annot instanceof XmlMap) {
$propertyMetadata->xmlCollection = true;
$propertyMetadata->xmlCollectionInline = $annot->inline;
$propertyMetadata->xmlEntryName = $annot->entry;
$propertyMetadata->xmlEntryNamespace = $annot->namespace;
$propertyMetadata->xmlKeyAttribute = $annot->keyAttribute;
} elseif ($annot instanceof XmlKeyValuePairs) {
$propertyMetadata->xmlKeyValuePairs = true;
Expand Down
8 changes: 8 additions & 0 deletions src/JMS/Serializer/Metadata/Driver/XmlDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,10 @@ protected function loadMetadataFromFile(\ReflectionClass $class, $path)
if (isset($colConfig->attributes()->{'entry-name'})) {
$pMetadata->xmlEntryName = (string) $colConfig->attributes()->{'entry-name'};
}

if (isset($colConfig->attributes()->namespace)) {
$pMetadata->xmlEntryNamespace = (string) $colConfig->attributes()->namespace;
}
}

if (isset($pElem->{'xml-map'})) {
Expand All @@ -186,6 +190,10 @@ protected function loadMetadataFromFile(\ReflectionClass $class, $path)
$pMetadata->xmlEntryName = (string) $colConfig->attributes()->{'entry-name'};
}

if (isset($colConfig->attributes()->namespace)) {
$pMetadata->xmlEntryNamespace = (string) $colConfig->attributes()->namespace;
}

if (isset($colConfig->attributes()->{'key-attribute-name'})) {
$pMetadata->xmlKeyAttribute = (string) $colConfig->attributes()->{'key-attribute-name'};
}
Expand Down
9 changes: 9 additions & 0 deletions src/JMS/Serializer/Metadata/Driver/YamlDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ protected function loadMetadataFromFile(\ReflectionClass $class, $file)
if (isset($colConfig['entry_name'])) {
$pMetadata->xmlEntryName = (string) $colConfig['entry_name'];
}

if (isset($colConfig['namespace'])) {
$pMetadata->xmlEntryNamespace = (string) $colConfig['namespace'];
}
}

if (isset($pConfig['xml_map'])) {
Expand All @@ -133,9 +137,14 @@ protected function loadMetadataFromFile(\ReflectionClass $class, $file)
$pMetadata->xmlEntryName = (string) $colConfig['entry_name'];
}

if (isset($colConfig['namespace'])) {
$pMetadata->xmlEntryNamespace = (string) $colConfig['namespace'];
}

if (isset($colConfig['key_attribute_name'])) {
$pMetadata->xmlKeyAttribute = $colConfig['key_attribute_name'];
}

}

if (isset($pConfig['xml_element'])) {
Expand Down
1 change: 1 addition & 0 deletions src/JMS/Serializer/Metadata/PropertyMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class PropertyMetadata extends BasePropertyMetadata
public $xmlCollection = false;
public $xmlCollectionInline = false;
public $xmlEntryName;
public $xmlEntryNamespace;
public $xmlKeyAttribute;
public $xmlAttribute = false;
public $xmlValue = false;
Expand Down
83 changes: 46 additions & 37 deletions src/JMS/Serializer/XmlDeserializationVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class XmlDeserializationVisitor extends AbstractVisitor
{
private $objectStack;
private $metadataStack;
private $objectMetadataStack;
private $currentObject;
private $currentMetadata;
private $result;
Expand All @@ -46,6 +47,7 @@ public function setNavigator(GraphNavigator $navigator)
$this->navigator = $navigator;
$this->objectStack = new \SplStack;
$this->metadataStack = new \SplStack;
$this->objectMetadataStack = new \SplStack;
$this->result = null;
}

Expand Down Expand Up @@ -74,6 +76,7 @@ public function prepare($data)
}

$doc = simplexml_load_string($data);

libxml_use_internal_errors($previous);
libxml_disable_entity_loader($previousEntityLoaderState);

Expand Down Expand Up @@ -144,8 +147,14 @@ public function visitDouble($data, array $type, Context $context)
public function visitArray($data, array $type, Context $context)
{
$entryName = null !== $this->currentMetadata && $this->currentMetadata->xmlEntryName ? $this->currentMetadata->xmlEntryName : 'entry';
$namespace = null !== $this->currentMetadata && $this->currentMetadata->xmlEntryNamespace ? $this->currentMetadata->xmlEntryNamespace : null;

if ($namespace === null && $this->objectMetadataStack->count()) {
$classMetadata = $this->objectMetadataStack->top();
$namespace = isset($classMetadata->xmlNamespaces[''])?$classMetadata->xmlNamespaces['']:$namespace;
}

if ( ! isset($data->$entryName)) {
if ( ! isset($data->$entryName) ) {
if (null === $this->result) {
return $this->result = array();
}
Expand All @@ -159,11 +168,13 @@ public function visitArray($data, array $type, Context $context)

case 1:
$result = array();

if (null === $this->result) {
$this->result = &$result;
}

foreach ($data->$entryName as $v) {
$nodes = $data->children($namespace)->$entryName;
foreach ($nodes as $v) {
$result[] = $this->navigator->accept($v, $type['params'][0], $context);
}

Expand All @@ -180,12 +191,14 @@ public function visitArray($data, array $type, Context $context)
$this->result = &$result;
}

foreach ($data->$entryName as $v) {
if ( ! isset($v[$this->currentMetadata->xmlKeyAttribute])) {
$nodes = $data->children($namespace)->$entryName;
foreach ($nodes as $v) {
$attrs = $v->attributes();
if ( ! isset($attrs[$this->currentMetadata->xmlKeyAttribute])) {
throw new RuntimeException(sprintf('The key attribute "%s" must be set for each entry of the map.', $this->currentMetadata->xmlKeyAttribute));
}

$k = $this->navigator->accept($v[$this->currentMetadata->xmlKeyAttribute], $keyType, $context);
$k = $this->navigator->accept($attrs[$this->currentMetadata->xmlKeyAttribute], $keyType, $context);
$result[$k] = $this->navigator->accept($v, $entryType, $context);
}

Expand All @@ -199,7 +212,7 @@ public function visitArray($data, array $type, Context $context)
public function startVisitingObject(ClassMetadata $metadata, $object, array $type, Context $context)
{
$this->setCurrentObject($object);

$this->objectMetadataStack->push($metadata);
if (null === $this->result) {
$this->result = $this->currentObject;
}
Expand All @@ -213,22 +226,11 @@ public function visitProperty(PropertyMetadata $metadata, $data, Context $contex
throw new RuntimeException(sprintf('You must define a type for %s::$%s.', $metadata->reflection->class, $metadata->name));
}

if ($metadata->xmlAttribute) {
if ('' !== $namespace = (string) $metadata->xmlNamespace) {
$registeredNamespaces = $data->getDocNamespaces();
if (false === $prefix = array_search($namespace, $registeredNamespaces)) {
$prefix = uniqid('ns-');
$data->registerXPathNamespace($prefix, $namespace);
}
$attributeName = ($prefix === '') ? $name : $prefix.':'.$name;
$nodes = $data->xpath('./@'.$attributeName);
if ( ! empty($nodes)) {
$v = (string) reset($nodes);
$metadata->reflection->setValue($this->currentObject, $v);
}
if ($metadata->xmlAttribute) {

} elseif (isset($data[$name])) {
$v = $this->navigator->accept($data[$name], $metadata->type, $context);
$attributes = $data->attributes($metadata->xmlNamespace);
if (isset($attributes[$name])) {
$v = $this->navigator->accept($attributes[$name], $metadata->type, $context);
$metadata->reflection->setValue($this->currentObject, $v);
}

Expand All @@ -244,8 +246,8 @@ public function visitProperty(PropertyMetadata $metadata, $data, Context $contex

if ($metadata->xmlCollection) {
$enclosingElem = $data;
if ( ! $metadata->xmlCollectionInline && isset($data->$name)) {
$enclosingElem = $data->$name;
if (!$metadata->xmlCollectionInline) {
$enclosingElem = $data->children($metadata->xmlNamespace)->$name;
}

$this->setCurrentMetadata($metadata);
Expand All @@ -256,23 +258,29 @@ public function visitProperty(PropertyMetadata $metadata, $data, Context $contex
return;
}

if ('' !== $namespace = (string) $metadata->xmlNamespace) {
$registeredNamespaces = $data->getDocNamespaces();
if (false === $prefix = array_search($namespace, $registeredNamespaces)) {
$prefix = uniqid('ns-');
$data->registerXPathNamespace($prefix, $namespace);
}
$elementName = ($prefix === '') ? $name : $prefix.':'.$name;
$nodes = $data->xpath('./'.$elementName);
if (empty($nodes)) {
if ($metadata->xmlNamespace) {
$node = $data->children($metadata->xmlNamespace)->$name;
if (!$node->count()) {
return;
}
$node = reset($nodes);
} else {
if ( ! isset($data->$name)) {
return;

$namespaces = $data->getDocNamespaces();

if (isset($namespaces[''])) {
$prefix = uniqid('ns-');
$data->registerXPathNamespace($prefix, $namespaces['']);
$nodes = $data->xpath('./'.$prefix. ':'.$name );
if (empty($nodes)) {
return;
}
$node = reset($nodes);
} else {
if (!isset($data->$name)) {
return;
}
$node = $data->$name;
}
$node = $data->$name;
}

$v = $this->navigator->accept($node, $metadata->type, $context);
Expand All @@ -289,6 +297,7 @@ public function visitProperty(PropertyMetadata $metadata, $data, Context $contex
public function endVisitingObject(ClassMetadata $metadata, $data, array $type, Context $context)
{
$rs = $this->currentObject;
$this->objectMetadataStack->pop();
$this->revertCurrentObject();

return $rs;
Expand Down Expand Up @@ -359,7 +368,7 @@ private function getDomDocumentTypeEntitySubset(\DOMDocumentType $child, $data)
if (null !== $child->internalSubset) {
return str_replace(array("\n", "\r"), '', $child->internalSubset);
}

$startPos = $endPos = stripos($data, '<!doctype');
$braces = 0;
do {
Expand Down
Loading

0 comments on commit e5df8e3

Please sign in to comment.