Grouping based on the node value if only it exists

2019-09-15 10:21发布

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条回答
ゆ 、 Hurt°
2楼-- · 2019-09-15 11:07

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>
查看更多
登录 后发表回答