Выберите узлы, у которых нет ключа с помощью XSLT / XPath - PullRequest
3 голосов
/ 13 декабря 2011

Я использую метод Мюнхена для группировки узлов в документе XML.

В качестве части моего вывода я хотел бы выбрать все узлы, к которым у меня есть , а не * 1004.* назначил ключ.

Я пробовал

<xsl:apply-templates select="*[key('kcWWPN','')]"/>

, но это не работает должным образом, поскольку он не выбирает никаких узлов.

Любойпредложения о том, как это сделать?

Ответы [ 2 ]

5 голосов
/ 13 декабря 2011

Как часть моего вывода, я хотел бы выбрать все узлы, к которым я не назначен ключ.

Хороший вопрос, + 1.

Вот два разных, но простых решения :

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

I. Просто определите встречную клавишу :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kDByClass" match="d" use="@class"/>

 <xsl:key name="kCounterKey" match="*[not(self::d)]" use="."/>

 <xsl:template match="*[key('kCounterKey', .)]">
  Counter Keyed: <xsl:value-of select="name()"/>

  <xsl:apply-templates/>
 </xsl:template>

 <xsl:template match="text()"/>
</xsl:stylesheet>

когда это преобразование применяется к следующему документу XML (заимствовано у @lwburk):

<root>
    <d class="test">1</d>
    <d class="test">2</d>
    <d class="something">1</d>
    <q>3</q>
</root>

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

  Counter Keyed: root
  Counter Keyed: q

II. Использовать установленную разность всех элементов и всех ключевых элементов (последняя найдена с использованием мюнхевской группировки):

Это решение проще, чем первое, потому что не нужно составлять контрключ:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kDByClass" match="d" use="@class"/>

 <xsl:key name="kCounterKey" match="*[not(self::d)]" use="."/>

 <xsl:variable name="vKeyedValues" select=
 "//*[generate-id()
     = generate-id(key('kDByClass', @class)[1])
     ]
      /@class
  "/>

  <xsl:variable name="vKeyedElements" select=
   "key('kDByClass', $vKeyedValues)"/>

  <xsl:variable name="vNonKeyedElements" select=
  "//*[not(count(.|$vKeyedElements) = count($vKeyedElements))]
  "/>

 <xsl:template match="/">
  <xsl:for-each select="$vNonKeyedElements">
    Not Keyed: <xsl:value-of select="name()"/>
  </xsl:for-each>
 </xsl:template>

 <xsl:template match="text()"/>
</xsl:stylesheet>

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

Not Keyed: root
Not Keyed: q

Примечание : последнее решение может не работать, когда ключ не является узлом, а является результатом функции, такой как substring-before(). В этом случае мы просто модифицируем исходный ключ, поэтому его атрибут use просто: use="." и используем это решение с измененным исходным ключом. Можно доказать, что эта процедура создает правильный, требуемый набор элементов.

2 голосов
/ 13 декабря 2011

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

Например, предположим, что ключи были созданы с использованием конкатенации значения атрибута class каждого элемента и его строкового значения.Мы могли бы выбрать все элементы в документе, которые не имеют назначенной клавиши, используя следующее:

<xsl:apply-templates
            select="//*[not(count(.|key('byClass', concat(@class, '|', .)))=
                            count(key('byClass', concat(@class, '|', .))))]"/>

Вот полная демонстрация:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>
    <xsl:key name="byClass" match="d" use="concat(@class, '|', .)"/>
    <xsl:template match="/">
        <xsl:apply-templates
            select="//*[not(count(.|key('byClass', concat(@class, '|', .)))=
                            count(key('byClass', concat(@class, '|', .))))]"
            mode="test"/>
    </xsl:template>
    <xsl:template match="*" mode="test">
        <xsl:value-of
            select="concat('Node ', local-name(), 
               ' not assigned a key', '&#xa;')"/>
        <xsl:apply-templates/>
    </xsl:template>
    <xsl:template match="text()"/>
</xsl:stylesheet>

Применяется к этому входу:

<root>
    <d class="test">1</d>
    <d class="test">2</d>
    <d class="something">1</d>
    <q class="test">1</q>
</root>

Создает:

Node root not assigned a key
Node q not assigned a key

Обратите внимание, что ключ, создаваемый q, является фактическим ключом, который отображается на группу узлов в документе, но этот элемент отсутствует в наборе.возвращается key('byClass', 'test|1'), поэтому мы говорим, что этому узлу не был назначен ключ.

Обратите также внимание, что пустая строка ('') является совершенно допустимым ключом, поэтому этого не происходитто, на что вы надеялись, будет:

<xsl:apply-templates select="*[key('kcWWPN', '')]"/>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...