XSLT group if condition is matched

2020-05-03 10:54发布

问题:

INPUT:

  <?xml version="1.0" encoding="UTF-8"?>
<output>
<input>
    <!--details-->
</input>
<meta2>
    <tag>
        <output_getquerydata>
            <queries>
                <query name="part1">
                    <parameters>
                        <!--details-->
                    </parameters>
                    <queryErrors>
                        <!--details-->
                    </queryErrors>
                    <queryResults>
                        <record id="1">                              
                            <column name="VRIdTask">1</column>                               
                            <column name="MSTP">22</column>
                            <column name="VRPlanId">11310224</column>                                
                            <column name="MSONPC">221</column> 
                        </record>
                        <record id="2">
                            <column name="VRIdTask">1</column>
                            <column name="MSTP">22</column>
                            <column name="VRPlanId">11310224</column> 
                            <column name="MSONPC">2211</column> 
                        </record>
                        <record id="3"> 
                            <column name="VRIdTask">3</column> 
                            <column name="MSTP"/>
                            <column name="VRPlanId">11310337</column> 
                            <column name="MSONPC"/> 
                        </record>
                        <record id="4"> 
                            <column name="VRIdTask">2</column> 
                            <column name="MSTP"/>
                            <column name="VRPlanId">11310281</column> 
                            <column name="MSONPC">2221</column> 
                        </record>
                        <record id="5"> 
                            <column name="VRIdTask">4</column> 
                            <column name="MSTP">33</column>
                            <column name="VRPlanId">11310281</column> 
                            <column name="MSONPC">222221</column> 
                        </record>
                        <record id="6"> 
                            <column name="VRIdTask">4</column> 
                            <column name="MSTP">331</column>
                            <column name="VRPlanId">11310281</column> 
                            <column name="MSONPC">222221</column> 
                        </record>
                    </queryResults>
                </query>
            </queries>
        </output_getquerydata>
    </tag>
</meta2>
</output>

XSL:

      <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="k" match="output/meta2/tag/output_getquerydata/queries/query/queryResults/record" use="./column[@name='VRPlanId']"/>
<xsl:template match="@*|node()">
    <xsl:variable name="distinctOrder" select="//record[string(column[@name='VRPlanId'])][count(. | key('k', column[@name='VRPlanId'])[1]) >1]"/>
    <xsl:if test="count(key('k', .//column[@name='VRPlanId'])) >0 or //queryError">
        <Cdo>
            <parameters>
                <xsl:for-each select="$distinctOrder">
                    <task id="{concat(column[@name='MSTP'],',',column[@name='MSONPC'])}"/>
                    <action id="{column[@name='VRPlanId']}"/>
                </xsl:for-each>
                <xsl:for-each select="key('k', column[@name='VRPlanId'])">
                    <action id="{column[@name='VRPlanId']}"/>
                </xsl:for-each>
            </parameters>
        </Cdo>
    </xsl:if>
</xsl:template>
<xsl:template match="input"/>
</xsl:stylesheet>

Hello everyone,

Thanks for your help. The goal is to check for every distinct VRPlanID value that we build the tag Group the corresponding MSTP, MSONPC (distinct) if they're not null/empty in the id part of the tag. So, we group after VRPlanID, then get the distinct values from each record from MSTP/MSONPC. If they're empty then just build a dummy / null message/tag. (not done)

So, the desired output would be:

  <output>
   <Cdo>
     <parameters>
       <task id="22,221,2211"/>                
       <action id="1131024"/>
     </parameters>
 </Cdo>
<Cdo>
    <parameters>
       <task id="2221/>                
       <action id="999"/>
    </parameters>
 </Cdo>
  <Cdo>
    <parameters>
       <task id="33,331,222221"/>                
       <action id="11310281"/>
    </parameters>
 </Cdo>
  </output>

Later edit:

with this:

        <!--parameter to filter per VRPlanId-->
    <xsl:param name="param.VRPlanId"/>
    <xsl:variable name="var.mstp">
        <xsl:for-each select="/output/meta2/tag/output_getquerydata/queries/query/queryResults/record[column[@name='VRPlanId'] = $param.VRPlanId]">
            <!--preventing empty blocks and duplicates for MSTP-->
            <xsl:if test="string-length(column[@name='MSTP']) &gt;0 
            and not(preceding::record[column[@name='MSTP']/text() = current()/column[@name='MSTP']/text()])">
                <xsl:value-of select="column[@name='MSTP']"/>
            </xsl:if>
        </xsl:for-each>
    </xsl:variable>
    <!--concat all non-duplicates MSTP and MSONPC without last delimiter-->
    <xsl:value-of select="$var.mstp"/>
