问题
My requirement is slightly complex one.I have to use only XSLT 1.0. I could able to get the solution using XSLT 2.0, but I need the solution using 1.0.
I have the following input xml:
<results>
<row>
<CASEID>C1</CASEID>
<CASEBA>MEDICAID</CASEBA>
<ISSUEID>I1</ISSUEID>
<ISSUEBA>MEDICAID</ISSUEBA>
<OBJECTID>1</OBJECTID>
<OBJECTBA>MEDICAID</OBJECTBA>
</row>
<row>
<CASEID>C1</CASEID>
<CASEBA>MEDICAID</CASEBA>
<ISSUEID>I2</ISSUEID>
<ISSUEBA>MEDICAID</ISSUEBA>
<OBJECTID>2</OBJECTID>
<OBJECTBA>MEDICAID</OBJECTBA>
</row>
<row>
<CASEID>C1</CASEID>
<CASEBA>MEDICAID</CASEBA>
<ISSUEID>I1</ISSUEID>
<ISSUEBA>MEDICAID</ISSUEBA>
<OBJECTID>extra</OBJECTID>
<OBJECTBA>MEDICAID</OBJECTBA>
</row>
<row>
<CASEID>C2</CASEID>
<CASEBA>MEDICAID</CASEBA>
<ISSUEID>I3</ISSUEID>
<ISSUEBA>MEDICAID</ISSUEBA>
<OBJECTID>3</OBJECTID>
<OBJECTBA>MEDICAID</OBJECTBA>
</row>
</results>
I have to transform the above xml to simpler one using XSLT by following the below conditions:
1)First target is to move all the <CASEID>
's whose values are same under the new <CASE>
tag along with <CASEBA>
node.
For Example:
<CASE>
<CASEID>C1</CASEID>
<CASEBA>MEDICAID</CASEBA>
</CASE>
2)List out all the <ISSUEID>
&<ISSUEBA>
whose <CASEID>
's are equal in different <row>
's and move them under the new tag<ISSUE>
within the recently created <CASE>
tag.
For example:
<CASE>
<CASEID>C1</CASEID>
<CASEBA>MEDICAID</CASEBA>
<ISSUE>
<ISSUEID>I1</ISSUEID>
<ISSUEBA>MEDICAID</ISSUEBA>
</ISSUE>
<ISSUE>
<ISSUEID>I2</ISSUEID>
<ISSUEBA>MEDICAID</ISSUEBA>
</ISSUE>
</CASE>
3)List out all the <OBJECTID>
's whose <ISSUEID>
's are equal in different <row>
's and move them under the new <SOURCE>
tag within <ISSUE>
tag which will be definitely under the <CASE>
.
For example:
<CASE>
<CASEID>C1</CASEID>
<CASEBA>MEDICAID</CASEBA>
<ISSUE>
<ISSUEID>I1</ISSUEID>
<ISSUEBA>MEDICAID</ISSUEBA>
<SOURCE>
<OBJECTID>1</OBJECTID>
<OBJECTBA>MEDICAID</OBJECTBA>
</SOURCE>
<SOURCE>
<OBJECTID>extra</OBJECTID>
<OBJECTBA>MEDICAID</OBJECTBA>
</SOURCE>
</ISSUE>
<ISSUE>
<ISSUEID>I2</ISSUEID>
<ISSUEBA>MEDICAID</ISSUEBA>
<SOURCE>
<OBJECTID>2</OBJECTID>
<OBJECTBA>MEDICAID</OBJECTBA>
</SOURCE>
</ISSUE>
</CASE>
The final output xml should be as below:
<?xml version="1.0" encoding="UTF-8"?>
<results>
<CASE>
<CASEID>C1</CASEID>
<CASEBA>MEDICAID</CASEBA>
<ISSUE>
<ISSUEID>I1</ISSUEID>
<ISSUEBA>MEDICAID</ISSUEBA>
<SOURCE>
<OBJECTID>1</OBJECTID>
<OBJECTBA>MEDICAID</OBJECTBA>
</SOURCE>
<SOURCE>
<OBJECTID>extra</OBJECTID>
<OBJECTBA>MEDICAID</OBJECTBA>
</SOURCE>
</ISSUE>
<ISSUE>
<ISSUEID>I2</ISSUEID>
<ISSUEBA>MEDICAID</ISSUEBA>
<SOURCE>
<OBJECTID>2</OBJECTID>
<OBJECTBA>MEDICAID</OBJECTBA>
</SOURCE>
</ISSUE>
</CASE>
<CASE>
<CASEID>C2</CASEID>
<CASEBA>MEDICAID</CASEBA>
<ISSUE>
<ISSUEID>I3</ISSUEID>
<ISSUEBA>MEDICAID</ISSUEBA>
<SOURCE>
<OBJECTID>3</OBJECTID>
<OBJECTBA>MEDICAID</OBJECTBA>
</SOURCE>
</ISSUE>
</CASE>
</results>
Please forgive me if I didn't explain my requirement properly. Please do ask me if you need any additional information. It would be really great if someone helps me out.
回答1:
In your previous question (Transforming xml based on node values) the answer mentioned about using a technique called Muenchian Grouping in XSLT 1.0. In your case, you have nested grouping. You first group by CASEID
, then group by CASEID
and ISSUEID
, and then finally by CASEID
, ISSUEID
and OBJECTID
This means defining three keys
<xsl:key name="case" match="row" use="CASEID" />
<xsl:key name="issue" match="row" use="concat(CASEID, '|', ISSUEID)" />
<xsl:key name="object" match="row" use="concat(CASEID, '|', ISSUEID, '|', OBJECTID)" />
To group by CASEID
you would select the first element of each group like so
<xsl:apply-templates select="row[generate-id() = generate-id(key('case', CASEID)[1])]" mode="case" />
(I am using mode
here because the final XSLT will have multiple templates matching row
so you need to distinguish between them).
In the matching template, to then group by ISSUEID
within the distinct CASEID
values, you would do this.
<xsl:apply-templates select="key('case', CASEID)[generate-id() = generate-id(key('issue', concat(CASEID, '|', ISSUEID))[1])]" mode="issue" />
You would then do similar to group by OBJECTID
in the other template.
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:key name="case" match="row" use="CASEID" />
<xsl:key name="issue" match="row" use="concat(CASEID, '|', ISSUEID)" />
<xsl:key name="object" match="row" use="concat(CASEID, '|', ISSUEID, '|', OBJECTID)" />
<xsl:template match="results">
<xsl:copy>
<xsl:apply-templates select="row[generate-id() = generate-id(key('case', CASEID)[1])]" mode="case" />
</xsl:copy>
</xsl:template>
<xsl:template match="row" mode="case">
<CASE>
<xsl:apply-templates select="CASEID|CASEBA" />
<xsl:apply-templates select="key('case', CASEID)[generate-id() = generate-id(key('issue', concat(CASEID, '|', ISSUEID))[1])]" mode="issue" />
</CASE>
</xsl:template>
<xsl:template match="row" mode="issue">
<ISSUE>
<xsl:apply-templates select="ISSUEID|ISSUEBA" />
<xsl:apply-templates select="key('issue', concat(CASEID, '|', ISSUEID))[generate-id() = generate-id(key('object', concat(CASEID, '|', ISSUEID, '|', OBJECTID))[1])]" mode="object" />
</ISSUE>
</xsl:template>
<xsl:template match="row" mode="object">
<OBJECT>
<xsl:apply-templates select="OBJECTID|OBJECTBA" />
</OBJECT>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
来源:https://stackoverflow.com/questions/43818999/slightly-complex-grouping-based-on-node-values-using-xslt-1-0