Search code examples
xmlxsltxsdxslt-3.0

XSI Namespace Copied To Result Sequence


I have an XML file containing a bunch of look-ups. A simplified version below:

<?xml version="1.0" encoding="UTF-8"?>
<Parties xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="/usr/local/share/xml/schema/quantile/Parties.xsd">
    <Party name="Bens Birthday">
        <ZIP>AB1234</ZIP>
        <EMAIL>[email protected]</EMAIL>
        <ATTENDEES>
            <ATTENDEE>Bob</ATTENDEE>
            <ATTENDEE>David</ATTENDEE>
        </ATTENDEES>
    </Party>
</Parties>

I've written a simple XSD to validate this data:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="Parties">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="Party" maxOccurs="unbounded">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name="ZIP" minOccurs="0" maxOccurs="1" type="xs:string"/>
                            <xs:element name="EMAIL" minOccurs="0" maxOccurs="1" type="xs:string"/>
                            <xs:element name="ATTENDEES" minOccurs="0" maxOccurs="1">
                                <xs:complexType>
                                    <xs:sequence>
                                        <xs:element name="ATTENDEE" minOccurs="1" maxOccurs="unbounded" type="xs:string"/>
                                    </xs:sequence>
                                </xs:complexType>
                             </xs:element>
                        </xs:sequence>
                        <xs:attribute name="name" type="xs:string" use="required"/>
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

This works well.

I'm loading the XML file into an XSLT and using it as a look-up within the transform.

<xsl:variable name="party-details" select="document($party-file)/Parties"/>
<xsl:variable name="party-map" select="$party-details/Party[@name='Bens Birthday']"/>

This works well when I'm interrogating a text node:

<FOO>{$party-map/EMAIL}</FOO>

But if I try to do the same with a sequence:

<xsl:sequence select="$party-map/ATTENDEES/ATTENDEE"/>

I inherit the namespace definition from the Party XML File:

<Parties xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="/usr/local/share/xml/schema/quantile/Parties.xsd">

Resulting in output like this:

<ATTENDEE xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Bob</ATTENDEE>

Obviously if I remove the xmlns:xsi and xsi:noNamespaceSchemaLocation from the Party XML file, the problem goes away... but then I'm not validating it.

Ultimately this breaks my XSLT output validation as the xsi definition is not allowed on the ATTENDEE element.

I kinda see what's happening here - all children of Parties inherit the namespace xmlns:xsi, so when I splice the ATTENDEE sequence into my XSLT output, the lack of an xsi definition in the XSLT means the processor adds it in. It doesn't do this for text nodes, I assume, because they have no concept of namespace, only the element carries this.

I can move the problem by defining xmlns:xsi elsewhere on an output element and parent of ATTENDEE in the XSLT.

This is still putting constraints on my output format tho. It feels like I should be able to strip the namespace out, but I'm at a loss on how to proceed.

Any ideas? Or if someone can categorically say this is impossible, any other suggestions - like use DTD instead, or leave the schema off the XML completely, and do the validation inside the XSLT perhaps? I'm guessing now!


Solution

  • Don't use xsl:sequence then, use xsl:copy-of copy-namespaces="no" e.g.

    <xsl:copy-of select="Parties/Party[@name = 'Bens Birthday']/ATTENDEES" copy-namespaces="no"/>
    

    https://xsltfiddle.liberty-development.net/bdxtpU uses your sample as the primary input but of course you can do the same with your <xsl:copy-of select="$party-map/ATTENDEES/ATTENDEE" copy-namespaces="no"/>.