简体   繁体   中英

XML restructure using XSLT 2.0

I am trying to restructure an xml. So needing to split the Product up. I have done a couple of xslt with help from yourselves however these were for splitting the XML into multiple XML. This however seems slightly more challenging. I am struggling with where to begin. Any help would be great. I am using XSLT 2.0.

Cheers

Input XML

<?xml version="1.0"?>
<Order_Root>
  <Orders Number="12345">
    <Info Name="John Doe" Reference="1/2/2019">
      <LineItems>
        <LineItem LineItemNumber="01" CustomerProductName="Test">
          <LineItemPrice Price="0" Charge="0" />
          <Products>
            <Product ProductName="A" />
            <Product ProductName="B" />
            <Product ProductName="C" />
          </Products>
        </LineItem>
      </LineItems>
    </Info>
  </Orders>
</Order_Root>

Result XML

<?xml version="1.0"?>
<Order_Root>
  <Orders Number="12345">
    <Info Name="John Doe" Reference="1/2/2019">
      <LineItems>
        <LineItem LineItemNumber="01" CustomerProductName="Test">
          <LineItemPrice Price="0" Charge="0" />
          <Products>
            <Product ProductName="A" />
          </Products>
        </LineItem>
        <LineItem LineItemNumber="01" CustomerProductName="Test">
          <LineItemPrice Price="0" Charge="0" />
          <Products>
            <Product ProductName="B" />
          </Products>
        </LineItem>
        <LineItem LineItemNumber="01" CustomerProductName="Test">
          <LineItemPrice Price="0" Charge="0" />
          <Products>
            <Product ProductName="C" />
          </Products>
        </LineItem>
      </LineItems>
    </Info>
  </Orders>
</Order_Root>

I think it can be used as a good example of using XSLT 3's snapshot function together with a second mode and a tunnel parameter to pass on that LineItemPrice :

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="3.0">

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

    <xsl:mode on-no-match="shallow-copy"/>

    <xsl:mode name="price" on-no-match="shallow-copy"/>

    <xsl:template match="LineItem">
        <xsl:apply-templates select=".//Product!snapshot()/ancestor::LineItem" mode="price">
            <xsl:with-param name="price" tunnel="yes" select="LineItemPrice"/>
        </xsl:apply-templates>
    </xsl:template>

    <xsl:template match="LineItem" mode="price">
        <xsl:param name="price" tunnel="yes"/>
        <xsl:copy>
            <xsl:apply-templates select="@*, $price, node()" mode="#current"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/bFN1y8J

XSLT 3 is available with Saxon 9.8 or later or Altova 2017 and later, so chances are if you use an up-to-date XSLT 2 processor you have a processor that can also use XSLT 3 instead of the tagged XSLT 2.

XSLT 2 doesn't have snapshot but of course you can try to implement the same approach of pushing each product through a different mode to reconstruct a subtree there as well, only the other templates would now need to exclude all but one Product :

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="2.0">

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

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

    <xsl:template match="LineItem">
        <xsl:apply-templates select=".//Product"/>
    </xsl:template>

    <xsl:template match="Product">
        <xsl:apply-templates select="ancestor::LineItem" mode="price">
            <xsl:with-param name="product" tunnel="yes" select="."/>
        </xsl:apply-templates>
    </xsl:template>

    <xsl:template match="Product" mode="price">
        <xsl:param name="product" tunnel="yes"/>
        <xsl:if test=". is $product">
            <xsl:next-match/>
        </xsl:if>
    </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/bFN1y8J/1

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