Подсчет различных элементов и анализ значений, разделенных запятыми, с использованием XSLT - PullRequest
1 голос
/ 24 июня 2010

Предположим, у меня есть такой XML:

<child_metadata>
    <metadata>
        <attributes>
            <metadata_valuelist value="[SampleItem3]"/>
        </attributes>
    </metadata>
    <metadata>
        <attributes>
            <metadata_valuelist value="[SampleItem1]"/>
        </attributes>
    </metadata>
    <metadata>
        <attributes>
            <metadata_valuelist value="[SampleItem1, SampleItem2]"/>
        </attributes>
    </metadata>
</child_metadata>

То, что я хочу сделать, - это подсчитать количество различных значений в метаданных. Существуют следующие различные значения: SampleItem1, SampleItem2 и SampleItem3. Итак, я хочу получить значение 3. (Хотя SampleItem1 встречается дважды, я считаю его только один раз.)

Как я могу сделать это в XSLT?

Я понимаю, что здесь есть две проблемы: во-первых, разделение запятых значений в списках, и, во-вторых, подсчет количества уникальных значений. Однако я не уверен, что смог бы объединить решения этих двух проблем, поэтому я задаю его как один вопрос.

Ответы [ 3 ]

3 голосов
/ 24 июня 2010

Другой способ без расширения:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

    <xsl:variable name="all-value" select="/*/*/*/*/@value"/> 

    <xsl:template match="/"> 
        <xsl:variable name="count"> 
            <xsl:apply-templates select="$all-value"/> 
        </xsl:variable> 
        <xsl:value-of select="string-length($count)"/> 
    </xsl:template>  

    <xsl:template match="@value" name="value">
        <xsl:param name="meta" select="translate(.,'[] ','')"/>
        <xsl:choose>
            <xsl:when test="contains($meta,',')">
                <xsl:call-template name="value">
                    <xsl:with-param name="meta" select="substring-before($meta,',')"/>
                </xsl:call-template>
                <xsl:call-template name="value">
                    <xsl:with-param name="meta" select="substring-after($meta,',')"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:if test="count(.|$all-value[contains(translate(.,'[] ','&#xA;&#xA;&#xA;'),
                                                          concat('&#xA;',$meta,'&#xA;'))][1])=1">
                    <xsl:value-of select="1"/> 
                </xsl:if> 
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template> 

</xsl:stylesheet> 

Примечание : возможно, можно оптимизировать с помощью xsl:key вместо xsl:variable Редактировать : сопоставить хитрые метаданные.

1 голос
/ 26 июня 2010

Это (примечание: только одно) преобразование :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:msxsl="urn:schemas-microsoft-com:xslt"
 >
 <xsl:output method="text"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kValue" match="value" use="."/>

 <xsl:template match="/">
   <xsl:variable name="vRTFPass1">
    <values>
     <xsl:apply-templates/>
    </values>
   </xsl:variable>

   <xsl:variable name="vPass1"
        select="msxsl:node-set($vRTFPass1)"/>

   <xsl:for-each select="$vPass1">
     <xsl:value-of select=
      "count(*/value[generate-id()
                    =
                     generate-id(key('kValue', .)[1])
                    ]
             )
      "/>
   </xsl:for-each>
 </xsl:template>

 <xsl:template match="metadata_valuelist">
  <xsl:call-template name="tokenize">
    <xsl:with-param name="pText" select="translate(@value, '[],', '')"/>
  </xsl:call-template>
 </xsl:template>

 <xsl:template name="tokenize">
    <xsl:param name="pText" />

    <xsl:choose>
      <xsl:when test="not(contains($pText, ' '))">
        <value><xsl:value-of select="$pText"/></value>
      </xsl:when>
      <xsl:otherwise>
        <value>
         <xsl:value-of select="substring-before($pText, ' ')"/>
        </value>
        <xsl:call-template name="tokenize">
         <xsl:with-param name="pText" select=
          "substring-after($pText, ' ')"/>
        </xsl:call-template>
      </xsl:otherwise>
    </xsl:choose>
 </xsl:template>
