Skip to content

fix(YamlConverter): respect form="unqualified" on individual elements in getElementNamespace - #195

Open
XenosEleatikos wants to merge 1 commit into
goetas-webservices:masterfrom
XenosEleatikos:fix/respect-form-unqualified-in-get-element-namespace
Open

fix(YamlConverter): respect form="unqualified" on individual elements in getElementNamespace#195
XenosEleatikos wants to merge 1 commit into
goetas-webservices:masterfrom
XenosEleatikos:fix/respect-form-unqualified-in-get-element-namespace

Conversation

@XenosEleatikos

@XenosEleatikos XenosEleatikos commented Mar 17, 2026

Copy link
Copy Markdown

Problem

YamlConverter::getElementNamespace assigns a namespace to an element even when that element explicitly declares form="unqualified" in the XSD.

The buggy condition:

protected function getElementNamespace(Schema $schema, ElementItem $element)
{
    if ($element->getSchema()->getTargetNamespace() &&
        ($schema->getElementsQualification()   // ← this OR-branch is the 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, which short-circuits the entire condition. This means an element that explicitly declares form="unqualified" still gets a namespace entry in the generated JMS YAML metadata — and is then serialized with a namespace prefix by JMS Serializer, violating the XSD contract.

The xsd-reader correctly reads form="unqualified" and calls setQualified(false) on the element. That correct result is silently overridden by the OR condition.

Reproducer

XSD with elementFormDefault="qualified" and a local element that overrides with form="unqualified":

<!-- parent 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'   # should not be here
name:
    xml_element:
        namespace: 'urn:example:schema'   # should not be here

This causes JMS Serializer to produce <ns:code> and <ns:name> instead of <code> and <name>, which validators reject.

Fix

Remove $schema->getElementsQualification() from the OR condition. $element->isQualified() already incorporates the schema-level elementFormDefault as a fallback when no explicit form attribute is present, so the removed check was 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())
         ) {

Behaviour matrix

Schema elementFormDefault Element form attribute 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 ✓

… in getElementNamespace

When a schema has elementFormDefault="qualified" but an individual element
explicitly declares form="unqualified", getElementNamespace incorrectly
assigned a namespace to that element via the $schema->getElementsQualification()
OR branch, overriding the element's own correct isQualified()=false result.

The XÖV/XÖV-Standard xoev-code pattern relies on this distinction: all
code/name child elements are declared form="unqualified" for cross-schema
interoperability, even when the containing schema uses elementFormDefault="qualified".

The fix removes $schema->getElementsQualification() from the condition entirely.
$element->isQualified() already incorporates the schema-level default as a
fallback when no explicit form attribute is present, so the removed condition
was both redundant and incorrect.

Reproducer: any XSD where elementFormDefault="qualified" and a local element
has form="unqualified"; before this fix the generated JMS YAML metadata would
include an erroneous namespace entry for that element.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants