Как использовать XSLT для маркировки определенных узлов уникальными последовательными увеличивающимися целочисленными идентификаторами? - PullRequest
4 голосов
/ 07 июня 2010

Я пытаюсь использовать XSLT для преобразования документа, помечая группу узлов XML целочисленными идентификаторами, начиная с 0 и увеличивая на единицу для каждого узла в группе. XML, переданный в таблицу стилей, должен быть отражен, но дополнен, чтобы включить эту дополнительную информацию.

Чтобы было ясно, о чем я говорю, вот как это преобразование будет выражаться с помощью DOM:

states = document.getElementsByTagName("state");
for( i = 0; i < states.length; i++){
    states.stateNum = i;
}

Это очень просто с DOM, но у меня гораздо больше проблем с этим с XSLT. Текущая стратегия, которую я разработал, состояла в том, чтобы начать с преобразования идентификаторов, а затем создать глобальную переменную, которая выбирает и хранит все узлы, которые я хочу нумеровать. Затем я создаю шаблон, соответствующий этому виду узла. Таким образом, идея заключается в том, что в шаблоне я бы искал положение соответствующего узла в списке переменных глобальной переменной, что дало бы мне уникальный номер, который я мог бы затем установить в качестве атрибута.

Проблема с этим подходом состоит в том, что функция позиции может использоваться только с узлом контекста, поэтому что-то вроде следующего недопустимо:

<template match="state">
    <variable name="stateId" select="@id"/>
    <variable name="uniqueStateNum" select="$globalVariable[@id = $stateId]/position()"/>
</template>

То же самое верно для следующего:

<template match="state">
    <variable name="stateId" select="@id"
    <variable name="stateNum" select="position($globalVariable[@id = $stateId])/"/>
</template>

Чтобы использовать position () для поиска позиции элемента в $ globalVariable, необходимо изменить контекстный узел.

Я нашел решение, но оно крайне неоптимально. По сути, в шаблоне я использую for-each для перебора глобальной переменной. For-each изменяет узел контекста, поэтому это позволяет мне использовать position () так, как я описал. Проблема заключается в том, что это превращает то, что обычно является операцией O (n), в операцию O (n ^ 2), где n - длина списка узлов, поскольку для этого требуется выполнять итерацию по всему списку всякий раз, когда сопоставляется шаблон. Я думаю, что должно быть более элегантное решение.

В целом, вот моя текущая (немного упрощенная) таблица стилей xslt:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:s="http://www.w3.org/2005/07/scxml"
    xmlns="http://www.w3.org/2005/07/scxml"
    xmlns:c="http://msdl.cs.mcgill.ca/"
    version="1.0">
    <xsl:output method="xml"/>

    <!-- we copy them, so that we can use their positions as identifiers -->
    <xsl:variable name="states" select="//s:state" />


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

    <xsl:template match="s:state">

        <xsl:variable name="stateId">
            <xsl:value-of select="@id"/>
        </xsl:variable>

        <xsl:copy>
            <xsl:apply-templates select="@*"/>

            <xsl:for-each select="$states">
                <xsl:if test="@id = $stateId">
                    <xsl:attribute name="stateNum" namespace="http://msdl.cs.mcgill.ca/">
                        <xsl:value-of select="position()"/>
                    </xsl:attribute>
                </xsl:if>
            </xsl:for-each>

            <xsl:apply-templates select="node()"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

Буду признателен за любые советы. Спасибо.

Ответы [ 2 ]

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

Это преобразование :

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

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

 <xsl:template match="s:state">
  <xsl:variable name="vNum">
   <xsl:number level="any" count="s:state"/>
  </xsl:variable>

  <xsl:copy>
   <xsl:copy-of select="@*"/>

   <xsl:attribute name="stateId">
    <xsl:value-of select="@id"/>
   </xsl:attribute>

   <xsl:attribute name="id">
     <xsl:value-of select="$vNum -1"/>
   </xsl:attribute>

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

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

<scxml xmlns="http://www.w3.org/2005/07/scxml">
    <state id="Compound1">
        <state id="Basic1"/>
        <state id="Basic2"/>
        <state id="Basic3"/>
    </state>
</scxml>

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

<scxml xmlns="http://www.w3.org/2005/07/scxml">
    <state stateId="Compound1" id="0">
        <state stateId="Basic1" id="1"/>
        <state stateId="Basic2" id="2"/>
        <state stateId="Basic3" id="3"/>
    </state>
</scxml>
0 голосов
/ 07 июня 2010

Простейший подход:

<xsl:template match="s:state">
  <xsl:copy>
    <xsl:apply-templates select="@*"/>
    <xsl:attribute name="stateNum" namespace="http://msdl.cs.mcgill.ca/">
      <xsl:value-of select="count(preceding::s:state)" />
    </xsl:attribute>
    <xsl:apply-templates select="node()"/>
  </xsl:copy>
</xsl:template>

Не уверен, как ваш XSLT-процессор обрабатывает ось preceding, так что в любом случае это что-то для сравнения.

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