2010-06-23 7 views
1

Предположим, что у меня есть код:Подсчет отдельных элементов и анализ значений, разделенных запятой, используя XSLT

<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> 

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

Как это сделать в XSLT?

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

ответ

3

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

<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 Edit: Match хитрых метаданными.

+0

Это решение дает ожидаемый результат в данном конкретном случае. Однако при использовании следующих значений: '[SampleItem3]', '[SampleItem1SampleItem2, SampleItem2]', '[SampleItem1, SampleItem1SampleItem2]' - он вызывает неверный ответ: 3. Правильный ответ: 4. –

+0

Это довольно аккуратное решение; Незначительная точка, но вам не нужен элемент '' вверху? – Flynn1179

+0

@ Dimitre: Вы правы! Я отредактировал ответ. Кроме того, я ищу решение, не раздвигая дерево дважды. – 2010-06-24 13:38:38

-1

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

<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> 
... 

«подстрока-до/подстрока-после выбора вы видите вблизи верхних полос выключить„[“и„]“перед передачей его в„пробоя“шаблона. Этот шаблон будет проверять, есть ли запятая в его параметре 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 вы получите от первого преобразования, и это будет просто выплюнуть одно значение в качестве вывода текста, который говорит вам, сколько различных значений у вас есть.

EDIT: Я, вероятно, следует отметить, это решение делает несколько предположений о вашем входе:

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

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

<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().

Смежные вопросы