XSL - Duplicate child's parent node

2019-08-21 10:30发布

After searching for the node with the same name in other structure and replace his value, how can I duplicate the parent node of the matched one?

Can I set a key for each child's parent in the template?

From this,

<Message>
<XMLNSC>
    <MaXML>
        <Rule>
            <A>RuleA</A>
            <D>RuleD</D>
        </Rule>
        <Body>
            <A>valA</A>
            <B>valB</B>
            <C>
                <D>valD</D>
            </C>
        </Body>
    </MaXML>
</XMLNSC>

To this,

<Message>
<XMLNSC>
    <MaXML>
        <Rule>
            <A>RuleA</A>
            <D>RuleD</D>
        </Rule>
        <Body>
            <A>RuleA</A>
            <A>RuleA</A>
            <B>valB</B>
            <C>
                <D>RuleD</D>
                <D>RuleD</D>
            </C>
            <C>
                <D>RuleD</D>
                <D>RuleD</D>
            </C>
        </Body>
        <Body>
            <A>RuleA</A>
            <A>RuleA</A>
            <B>valB</B>
            <C>
                <D>RuleD</D>
                <D>RuleD</D>
            </C>
            <C>
                <D>RuleD</D>
                <D>RuleD</D>
            </C>
        </Body>
    </MaXML>
</XMLNSC>

My XSLT, https://xsltfiddle.liberty-development.net/pPqsHTK/5

标签: xml xslt
1条回答
Ridiculous、
2楼-- · 2019-08-21 11:07

If you write a template for those descendants of MaXML which have a child element with a key match you can simply use the identity template twice on those descendant elements, I had the exclude the Rule elements explicitly then:

<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">

  <xsl:strip-space elements="*"/>
  <xsl:output indent="yes"/>

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

  <xsl:key name="ref" match="MaXML/Rule/*" use="concat(generate-id(ancestor::MaXML), '|', name())"/>

  <xsl:template match="MaXML//*[not(self::Rule)][*[key('ref', concat(generate-id(ancestor::MaXML), '|', name()))]]">
      <xsl:call-template name="identity"/>
      <xsl:call-template name="identity"/>
  </xsl:template>

  <xsl:template match="Body//*[key('ref', concat(generate-id(ancestor::MaXML), '|', name()))]">
      <xsl:copy>
          <xsl:value-of select="key('ref', concat(generate-id(ancestor::MaXML), '|', name()))"/>
      </xsl:copy>

      <!-- duplicate matched node -->
        <xsl:element name="{name()}">
            <xsl:value-of select="key('ref', concat(generate-id(ancestor::MaXML), '|', name()))"/>
        </xsl:element>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/pPqsHTK/6

With XSLT 3 it a bit easier:

  <xsl:template match="MaXML//*[not(self::Rule)][*[key('ref', node-name(), ancestor::MaXML)]]">
      <xsl:next-match/>
      <xsl:next-match/>
  </xsl:template>

https://xsltfiddle.liberty-development.net/pPqsHTK/8

查看更多
登录 后发表回答