Search code examples
xsltinheritancexsdxslt-2.0extend

Match nodes inheriting from type in XSLT


We have an XSD which defines the following:

<xsd:element name="Product" type="cm:Product" abstract="true" />
<xsd:complexType name="Product" abstract="true">
    <xsd:sequence>
        <xsd:element name="name" type="xsd:string" minOccurs="0" />
        <!-- Other common elements -->
    </xsd:sequence>
</xsd:complexType>

<xsd:element name="Subscription" type="cm:Subscription" />
<xsd:complexType name="Subscription">
    <xsd:complexContent>
        <xsd:extension base="cm:Product">
            <xsd:sequence>
                <!-- Subscription specific elements -->
            </xsd:sequence>
        </xsd:extension>
    </xsd:complexContent>
</xsd:complexType>

I need to create an XSLT which takes the product name and a few other things and convert it into a web service request. The problem is that I don't really know if the top element actually says cm:Product, cm:Subscription or something completely different (but which extends cm:Product).

Is there a way I can somehow write a template which matches both cm:Product elements and all elements extending cm:Product?


Simple example input

<Subscription xmlns="http://schema"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <name>Basis</name>
</Subscription>

What I have so far

<xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:cm="http://schema" 
    exclude-result-prefixes="cm">

    <xsl:param name="processID" />

    <xsl:template match="/">
        <xsl:element name="RequestElement">
            <xsl:element name="processId">
                <xsl:value-of select="$processID" />
            </xsl:element>
            <xsl:apply-templates />
        </xsl:element>
    </xsl:template>

    <xsl:template match="cm:Product/cm:name">
        <xsl:element name="name">
            <xsl:value-of select="." />
        </xsl:element>
    </xsl:template>

    <xsl:template match="text()" />

</xsl:stylesheet>

This works if I change cm:Product to cm:Subscription, which is in my particular input xml, but the problem is that I can't know that it actually is a cm:Subscription. All I "know" is that it is an element extending cm:Product


Solution

  • <xsl:template match="element(*, cm:Product)/cm:name">
    

    should do it, but you'll need to add a suitable top-level

    <xsl:import-schema namespace="https://schema" schema-location="....." />
    

    and of course it requires that your processor is schema-aware.