Search code examples
xmlxsltxslkey

XSL build node based on key


INPUT:

<?xml version="1.0" encoding="UTF-8"?>
<comtec>
<record id="1">
<column name="id_task">44422</column>
<column name="capabilityCode">FR-JUNIOR</column>
<column name="ColMiss">miss</column>
<column name="ColHave"/>
<column name="order_externalId">1867139</column>
</record>
<record id="2">
<column name="id_task">44422</column>
<column name="capabilityCode">FR-XXX</column>
<column name="ColMiss"/>
<column name="ColHave">notmiss</column>
<column name="order_externalId">1867139</column>
</record>
<record id="3">
<column name="id_task">44422</column>
<column name="capabilityCode">AVS</column>
<column name="ColMiss"/>
<column name="ColHave">notmiss</column>
<column name="order_externalId">1867139</column>
</record>
<record id="4">
<column name="id_task">60038</column>
<column name="PropertyDescr">ReqMissing</column>
<column name="capabilityCode">FR-AUTOPLAN</column>
<column name="ResCapString">FR-RDV1, FR-GGE1, FR-PARC2, FR-RDV VIP</column>
<column name="ColMiss">miss</column>
<column name="ColHave"/>
<column name="order_externalId">1867454</column>
</record>
<record id="5">
<column name="id_task">60038</column>
<column name="capabilityCode">FR-XXX</column>
<column name="ColMiss">miss</column>
<column name="ColHave"/>
<column name="order_externalId">1867454</column>
</record>
</comtec>

Desired output:

<comtec>
<task_order>
    <id>1867139</id>
    <capabilities>
        <capability>
            <capability_code>FR-JUNIOR</capability_code>
            <available>true</available>
        </capability>
        <capability>
            <capability_code>FR-XXX</capability_code>
            <required>true</required>
        </capability>
        <capability>
            <capability_code>AVS</capability_code>
            <required>true</required>
        </capability>
    </capabilities>
    <task>
        <id>1867139</id>
        <task_kind_code>drive_through</task_kind_code>
    </task>
</task_order>
<task_order>
    <id>1867454</id>
    <capabilities>
        <capability>
            <capability_code>FR-AUTOPLAN</capability_code>
            <available>true</available>
        </capability>
        <capability>
            <capability_code>FR-XXX</capability_code>
            <available>true</available>
        </capability>
    </capabilities>
    <task>
        <id>1867454</id>
        <task_kind_code>drive_through</task_kind_code>
    </task>
</task_order>
</comtec>

Hello. The desired output is based on the following logic: id_task has multiple records a)if only has values int the 'ColMiss' column then build empty capability tag (second record tag in the output) b)if it has values in both 'ColMiss' column and 'ColHave' then build the capability tag with only the values from the 'CapabilityCode' column in the records that have 'ColHave' not null.

I hope it makes sense, i'm still a novice with the keys/var parts of XSL and im working on some examples to understand.

Thank you

UPDATE:

Current XSL - without keys/variables

   <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
   <xsl:output method="xml"/>
   <xsl:key name="record" match="record" use="record/column[@name='id_task']"/>
   <xsl:template match="comtec">
    <!--grouped by id_task-->
    <xsl:for-each select="record[generate-id(.)=
  generate-id(key('record',
  record/column[@name='id_task'])[1])]">
<task_order>
            <id>
                <xsl:value-of select="column[@name='id_task']"/>
            </id>
            <capabilities>
                <!--the fields in capabilities-->
                <xsl:variable name="values" select="key('record',
                  column[@name='id_task'])/
              column[@name='ColMiss']"/>
                <xsl:choose>
                    <xsl:when test="$values ='miss'">
                        <xsl:for-each select="$values ='miss'">
                            <capability>
                                <capability_code>
                                    <xsl:value-of select="column[name='capabilityCode']"/>
                                </capability_code>
                                <available>true</available>
                            </capability>
                        </xsl:for-each>
                    </xsl:when>
                    <xsl:when test="$values =''">
                        <xsl:for-each select="$values =''">
                            <capability>
                                <capability_code>
                                    <xsl:value-of select="column[name='capabilityCode']"/>
                                </capability_code>
                                <required>true</required>
                            </capability>
                        </xsl:for-each>
                    </xsl:when>
                </xsl:choose>
            </capabilities>
        </task_order>
    </xsl:for-each>
</xsl:template>
</xsl:stylesheet>

As you can see the problem is that it won't group the 'capability' tags under one parent but it creates a per record tag, which is normal because of the logic implemented. But I thought there's some other way besides using a key/variable.

Based on this XSL is there a way to go to the desired output?

Thank you again.


Solution

  • I couldn't fully work out your logic, but the first problem you have is how you have defined your key

    <xsl:key name="record" match="record" use="record/column[@name='id_task']"/>
    

    This will look for record elements, by looking for child record elements of them, which doesn't match what you have in your XML. It should be this...

    <xsl:key name="record" match="record" use="column[@name='id_task']"/>
    

    Similarly, for when you use this key in the Muenchian grouping...

    <xsl:for-each select="record[generate-id(.)=generate-id(key('record', column[@name='id_task'])[1])]">
    

    There is also a problem with your xsl:for-each...

    <xsl:for-each select="$values ='miss'">
    

    This is not valid because the result of $values ='miss' is either "true" or "false", and xsl:for-each requires nodes to be selected, not a Boolean.

    Anyway, try this XSLT instead. It doesn't give you the result you need because I could quite work out your exact logic, but it might give you a better starting point...

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" indent="yes"/>
    <xsl:key name="record" match="record" use="column[@name='id_task']"/>
    
    <xsl:template match="comtec">
        <!--grouped by id_task-->
        <xsl:for-each select="record[generate-id(.)=generate-id(key('record', column[@name='id_task'])[1])]">
            <task_order>
                <id>
                    <xsl:value-of select="column[@name='id_task']"/>
                </id>
                <capabilities>
                    <!--the fields in capabilities-->
                    <xsl:variable name="values" select="key('record',column[@name='id_task'])/column[@name='ColMiss']"/>
                    <xsl:for-each select="key('record',column[@name='id_task'])">
                        <capability>
                            <capability_code>
                                <xsl:value-of select="column[@name='capabilityCode']"/>
                            </capability_code>
                            <xsl:choose>
                                <xsl:when test="$values[. ='miss']">
                                    <required>true</required>
                                </xsl:when>
                                <xsl:otherwise>
                                    <available>true</available>
                                </xsl:otherwise>
                            </xsl:choose>
                        </capability>
                    </xsl:for-each>
                </capabilities>
            </task_order>
        </xsl:for-each>
    </xsl:template>
    </xsl:stylesheet>
    

    Do note, an expression like $values[. =''] if any of the values are blank, not necessarily all of them. If you wanted to test for all blanks, you would have to do not($values[. != '']) (i.e. there doesn't exist one that isn't blank).