简体   繁体   中英

XSL: how to split elements having a given sub-element

I have some XML like this

<a>
  <b attr = 'foo'>
    <d>text</d>
    <x attr1 = 'foo1'>x-text-1</x>
    <x>x-text-2</x>
    <e attr2 = 'foo2' />
  </b>
</a>

I need to split the entire tree rooted at <a> into two (or more) trees, having the same structure, but only one <x> per duplicate:

<a>
  <b attr = 'foo'>
    <d>text</d>
    <x attr1 = 'foo1'>x-text-1</x>
    <e attr2 = 'foo2' />
  </b>
</a>

<a>
  <b attr = 'foo'>
    <d>text</d>
    <x>x-text-2</x>
    <e attr2 = 'foo2' />
  </b>
</a>

The two new <a> nodes will be contained in another parent node. Apart from the presence of a/b/x paths, I don't know anything else about the structure (ie, <a> or any other node, including <x> , might contain nodes I don't know of and all nodes might have attributes I don't know of).

I've tried to figure out how to do that in XSL, but I don't really have a concrete idea. I can only think of a for-each over all a/b/x and then a copy-of that starts from a/ and excludes the x not equal to the one being considered by the current for-each iteration. But it's just an headache for me to try to code the idea in XSL, any help appreciated.

I need to split the entire tree rooted at <a> into two (or more) trees, having the same structure, but only one <x> per duplicate

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:template match="/">
    <root>
        <xsl:for-each select="//x">
            <xsl:apply-templates select="/*">
                <xsl:with-param name="x-id" select="generate-id()"/>
            </xsl:apply-templates>
        </xsl:for-each>
    </root>
</xsl:template>   

<!-- modified identity tranform -->
<xsl:template match="@*|node()">
    <xsl:param name="x-id" />
    <xsl:copy>
        <xsl:apply-templates select="@*|node()">
            <xsl:with-param name="x-id" select="$x-id"/>
        </xsl:apply-templates>
    </xsl:copy>
</xsl:template>

<xsl:template match="x">
    <xsl:param name="x-id" />
    <xsl:if test="generate-id() = $x-id">
        <xsl:copy-of select="."/>
    </xsl:if>
</xsl:template>

</xsl:stylesheet>

Applied to your input example, the result will be:

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <a>
      <b attr="foo">
         <d>text</d>
         <x attr1="foo1">x-text-1</x>
         <e attr2="foo2"/>
      </b>
   </a>
   <a>
      <b attr="foo">
         <d>text</d>
         <x>x-text-2</x>
         <e attr2="foo2"/>
      </b>
   </a>
</root>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM