XSLT dynamically filter on multiple elements

2019-08-29 12:34发布

问题:

I'm new to XSLT and trying to figure out how to use it to turn this XML:

<people>
    <person>
        <role>Parent</role>
        <lastname>Smith</lastname>
        <locations>
            <location>
                <city>Springfield</city>
                <state>MA</state>
                <unimportant-field>123</unimportant-field>
            </location>
            <location>
                <city>Chicago</city>
                <state>IL</state>
                <unimportant-field>456</unimportant-field>
            </location>
        </locations>
    </person>
    <person>
        <role>Child</role>
        <lastname>Smith</lastname>
        <locations>
            <location>
                <city>Springfield</city>
                <state>IL</state>
                <unimportant-field>789</unimportant-field>
            </location>
            <location>
                <city>Chicago</city>
                <state>IL</state>
                <unimportant-field>000</unimportant-field>
            </location>
            <location>
                <city>Washington</city>
                <state>DC</state>
                <unimportant-field>555</unimportant-field>
            </location>
        </locations>
    </person>
</people>

Into

<people>
    <person>
        <role>Parent</role>
        <lastname>Smith</lastname>
        <locations>
            <location>
                <city>Springfield</city>
                <state>MA</state>
                <unimportant-field>123</unimportant-field>
            </location>
            <location>
                <city>Chicago</city>
                <state>IL</state>
                <unimportant-field>456</unimportant-field>
            </location>
        </locations>
    </person>
    <person>
        <role>Child</role>
        <lastname>Smith</lastname>
        <locations>
            <location>
                <city>Chicago</city>
                <state>IL</state>
                <unimportant-field>000</unimportant-field>
            </location>
        </locations>
    </person>
</people>

Basically what is going on is that there is a pseudo hierarchy where I need to filter out locations on the "child" elements that don't also exist in the "parent" element. The child elements are related to their parent by lastname. However there is "unimportant-field" in each location that won't match between parent and child, but shouldn't affect the filtering.

Right now I'm trying to use this:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="/people">
        <people>
          <xsl:apply-templates select="person"/>
        </people>
    </xsl:template>

    <xsl:template match="person">
        <person>
            <xsl:copy-of select="role" /> 
            <xsl:copy-of select="lastname" /> 
            <xsl:apply-templates select="locations"/>
        </person>
    </xsl:template>

    <xsl:template match="locations">
        <locations>
            <xsl:apply-templates select="location[city = /people/person[role = 'Parent' and lastname = current()/../lastname]/locations/location/city and state = /people/person[role = 'Parent' and lastname = current()/../lastname]/locations/location/state]"/>
        </locations>
    </xsl:template>

    <xsl:template match="location">
        <location>
            <xsl:copy-of select="city" />
            <xsl:copy-of select="state" />
            <xsl:copy-of select="unimportant-field" />
        </location>
    </xsl:template>

</xsl:stylesheet>

The issue is that it is retaining the Springfield, IL element in the child because it is independently checking if the city exists anywhere in the parent and if the state exists anywhere in the parent. What I need to do is figure out if the combination of city and state exists on single location in the parent, while still ignoring "unimportant-field". I'm not sure how to do that though.

回答1:

Try it this way?

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:key name="parent-location" match="person[role='Parent']/locations/location" use="concat(../../lastname, '|', city, '|', state)" />

<!-- identity transform -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="person[role='Child']/locations/location[not(key('parent-location', concat(../../lastname, '|', city, '|', state)))]"/>

</xsl:stylesheet>


标签: xml xslt