问题
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:
<?xml version="1.0" encoding="UTF-8"?>
<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>
<row>
<ISSUEID>I6</ISSUEID>
<ISSUEBA>MEDICAID</ISSUEBA>
<OBJECTID>10</OBJECTID>
<OBJECTBA>MEDICAID</OBJECTBA>
</row>
<row>
<CASEID>C11</CASEID>
<CASEBA>MEDICAID</CASEBA>
<OBJECTID>11</OBJECTID>
<OBJECTBA>MEDICAID</OBJECTBA>
</row>
</results>
I have to transform the above xml to simpler one using XSLT by following the below conditions:
If I have the
<CASEID>, then<CASE>tag should be there. If I have the<ISSUEID>, then<ISSUE>tag should be there. If I have the<OBJECT>, then<OBJECTID>tag should be there. The reason behind this is some of the row elements might have<CASEID><ISSUEID><OBJECTID>and some not. A<CASE>can directly have a<SOURCE>under it if there is no<ISSUE>.
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>
<ISSUE>
<ISSUEID>I6</ISSUEID>
<ISSUEBA>MEDICAID</ISSUEBA>
<SOURCE>
<OBJECTID>10</OBJECTID>
<OBJECTBA>MEDICAID</OBJECTBA>
</SOURCE>
</ISSUE>
<CASE>
<CASEID>C11</CASEID>
<CASEBA>MEDICAID</CASEBA>
<SOURCE>
<OBJECTID>11</OBJECTID>
<OBJECTBA>MEDICAID</OBJECTBA>
</SOURCE>
</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:
You can still solve this using Muenchian Grouping. If you are grouping by CASEID initially, but CASEID may not be present, you can define the first key like so
<xsl:key name="case" match="row" use="string(CASEID)" />
So, for the first key, row elements with no CASEID will be matched by an empty string and grouped together.
To group by INDEXID within CASEID, you can use a key like this, as this will still work with no CASEID (as concat returns a string)
<xsl:key name="issue" match="row" use="concat(CASEID, '|', ISSUEID)" />
To select the distinct CASEID records you would then do this:
<xsl:apply-templates select="row[generate-id() = generate-id(key('case', string(CASEID))[1])]" mode="case" />
However, in the template that matched the row elements for this mode, you would need a xsl:choose statement, to check whether CASEID existed or not. If they did exist, you would create <CASE> element. If not, you would apply the next level key.
<xsl:choose>
<xsl:when test="CASEID">
<CASE>
<!-- Apply next key -->
</CASE>
</xsl:when>
<xsl:otherwise>
<!-- Apply next key -->
</xsl:otherwise>
</xsl:choose>
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="string(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', string(CASEID))[1])]" mode="case" />
</xsl:copy>
</xsl:template>
<xsl:template match="row" mode="case">
<xsl:choose>
<xsl:when test="CASEID">
<CASE>
<xsl:apply-templates select="CASEID|CASEBA" />
<xsl:apply-templates select="key('case', string(CASEID))[generate-id() = generate-id(key('issue', concat(CASEID, '|', ISSUEID))[1])]" mode="issue" />
</CASE>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="key('case', concat('', CASEID))[generate-id() = generate-id(key('issue', concat(CASEID, '|', ISSUEID))[1])]" mode="issue" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="row" mode="issue">
<xsl:choose>
<xsl:when test="ISSUEID">
<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:when>
<xsl:otherwise>
<xsl:apply-templates select="key('issue', concat(CASEID, '|', ISSUEID))[generate-id() = generate-id(key('object', concat(CASEID, '|', ISSUEID, '|', OBJECTID))[1])]" mode="object" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="row" mode="object">
<xsl:if test="OBJECTID">
<SOURCE>
<xsl:apply-templates select="OBJECTID|OBJECTBA" />
</SOURCE>
</xsl:if>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
来源:https://stackoverflow.com/questions/43820145/grouping-based-on-the-node-value-if-only-it-exists-using-xslt-1-0