</xsl:template>

I get the concatenated results, but not in separate tags

    <parameters>
            <task id="131957513196231319667"/>
            <action id="11310281"/>
        </parameters>

回答1:

I think it is better to prevent duplicates in MSTP and MSONPC and put them into one template where input parameter will be non-duplicated VRPlanId as in XSL below:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
    <xsl:output method="xml" /> 
    <xsl:template name="task-attr">
        <!--parameter to filter per VRPlanId-->     
        <xsl:param name="param.VRPlanId"/>              
        <xsl:variable name="var.mstp">                      
            <xsl:for-each select="/output/meta2/tag/output_getquerydata/queries/query/queryResults/record[column[@name='VRPlanId'] = $param.VRPlanId]">
                <!--preventing empty blocks and duplicates for MSTP-->          
                <xsl:if test="string-length(column[@name='MSTP']) &gt;0 and not(preceding::record[column[@name='MSTP']/text() = current()/column[@name='MSTP']/text()])">                   
                    <xsl:value-of select="concat(column[@name='MSTP'], ',')"/>              
                </xsl:if>                                       
            </xsl:for-each>                             
        </xsl:variable>     
        <xsl:variable name="var.msonpc">                        
            <xsl:for-each select="/output/meta2/tag/output_getquerydata/queries/query/queryResults/record[column[@name='VRPlanId'] = $param.VRPlanId]">
                <!--preventing empty blocks and duplicates for MSONPC-->            
                <xsl:if test="string-length(column[@name='MSONPC']) &gt;0 and not(preceding::record[column[@name='MSONPC']/text() = current()/column[@name='MSONPC']/text()])">                 
                    <xsl:value-of select="concat(column[@name='MSONPC'], ',')"/>                
                </xsl:if>                                       
            </xsl:for-each>                             
        </xsl:variable>
        <!--concat all non-duplicates MSTP and MSONPC without last delimiter--> 
        <xsl:value-of select="concat($var.mstp, substring($var.msonpc, 1, string-length($var.msonpc)-1))"/>     
    </xsl:template>

    <xsl:template match="/">        
        <output>            
            <xsl:for-each select="/output/meta2/tag/output_getquerydata/queries/query/queryResults/record">             
                <xsl:if test ="not(preceding::record[column[@name='VRPlanId']/text() = current()/column[@name='VRPlanId']/text()])">                    
                    <xsl:variable name="task.id">                                                   
                        <xsl:call-template name="task-attr">
                            <!--as input parameter put non-duplicate VRPlanId-->                                                            
                            <xsl:with-param name="param.VRPlanId" select="column[@name='VRPlanId']"/>                                                   
                        </xsl:call-template>                                    
                    </xsl:variable>
                    <!--if all non-duplicates MSTP and MSONPC will be blank block Cdo won't be created-->                   
                    <xsl:if test="string-length($task.id) &gt;0">                       
                        <Cdo>                           
                            <parameters>                                
                                <task>                                                          
                                    <xsl:attribute name="id">                                                       
                                        <xsl:value-of select="$task.id"/>               
                                    </xsl:attribute>                                
                                </task>                             
                                <action>                                                            
                                    <xsl:attribute name="id">                                                                           
                                        <xsl:value-of select="column[@name='VRPlanId']"/>                                                               
                                    </xsl:attribute>                                
                                </action>                                           
                            </parameters>                       
                        </Cdo>                                      
                    </xsl:if>               
                </xsl:if>           
            </xsl:for-each>                     
        </output>   
    </xsl:template>
</xsl:stylesheet>

You have edited your version of XML but in case of XML below:

<?xml version="1.0" encoding="UTF-8"?>
<output>
<input>
    <!--details-->
</input>
<meta2>
    <tag>
        <output_getquerydata>
            <queries>
                <query name="part1">
                    <parameters>
                        <!--details-->
                    </parameters>
                    <queryErrors>
                        <!--details-->
                    </queryErrors>
                    <queryResults>
                        <record id="1">                              
                            <column name="VRIdTask">1</column>                               
                            <column name="MSTP">22</column>
                            <column name="VRPlanId">11310224</column>                                
                            <column name="MSONPC">221</column> 
                        </record>
                        <record id="2">
                            <column name="VRIdTask">1</column>
                            <column name="MSTP">22</column>
                            <column name="VRPlanId">11310224</column> 
                            <column name="MSONPC">2211</column> 
                        </record>
                        <record id="3"> 
                            <column name="VRIdTask">3</column> 
                            <column name="MSTP"/>
                            <column name="VRPlanId">11310337</column> 
                            <column name="MSONPC"/> 
                        </record>
                        <record id="4"> 
                            <column name="VRIdTask">2</column> 
                            <column name="MSTP"/>
                            <column name="VRPlanId">11310281</column> 
                            <column name="MSONPC">2221</column> 
                        </record>
                        <record id="5"> 
                            <column name="VRIdTask">4</column> 
                            <column name="MSTP">33</column>
                            <column name="VRPlanId">11310281</column> 
                            <column name="MSONPC">222221</column> 
                        </record>
                        <record id="6"> 
                            <column name="VRIdTask">4</column> 
                            <column name="MSTP">331</column>
                            <column name="VRPlanId">11310281</column> 
                            <column name="MSONPC">222221</column> 
                        </record>
                    </queryResults>
                </query>
            </queries>
        </output_getquerydata>
    </tag>
