问题
Merge XML nodes based on ReferenceNumber Value
How to merge the xml request to the output given below using XSLT based on the Key value. Basically the xml response has to be merged for ReferenceNumber, along with the respective ProductCode and SecondaryDivision. Below is the Request and the Expected response.
I tried Using following XSLT but it did not gave desired result.
Below is the XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="refNo" match="Envelope/Body/DocumentMetadataResponse" use="ReferenceNumber" />
<xsl:template match="@*|node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="Envelope/Body/DocumentMetadataResponse/DocumentMetadata" />
<xsl:template match="Envelope/Body/DocumentMetadataResponse/DocumentMetadata[generate-id()=generate-id(key('refNo', DocumentMetadata/ReferenceNumber))]">
<xsl:call-template name="identity"/>
</xsl:template>
<xsl:template match="DocumentMetadata">
<xsl:copy>
<xsl:apply-templates select="@*|ReferenceNumber" />
<divisionses>
<xsl:apply-templates select="key('refNo', ReferenceNumber)/DocumentMetadata" mode="divisions" />
</divisionses>
</xsl:copy>
</xsl:template>
<xsl:template match="DocumentMetadata" mode="divisions">
<Divisions>
<xsl:apply-templates select="*[not(self::ReferenceNumber)]" />
</Divisions>
</xsl:template>
</xsl:stylesheet>
Input XML
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns2:DocumentMetadataResponse xmlns:ns2="http://www.example.com/mService">
<PageNumber>1</PageNumber>
<PageSize>1000</PageSize>
<TotalDocumentsAvailable>4919</TotalDocumentsAvailable>
<DocumentMetadata>
<ReferenceNumber>Ref-01</ReferenceNumber>
<SecondaryDivision>AT</SecondaryDivision>
<ProductCode>Product-AT-01</ProductCode>
</DocumentMetadata>
<DocumentMetadata>
<ReferenceNumber>Ref-01</ReferenceNumber>
<SecondaryDivision>AT</SecondaryDivision>
<ProductCode>Product-AT-02</ProductCode>
</DocumentMetadata>
<DocumentMetadata>
<ReferenceNumber>Ref-01</ReferenceNumber>
<SecondaryDivision>BE</SecondaryDivision>
<ProductCode>Product-BE-01</ProductCode>
</DocumentMetadata>
<DocumentMetadata>
<ReferenceNumber>Ref-02</ReferenceNumber>
<SecondaryDivision>BS</SecondaryDivision>
<ProductCode>Product-BS-01</ProductCode>
</DocumentMetadata>
</ns2:DocumentMetadataResponse>
</soap:Body>
</soap:Envelope>
Expected Output XML
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns2:DocumentMetadataResponse xmlns:ns2="http://www.example.com/mService">
<PageNumber>1</PageNumber>
<PageSize>1000</PageSize>
<TotalDocumentsAvailable>4919</TotalDocumentsAvailable>
<DocumentMetadata>
<ReferenceNumber>Ref-01</ReferenceNumber>
<Divisions>
<Division>
<SecondaryDivision>AT</SecondaryDivision>
<Products>
<Product>
<ProductCode>Product-AT-01</ProductCode>
</Product>
<Product>
<ProductCode>Product-AT-02</ProductCode>
</Product>
</Products>
</Division>
<Division>
<SecondaryDivision>BE</SecondaryDivision>
<Products>
<Product>
<ProductCode>Product-BE-01</ProductCode>
</Product>
</Products>
</Division>
</Divisions>
</DocumentMetadata>
<DocumentMetadata>
<ReferenceNumber>Ref-02</ReferenceNumber>
<Divisions>
<Division>
<SecondaryDivision>BS</SecondaryDivision>
<Products>
<Product>
<ProductCode>Product-BS-01</ProductCode>
</Product>
</Products>
</Division>
</Divisions>
</DocumentMetadata>
</ns2:DocumentMetadataResponse>
</soap:Body>
</soap:Envelope>
回答1:
You can use this XSLT-2.0 template to do the job. xsl:for-each-group
performs the two levels of grouping:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns2="http://www.example.com/mService">
<xsl:output method="xml" indent="yes" />
<xsl:template match="@*|node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="ns2:DocumentMetadataResponse ">
<xsl:copy>
<xsl:apply-templates select="@*|* except DocumentMetadata" />
<xsl:for-each-group select="DocumentMetadata" group-by="ReferenceNumber">
<DocumentMetadata>
<ReferenceNumber><xsl:value-of select="current-grouping-key()" /></ReferenceNumber>
<Divisions>
<xsl:for-each-group select="current-group()" group-by="SecondaryDivision">
<Division>
<SecondaryDivision><xsl:value-of select="current-grouping-key()" /></SecondaryDivision>
<Products>
<xsl:for-each select="current-group()">
<Product>
<ProductCode><xsl:value-of select="ProductCode" /></ProductCode>
</Product>
</xsl:for-each>
</Products>
</Division>
</xsl:for-each-group>
</Divisions>
</DocumentMetadata>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Output is:
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns2:DocumentMetadataResponse xmlns:ns2="http://www.example.com/mService">
<PageNumber>1</PageNumber>
<PageSize>1000</PageSize>
<TotalDocumentsAvailable>4919</TotalDocumentsAvailable>
<DocumentMetadata>
<ReferenceNumber>Ref-01</ReferenceNumber>
<Divisions>
<Division>
<SecondaryDivision>AT</SecondaryDivision>
<Products>
<Product>
<ProductCode>Product-AT-01</ProductCode>
</Product>
<Product>
<ProductCode>Product-AT-02</ProductCode>
</Product>
</Products>
</Division>
<Division>
<SecondaryDivision>BE</SecondaryDivision>
<Products>
<Product>
<ProductCode>Product-BE-01</ProductCode>
</Product>
</Products>
</Division>
</Divisions>
</DocumentMetadata>
<DocumentMetadata>
<ReferenceNumber>Ref-02</ReferenceNumber>
<Divisions>
<Division>
<SecondaryDivision>BS</SecondaryDivision>
<Products>
<Product>
<ProductCode>Product-BS-01</ProductCode>
</Product>
</Products>
</Division>
</Divisions>
</DocumentMetadata>
</ns2:DocumentMetadataResponse>
</soap:Body>
</soap:Envelope>
来源:https://stackoverflow.com/questions/58140104/merge-xml-nodes-based-on-certain-key-value-using-xslt