</xsl:stylesheet>

при применении к предоставленному документу XML :

<child_metadata>
    <metadata>
        <attributes>
            <metadata_valuelist value="[SampleItem3]"/>
        </attributes>
    </metadata>
    <metadata>
        <attributes>
            <metadata_valuelist value="[SampleItem1]"/>
        </attributes>
    </metadata>
    <metadata>
        <attributes>
            <metadata_valuelist value="[SampleItem1, SampleItem2]"/>
        </attributes>
    </metadata>
</child_metadata>

дает требуемый, правильный результат :

3

Примечание : поскольку это решение XSLT 1.0, необходимо преобразовать результаты первогоперейти от печально известного типа RTF к обычному дереву.Это делается с помощью функции xxx: node-set () вашего процессора XSLT 1.0 - в моем случае я использовал msxsl: node-set ().

0 голосов
/ 24 июня 2010

Вы, вероятно, хотите думать об этом в два этапа; сначала выполните преобразование, которое разбивает эти атрибуты значений, а затем подсчитать их довольно просто.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="@value">
    <xsl:call-template name="breakdown">
      <xsl:with-param name="itemlist" select="substring-before(substring-after(.,'['),']')" />
    </xsl:call-template>
  </xsl:template>

  <xsl:template name="breakdown">
    <xsl:param name="itemlist" />
    <xsl:choose>
      <xsl:when test="contains($itemlist,',')">
        <xsl:element name="value">
          <xsl:value-of select="normalize-space(substring-before($itemlist,','))" />
        </xsl:element>
        <xsl:call-template name="breakdown">
          <xsl:with-param name="itemlist" select="substring-after($itemlist,',')" />
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:element name="value">
          <xsl:value-of select="normalize-space($itemlist)" />
        </xsl:element>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

Помимо шаблона «поймать все» внизу, он собирает любые атрибуты значений в заданном вами формате и разбивает их на отдельные элементы (как подэлементы элемента «metadata_valuelist»), например:

...
<metadata_valuelist>
  <value>SampleItem1</value>
  <value>SampleItem2</value>
</metadata_valuelist>
...

Выбор 'substring-before / substring-after, который вы видите рядом с верхней частью, удаляет' ['и'] 'перед передачей его шаблону' breakdown '. Этот шаблон будет проверять, есть ли запятая в параметре 'itemlist', и если он есть, он выплевывает текст перед ним как содержимое элемента 'value', а затем рекурсивно вызывает себя с остальной частью списка. Если в параметре не было запятой, он просто выводит все содержимое параметра в виде элемента «значение».

Тогда просто запустите это:

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

  <xsl:key name="itemvalue" match="value" use="text()" />

  <xsl:template match="/">
    <xsl:value-of select="count(//value[generate-id(.) = generate-id(key('itemvalue',.)[1])])" />
  </xsl:template>
</xsl:stylesheet>

в XML, который вы получите от первого преобразования, и он просто выдаст одно значение в виде текстового вывода, которое сообщит вам, сколько у вас различных значений.

РЕДАКТИРОВАТЬ: Я, вероятно, должен указать, что это решение делает несколько предположений о вашем вводе:

  • Нигде в документе нет атрибутов с именем 'value'; если таковые имеются, вы можете изменить соответствие @value, чтобы выделить их специально.
  • Нет нигде в документе элементов с именем 'value'; поскольку первое преобразование создает их, второе не сможет различить два. Если есть, вы можете заменить две строки <xsl:element name="value"> на имя элемента, которое еще не использовалось.
  • Содержимое атрибута @value всегда начинается с «[» и заканчивается «]», и в списке нет символов «]»; если есть, функция 'substring-before' будет отбрасывать все после первого ']', а не только ']' в конце.
  • В названиях предметов, которые вы хотите сосчитать, нет запятых, например, [SampleItem1, "Sample2,3"]. Если они есть, «Sample2» и «3» будут рассматриваться как отдельные элементы.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...