</meta2>
</output>

The result is:

<?xml version="1.0" encoding="UTF-8"?>
<output>
    <Cdo>
        <parameters>
            <task id="22,221,2211"/>
            <action id="11310224"/>
        </parameters>
    </Cdo>
    <Cdo>
        <parameters>
            <task id="33,331,2221,222221"/>
            <action id="11310281"/>
        </parameters>
    </Cdo>
</output>

NOTE! In proposed XSL there is validation for empty values, if you will require include them just remove that validations.

In case when it is required to use each attribute value in separate task tag then you can create task blocks structure in separate template and call it in parameters block as below:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
    <xsl:output method="xml" /> 
    <xsl:template name="task-attr">
        <!--parameter to filter per VRPlanId-->     
        <xsl:param name="param.VRPlanId"/>
        <tasks>
            <xsl:for-each select="/output/meta2/tag/output_getquerydata/queries/query/queryResults/record[column[@name='VRPlanId'] = $param.VRPlanId]">
                <!--preventing empty blocks and duplicates for MSTP-->          
                <xsl:if test="string-length(column[@name='MSTP']) &gt;0 and not(preceding::record[column[@name='MSTP']/text() = current()/column[@name='MSTP']/text()])">
                    <task>
                        <xsl:attribute name="id">
                            <xsl:value-of select="column[@name='MSTP']"/>
                        </xsl:attribute>
                    </task>                                                   
                </xsl:if>                                       
            </xsl:for-each>
            <xsl:for-each select="/output/meta2/tag/output_getquerydata/queries/query/queryResults/record[column[@name='VRPlanId'] = $param.VRPlanId]">
                <!--preventing empty blocks and duplicates for MSONPC-->            
                <xsl:if test="string-length(column[@name='MSONPC']) &gt;0 and not(preceding::record[column[@name='MSONPC']/text() = current()/column[@name='MSONPC']/text()])">
                    <task>
                        <xsl:attribute name="id">                                                     
                            <xsl:value-of select="column[@name='MSONPC']"/>
                        </xsl:attribute>
                    </task>                
                </xsl:if>                                       
            </xsl:for-each>            
        </tasks>                                                   
    </xsl:template>

    <xsl:template match="/">        
        <output>            
            <xsl:for-each select="/output/meta2/tag/output_getquerydata/queries/query/queryResults/record">             
                <xsl:if test ="not(preceding::record[column[@name='VRPlanId']/text() = current()/column[@name='VRPlanId']/text()])"> 
                        <Cdo>                           
                            <parameters>                                                        
                                <xsl:call-template name="task-attr">                                                                                                                   
                                    <xsl:with-param name="param.VRPlanId" select="column[@name='VRPlanId']"/>                                                                           
                                </xsl:call-template>                                                             
                                <action>                                                            
                                    <xsl:attribute name="id">                                                                           
                                        <xsl:value-of select="column[@name='VRPlanId']"/>                                                               
                                    </xsl:attribute>                                
                                </action>                                           
                            </parameters>                       
                        </Cdo>                                                                                                                  
                </xsl:if>           
            </xsl:for-each>                     
        </output>   
    </xsl:template>
</xsl:stylesheet>

Then when you will check it with XML above the result will be as below:

<?xml version="1.0" encoding="UTF-8"?>
<output>
    <Cdo>
        <parameters>
            <tasks>
                <task id="22"/>
                <task id="221"/>
                <task id="2211"/>
            </tasks>
            <action id="11310224"/>
        </parameters>
    </Cdo>
    <Cdo>
        <parameters>
            <tasks/>
            <action id="11310337"/>
        </parameters>
    </Cdo>
    <Cdo>
        <parameters>
            <tasks>
                <task id="33"/>
                <task id="331"/>
                <task id="2221"/>
                <task id="222221"/>
            </tasks>
            <action id="11310281"/>
        </parameters>
    </Cdo>
</output>

Of course you can add blank values validation where it is required.

Hope it will help with your case.