Return all XML element taken one time in a node

2019-06-14 18:24发布

Each node contains a list of colour in the field LIGHT_COLOUR, identied by a letter R(red), W(white), etc etc. In the field LIGHT_COLOUR can be only one colour/letter or more than one separated by '-'.

This is my XML for testing:

<LIGHT_INFORMATION_LIST>
    <LIGHT_INFORMATION>
        <LIGHT_CHARACTERISTICS>Al</LIGHT_CHARACTERISTICS>
        <LIGHT_COLOUR>W-G</LIGHT_COLOUR>
    </LIGHT_INFORMATION>
    <LIGHT_INFORMATION>
        <LIGHT_CHARACTERISTICS>Al</LIGHT_CHARACTERISTICS>
        <LIGHT_COLOUR>W-R</LIGHT_COLOUR>
    </LIGHT_INFORMATION>
    <LIGHT_INFORMATION>
        <LIGHT_CHARACTERISTICS>F</LIGHT_CHARACTERISTICS>
        <LIGHT_COLOUR>R</LIGHT_COLOUR>
    </LIGHT_INFORMATION>
    <LIGHT_INFORMATION>
        <LIGHT_CHARACTERISTICS>F</LIGHT_CHARACTERISTICS>
        <LIGHT_COLOUR>G</LIGHT_COLOUR>
    </LIGHT_INFORMATION>
    <LIGHT_INFORMATION>
        <LIGHT_CHARACTERISTICS>F</LIGHT_CHARACTERISTICS>
        <LIGHT_COLOUR>W</LIGHT_COLOUR>
    </LIGHT_INFORMATION>
</LIGHT_INFORMATION_LIST>

This is my XSLT:

<xsl:for-each select="LIGHT_INFORMATION_LIST">
    <xsl:for-each select="LIGHT_INFORMATION">
        <xsl:for-each select="LIGHT_COLOUR">
            <xsl:value-of select="."/>
        </xsl:for-each> 
    </xsl:for-each> 
</xsl:for-each> 

Now my output is : W-G W-R R G W
This is all element in LIGHT_COLOUR field.

I would in Output all element taken one time (LIGHT_COLOUR distinct).

In Output I would : W G R

标签: xslt xslt-1.0
2条回答
smile是对你的礼貌
2楼-- · 2019-06-14 18:41

If - as I suspect - there is a known list of possible colors, and in this list each color is represented by a single letter, then this could be dead simple. For example, the following stylesheet:

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:param name="color-list">RGBCMYKW</xsl:param> 

<xsl:template match="/">
    <xsl:variable name="present-colors">
        <xsl:for-each select="LIGHT_INFORMATION_LIST/LIGHT_INFORMATION">
            <xsl:value-of select="translate(LIGHT_COLOUR, '-', '')"/>
        </xsl:for-each> 
    </xsl:variable> 
    <result>
        <xsl:value-of select="translate($color-list, translate($color-list, $present-colors, ''), '')"/>
    </result>
</xsl:template>

</xsl:stylesheet>

when applied to your example input, would return:

<?xml version="1.0" encoding="UTF-8"?>
<result>RGW</result>
查看更多
▲ chillily
3楼-- · 2019-06-14 18:53

One way to do this would be first to create a variable containing all the separate LIGHT_COLOUR elements concatenated into one string

  <xsl:variable name="allColours">
     <xsl:for-each select="LIGHT_INFORMATION_LIST/LIGHT_INFORMATION">
        <xsl:value-of select="concat(LIGHT_COLOUR, '-')" />
     </xsl:for-each>
  </xsl:variable>

So, in your example, it would be set to "W-G-T-W-R-E-E-R-E-E-".

You could then have a recursive template that splits the string into characters, but at the same time keeps a running list of the distinct values. So, on each iteration, it gets the next character before the hyphen and checks whether it is not already in the list of distinct values. If not, it adds it to this list, and the recursive template carries on processing until nothing left.

Try this XSLT

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

   <xsl:template match="/">
      <xsl:variable name="allColours">
         <xsl:for-each select="LIGHT_INFORMATION_LIST/LIGHT_INFORMATION">
            <xsl:value-of select="concat(LIGHT_COLOUR, '-')" />
         </xsl:for-each>
      </xsl:variable>

      <xsl:call-template name="tokenizeDistinct">
         <xsl:with-param name="string" select="$allColours" />
      </xsl:call-template>
   </xsl:template>

   <xsl:template name="tokenizeDistinct">
      <xsl:param name="string" />
      <xsl:param name="splitChar" select="'-'" />
      <xsl:param name="distinctList" select="' '" />

      <xsl:variable name="nextChar" select="substring-before(concat($string, $splitChar), $splitChar)" />
      <xsl:variable name="newDistinctList">
         <xsl:value-of select="$distinctList" />
         <xsl:if test="$nextChar != '' and not(contains($distinctList, concat(' ', $nextChar, ' ')))">
            <xsl:value-of select="concat($nextChar, ' ')" />
         </xsl:if>
      </xsl:variable>
      <xsl:choose>
         <xsl:when test="contains($string, $splitChar)">
            <xsl:call-template name="tokenizeDistinct">
               <xsl:with-param name="string" select="substring-after($string, $splitChar)" />
               <xsl:with-param name="splitChar" select="$splitChar" />
               <xsl:with-param name="distinctList" select="$newDistinctList" />
            </xsl:call-template>
         </xsl:when>
         <xsl:otherwise>
            <xsl:value-of select="$newDistinctList" />
         </xsl:otherwise>
      </xsl:choose>
   </xsl:template>
</xsl:stylesheet>

When applied to your XML, the following is output

W G R 
查看更多
登录 后发表回答