Skip to content

YamlConverter::getElementNamespace ignores form="unqualified" when schema has elementFormDefault="qualified" #196

Description

@XenosEleatikos

Bug description

YamlConverter::getElementNamespace assigns a namespace to a local element even when that element explicitly declares form="unqualified" in the XSD, producing invalid JMS Serializer metadata.

Root cause

protected function getElementNamespace(Schema $schema, ElementItem $element)
{
    if ($element->getSchema()->getTargetNamespace() &&
        ($schema->getElementsQualification()   // ← bug
            || ($element instanceof Element && $element->isQualified())
            || !$element->isLocal())
    ) {
        return $element->getSchema()->getTargetNamespace();
    }
    return null;
}

When the containing schema has elementFormDefault="qualified", $schema->getElementsQualification() returns true and short-circuits the entire OR. An element that explicitly declares form="unqualified" still receives a namespace entry in the generated YAML — the xsd-reader's correct isQualified()=false result is silently ignored.

Reproducer

<!-- schema: elementFormDefault="qualified" -->
<xs:complexType name="CodeType">
  <xs:sequence>
    <xs:element name="code" type="xs:token"            form="unqualified"/>
    <xs:element name="name" type="xs:normalizedString" form="unqualified" minOccurs="0"/>
  </xs:sequence>
</xs:complexType>

Generated YAML (wrong):

code:
    xml_element:
        namespace: 'urn:example:schema'   # must not be here
name:
    xml_element:
        namespace: 'urn:example:schema'   # must not be here

JMS Serializer then produces <ns:code> / <ns:name> instead of <code> / <name>, which schema validators reject with:

Invalid content was found starting with element '{urn:example:schema}name'. One of '{name}' is expected.

Expected behaviour

Elements with an explicit form="unqualified" must not receive a namespace entry, regardless of the parent schema's elementFormDefault.

Suggested fix

Remove $schema->getElementsQualification() from the condition. $element->isQualified() already incorporates elementFormDefault as a fallback when no explicit form attribute is present, making the removed check both redundant and incorrect.

-    protected function getElementNamespace(Schema $schema, ElementItem $element)
+    protected function getElementNamespace(Schema $_schema, ElementItem $element)
     {
         if ($element->getSchema()->getTargetNamespace() &&
-            ($schema->getElementsQualification() || ($element instanceof Element && $element->isQualified()) || !$element->isLocal())
+            (($element instanceof Element && $element->isQualified()) || !$element->isLocal())
         ) {

A PR with this fix is already open: #195

Behaviour matrix after fix

Schema elementFormDefault Element form Before After
qualified (none) namespace set ✓ namespace set ✓
qualified form="unqualified" namespace set ✗ no namespace ✓
qualified form="qualified" namespace set ✓ namespace set ✓
unqualified (none) no namespace ✓ no namespace ✓
unqualified form="qualified" namespace set ✓ namespace set ✓

Context

Discovered while generating JMS metadata for the German XÖV/XÖV-Standard xoev-code.xsd, where code and name child elements are intentionally form="unqualified" for cross-schema interoperability — a common pattern in German e-government standards.

Version

xsd2php 0.4.13

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions