开发者

xsl to only copy some elements recursively and remove some of the descendants

开发者 https://www.devze.com 2023-04-01 07:40 出处:网络
I would like to transform <?xml version=\"1.0\" ?> <mydoc> <file> <colors> <blue />

I would like to transform

<?xml version="1.0" ?>
<mydoc>
    <file>
        <colors>
            <blue />
            <red />
            <green />
        </colors>
        <secret>
            <username />
            <password />
        </secret>
    </file>
</mydoc>

into

<?xml version="1.0" ?>
<colors>
  <blue />
  <red />
</colors>

In plain english, I would like to copy the colors element recursively, including text, ignore the rest of the XML document and discard the green element.

There are solutions working with the example above but will fail if the XML varies even slightly. For instance by adding the nest eleme开发者_JAVA百科nt under the color element, or the other element which is not in the hierarchy of the color element, or text that is in the scope of the color element ( GOOD TEXT ) and another that is outside its scope ( BAD TEXT ).

<?xml version="1.0" ?>
<mydoc>
    <file>
        <colors>
            <nest>
              <blue />
              <red />
              <green />
            </nest>
            GOOD TEXT
        </colors>
        <secret>
            <username />
            <password />
        </secret>
        BAD TEXT
    </file>
    <other>BAD TEXT TWO</other>
</mydoc>

I am most interested in a solution that is generic and not customized to the exemples presented here.


This new transform is general enough to copy colors elements whatever their parent is.

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

    <xsl:template match="/mydoc/file/colors">
        <xsl:variable name="colors_parent"
            select="local-name(.//green/parent::*)"/>
        <xsl:copy>
            <xsl:copy-of 
                select=".//*[local-name()=$colors_parent]/*[not(self::green)]"/>
        </xsl:copy>
    </xsl:template>

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

</xsl:stylesheet>

I've also excluded all possible text elements. It's not still clear if you want to keep the "GOOD TEXT" text node. However it should be very easy for you now to adapt the transform to new requirements. For example if you want to keep any text node under colors element you can change use this transform:

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

    <xsl:template match="/mydoc/file/colors">
        <xsl:variable name="colors_parent"
            select="local-name(.//green/parent::*)"/>
        <xsl:copy>
            <xsl:copy-of 
                select=".//*[local-name()=$colors_parent]/*[not(self::green)]
                      | .//text()"/>
        </xsl:copy>
    </xsl:template>

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

</xsl:stylesheet>

Usage of identity rule (it copies the nest element)

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

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

    <xsl:template match="mydoc">
        <xsl:apply-templates select="file/colors"/>
    </xsl:template>

    <xsl:template match="green"/>

</xsl:stylesheet>


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

    <xsl:template match="/">
        <colors>
            <xsl:apply-templates select="//colors/*[not(self::green)]"/>
        </colors>
    </xsl:template>

    <xsl:template match="*">
        <xsl:copy-of select="."/>
    </xsl:template>

</xsl:stylesheet>

Output:

<colors>
    <blue />
    <red />
</colors>
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号