Home > OS >  how do i get a set of distinct xml node values using XSL
how do i get a set of distinct xml node values using XSL

Time:03-06

I have the following XML input and the desired output and what I am getting shown below. Idea is to set the callLegSet parameter when ever /Scripts/Script node is detected and when /Scripts/Script/Preamble is detected the callLegSet will contain the distinct set of values for the current Script element. I am not sure if this is the proper way to think about xsl an I read that a parameter can be set only once, so that may be an issue here; but if that is so, how is it that the list takes in new values ?

Can someone explain what's going on and how I may fix it.

  • XSL file

    <xsl:stylesheet version="1.0">
      <xsl:variable name="mode" select="1" />
    
      <xsl:key name="keyCallLeg" match="text()" use="." />
    
      <xsl:template match="Scripts/Script">
        <Script>
          <xsl:apply-templates>
            <xsl:with-param
              name="callLegSet"
              select="descendant::Command[(@command='10' or @command='11') and @mode='1']/Fields/Field[@name='type']/Value/text()
            [generate-id()=generate-id(key('keyCallLeg',.)[1])]">
            </xsl:with-param>
          </xsl:apply-templates>
        </Script>
      </xsl:template>
    
      <xsl:template match="Scripts/Script/Preamble">
        <xsl:param name="callLegSet"/>
        <Preamble>
          <pit>
            <xsl:for-each select="$callLegSet">
              <xsl:variable name="currElem" select="."/>
              <Field type="0" name="{$currElem}" mode="{$mode}"/>
            </xsl:for-each>
          </pit>
        </Preamble>
      </xsl:template>
    
      <xsl:template match="Commands"></xsl:template>
      <xsl:template match="Command"></xsl:template>
    
      <xsl:template match="*|@*|text()">
        <xsl:copy>
          <xsl:apply-templates select="*|@*|text()"/>
        </xsl:copy>
      </xsl:template>
    </xsl:stylesheet>

  • my Input file
 

       <xml>
            <Scripts>
                <Script>
              <Preamble></Preamble>
                    <Commands>
                <Command mode="1" command="10">
                            <Fields>
                                <Field ordinal="11" name="type">
                                    <Value>Type_A</Value>
                                </Field>
                            </Fields>
                        </Command>
                <Command mode="1" command="11">
                            <Fields>
                                <Field ordinal="14" name="type">
                                    <Value>Type_A</Value>
                                </Field>
                            </Fields>
                        </Command>
                    </Commands>
                </Script>
                <Script>
              <Preamble></Preamble>
              <Commands>
                <Command mode="1" command="10">
                  <Fields>
                    <Field ordinal="11" name="type">
                      <Value>Type_A</Value>
                    </Field>
                  </Fields>
                </Command>
                <Command mode="1" command="11">
                  <Fields>
                    <Field ordinal="14" name="type">
                      <Value>Type_B</Value>
                    </Field>
                  </Fields>
                </Command>
                <Command mode="1" command="10">
                  <Fields>
                    <Field ordinal="11" name="type">
                      <Value>Type_A</Value>
                    </Field>
                  </Fields>
                </Command>
                        <Command mode="1" command="11">
                            <Fields>
                                <Field ordinal="14" name="type">
                                    <Value>Type_C</Value>
                                </Field>
                            </Fields>
                        </Command>
                    </Commands>
                </Script>
                <Script>
              <Preamble></Preamble>
              <Commands>
                <Command mode="1" command="10">
                  <Fields>
                    <Field ordinal="11" name="type">
                      <Value>Type_B</Value>
                    </Field>
                  </Fields>
                </Command>
                <Command mode="1" command="10">
                  <Fields>
                    <Field ordinal="11" name="type">
                      <Value>Type_Z</Value>
                    </Field>
                  </Fields>
                </Command>
              </Commands>
                </Script>
            </Scripts>
        </xml>

  • Output

    <xml>
      <Scripts>
        <Script>
          <Preamble>
            <pit>
              <Field type="0" name="Type_A" mode="1" />
            </pit>
          </Preamble>
        </Script>
        <Script>
          <Preamble>
            <pit>
              <Field type="0" name="Type_B" mode="1" />
              <Field type="0" name="Type_C" mode="1" />
            </pit>
          </Preamble>
        </Script>
        <Script>
          <Preamble>
            <pit>
              <Field type="0" name="Type_Z" mode="1" />
            </pit>
          </Preamble>
        </Script>
      </Scripts>
    </xml>

  • Desired Output

    <xml>
      <Scripts>
        <Script>
          <Preamble>
            <pit>
              <Field type="0" name="Type_A" mode="1" />
            </pit>
          </Preamble>
        </Script>
        <Script>
          <Preamble>
            <pit>
              <Field type="0" name="Type_A" mode="1" />
              <Field type="0" name="Type_B" mode="1" />
              <Field type="0" name="Type_C" mode="1" />
            </pit>
          </Preamble>
        </Script>
        <Script>
          <Preamble>
            <pit>
              <Field type="0" name="Type_B" mode="1" />
              <Field type="0" name="Type_Z" mode="1" />
            </pit>
          </Preamble>
        </Script>
      </Scripts>
    </xml>

CodePudding user response:

AFAICT, the result you want can be produced quite simply using :

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:key name="k1" match="Command" use="concat(Fields/Field/Value, '|', generate-id(..))" />

<xsl:template match="/xml">
    <xml>
        <Scripts>
            <xsl:for-each select="Scripts/Script">
                <Script>
                    <Preamble>
                        <pit>
                            <xsl:for-each select="Commands/Command[count(. | key('k1', concat(Fields/Field/Value, '|', generate-id(..)))[1]) = 1]">
                                <Field type="0" name="{Fields/Field/Value}" mode="1" />
                            </xsl:for-each>
                        </pit>
                    </Preamble>
                </Script>
            </xsl:for-each>
        </Scripts>
    </xml>
</xsl:template>

</xsl:stylesheet>

And, of course, even simpler in XSLT 2.0 or higher, or with a processor that supports the EXSLT set:distinct() extension function.

  • Related