Combining XML using XSLT 1.0 [duplicate]

2019-09-20 11:04发布

问题:

This question already has an answer here:

  • combining xml, xpath or xquery 1 answer

I need to map the following but its difficult because it has different names:

<main>
    <order>
        <ID>123</ID>
        <Name>ABC</Name>
    </order>
    <order>
        <ID>4556</ID>
        <Name>AAA</Name>
        <ParentID>123</ParentID>
    </order>
</main>

The result should be:

<main>
    <order>
        <ID>123</ID>
        <Name>ABC</Name>
        <order>
            <ID>4556</ID>
            <Name>AAA</Name>
            <ParentID>123</ParentID>
        </order>
    </order>
</main>

回答1:

Like @michael.hor257k said, conceptually this question is the same as your previous question. I think you're just not understanding the concept. Hopefully the comments in my example will help.

If I understand the question correctly, you want to nest order elements inside of their "parent" order element based on ParentID. Since we're basing it on the ParentID, that's what we'll use for our key...

XML Input

<main>
    <order>
        <ID>123</ID>
        <Name>ABC</Name>
    </order>

    <order>
        <ID>4556</ID>
        <Name>AAA</Name>
        <ParentID>123</ParentID>
    </order>
</main>

XSLT 1.0

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

  <!--Create a key containing order elements that contain a non-empty ParentID.-->
  <xsl:key name="orderByParentId" match="order[string(ParentID)]" use="ParentID"/>

  <!--Identity transform (https://www.w3.org/TR/xslt#copying)-->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="/main">
    <xsl:copy>
      <!--To get the high level order elements, only apply-templates when order
      does not contain a non-empty ParentID child.-->
      <xsl:apply-templates 
        select="@*|order[not(string(ParentID))]"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="order">
    <xsl:copy>
      <!--Don't apply-templates to ParentID; we don't want to keep those.
      Do apply-templates to the key 'orderByParentId' when the key matches
      the current order's ID child.-->
      <xsl:apply-templates 
        select="@*|*[not(self::ParentID)]|key('orderByParentId',ID)"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

Output

<main>
   <order>
      <ID>123</ID>
      <Name>ABC</Name>
      <order>
         <ID>4556</ID>
         <Name>AAA</Name>
      </order>
   </order>
</main>


回答2:

One approach to copy all order nodes with index n>1 into the first one is the following XSLT:

<?xml version = "1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:template match="text()" />    <!-- suppresses copying of text() nodes -->

    <xsl:template match="main">        <!-- replicates the "main" node -->
      <main>
        <xsl:apply-templates />
      </main>
    </xsl:template>

    <xsl:template match="order[1]">    <!-- matches the first "order" node and copies all following ones -->
      <order>
        <xsl:copy-of select="*" />
        <xsl:copy-of select="following-sibling::order" />
      </order>   
    </xsl:template>    
</xsl:stylesheet>

The output looks like this:

<?xml version="1.0"?>
<main>
    <order>
        <ID>123</ID>
        <Name>ABC</Name>
        <order>
            <ID>4556</ID>
            <Name>AAA</Name>
            <ParentID>123</ParentID>
        </order>
    </order>
</main>