xslt1.0 (firefox): переформатирование XML-кода на основе общего количества различных значений - PullRequest
2 голосов
/ 17 января 2012

Я думаю, что эту проблему невозможно решить с помощью XSLT, так что мне придется что-то делать с JS или просто не реализовывать.Но прежде чем сдаться, конечно, я должен опубликовать здесь, чтобы увидеть, если я не прав, и XSLT может сделать такую ​​логику.1003 *

EDIT2: Я должен исправить ошибку в предоставленном коде XML.Решение должно иметь возможность обрабатывать несколько узлов с одинаковыми значениями.

Концепция заключается в том, что мне нужно переформатировать код, включая пустые теги, которые отражают общее количество различных значений.Трудно объяснить, гораздо проще понять, глядя на код.

Исходный код XML

<data>
    <prot seq="AAA">
        <node num="4">1345</node>
    </prot>
    <prot seq="BBB">
        <node num="7">6666</node>
    </prot>
    <prot seq="CCC">
        <node num="10">3e33</node>
    </prot>
    <prot seq="DDD">
        <node num="4">1345</node>
    </prot>
    <prot seq="EEE">
        <node num="10">3e33</node>
    </prot>
</data>

И желаемый результат

<root>
    <prot seq="AAA">
        <node num="4">1345</node><node num="7">-</node><node num="10">-</node>
    </prot>
    <prot seq="BBB">
        <node num="4">-</node><node num="7">6666</node><node num="10">-</node>
    </prot>
    <prot seq="CCC">
        <node num="4">-</node><node num="7">-</node><node num="10">3e33</node>
    </prot>
    <prot seq="DDD">
        <node num="4">1345</node><node num="7">-</node><node num="10">-</node>
    </prot>
    <prot seq="EEE">
        <node num="4">-</node><node num="7">-</node><node num="10">3e33</node>
    </prot>
</root>

Есть идеи?

Спасибо!

EDIT3: из решения Dimitri я получил это упрощенное решение

<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="kNodeByNum" match="/data/prot/node" use="@num"/>
<xsl:template match="/">
    <root>
        <xsl:apply-templates select="/data/prot"/>
    </root>
</xsl:template>
<xsl:template match="/data/prot">
  <xsl:variable name="current_num" select="node/@num"/>
  <xsl:variable name="current_value" select="node"/>
  <prot seq="{@seq}">
    <xsl:for-each select="/data/prot/node[
    generate-id()
    =
    generate-id(key('kNodeByNum', @num)[1])
    ]">
      <xsl:choose>
        <xsl:when test="@num = $current_num">
          <node num="{@num}"><xsl:value-of select="$current_value"/></node>
        </xsl:when>
        <xsl:otherwise>
          <node num="{@num}">-</node>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each>
  </prot>
</xsl:template>
</xsl:stylesheet>

Однако этот код также не может обрабатывать количество узлов в Firefox и выходитв бесконечно похожем цикле (и я вынужден принудительно закрывать firefox).

Но я думаю, что это не имеет никакого отношения к количеству узлов, но что-то не так с кодом (?)

Ответы [ 2 ]

2 голосов
/ 18 января 2012

Я не проверял это в FireFox, но он работает в Xalan и Saxon ...

Редактировать: протестирован в FireFox 8.0 и добавил комментарии к таблице стилей

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

  <!--Identity Template - Anything not matched by another template will be 
  copied without changes.-->
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <!--Change the name of the "data" element to "root" element.-->
  <xsl:template match="data">
    <root>
      <xsl:apply-templates/>
    </root>
  </xsl:template>

  <xsl:template match="prot">
    <!--Save the current "num" attribute from the child "node" element.-->
    <xsl:variable name="vCurrNum" select="node/@num"/>    
    <prot>
      <!--This will pass all attributes of "prot" through the identity template.-->
      <xsl:apply-templates select="@*"/>
      <!--This will process the the current "node" element and also any "node" elements in other
      "prot" elements that don't have the same "num" attribute as the current "node".-->
      <xsl:apply-templates select="node|/*/prot/node[@num != $vCurrNum]">
        <!--This passes the current "num" attribute to the "node" template that gets matched below.-->
        <xsl:with-param name="pNum" select="$vCurrNum"/>
        <!--This will sort the "node" elements based on their "num" attribute.-->
        <xsl:sort select="@num" data-type="number" order="ascending"/>
      </xsl:apply-templates>
    </prot>
  </xsl:template>

  <xsl:template match="node">
    <!--This is the "num" attribute value passed in the xsl:apply-templates above.-->
    <xsl:param name="pNum"/>
      <!--This will pass all attributes of "node" through the identity template.-->
      <xsl:choose>
        <!--If the current "num" attribute is the same as the "num" attribute passed to 
        the xsl:apply-templates ($pNum), output the value.-->
        <xsl:when test="@num = $pNum">          
          <node>
            <xsl:apply-templates select="@*"/>
            <xsl:apply-templates/>  
          </node>          
        </xsl:when>
        <!--This ensures we only get one of the other "node" elements. (The first "node" with that "num".)-->
        <xsl:when test="not(parent::prot[preceding-sibling::prot[node[@num = current()/@num]]])">
          <node>
            <xsl:apply-templates select="@*"/>
            <xsl:text>-</xsl:text>            
          </node>
        </xsl:when>
        <!--Ignore any other repeated node/num combinations.-->
        <xsl:otherwise/>                
      </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

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

<root>
   <prot seq="AAA">
      <node num="4">1345</node>
      <node num="7">-</node>
      <node num="10">-</node>
   </prot>
   <prot seq="BBB">
      <node num="4">-</node>
      <node num="7">6666</node>
      <node num="10">-</node>
   </prot>
   <prot seq="CCC">
      <node num="4">-</node>
      <node num="7">-</node>
      <node num="10">3e33</node>
   </prot>
</root>
1 голос
/ 18 января 2012

Вот краткое и простое (без xsl:choose, xsl:when и xsl:otherwise) решение :

<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:template match="node()|@*">
  <xsl:param name="pParent" select="/.."/>
  <xsl:copy>
   <xsl:apply-templates select="node()|@*">
    <xsl:with-param name="pParent" select="$pParent"/>
   </xsl:apply-templates>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="prot">
  <xsl:copy>
   <xsl:copy-of select="@*"/>
   <xsl:apply-templates select="../prot/node">
    <xsl:with-param name="pParent" select="."/>
   </xsl:apply-templates>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="node/text()">
  <xsl:param name="pParent" select="/.."/>

  <xsl:variable name="vSameParent" select=
  "boolean(not((../.. | $pParent)[2]))"/>

  <xsl:value-of select=
  "concat(substring('-', 1 +$vSameParent),
                    self::node()[$vSameParent]
                   )
  "/>
 </xsl:template>
</xsl:stylesheet>

, когда это преобразование применяется кпредоставляется XML-документ :

<data>
    <prot seq="AAA">
        <node num="4">1345</node>
    </prot>
    <prot seq="BBB">
        <node num="7">6666</node>
    </prot>
    <prot seq="CCC">
        <node num="10">3e33</node>
    </prot>
</data>

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

<data>
   <prot seq="AAA">
      <node num="4">1345</node>
      <node num="7">-</node>
      <node num="10">-</node>
   </prot>
   <prot seq="BBB">
      <node num="4">-</node>
      <node num="7">6666</node>
      <node num="10">-</node>
   </prot>
   <prot seq="CCC">
      <node num="4">-</node>
      <node num="7">-</node>
      <node num="10">3e33</node>
   </prot>
</data>

Объяснение :

  1. Мы используем и переопределяем модифицированную версию правила идентификации - которая принимает и передает параметр с именем $pParent.

  2. Параметр $pParent содержит элемент node, который выдал xsl:apply-templates, частью которого является обработка текущего узла.

  3. У нас есть два шаблона, которые переопределяют правило идентификации.Первый переопределяющий шаблон соответствует любому элементу prot.Оно почти идентично правилу идентификации, но оно устанавливает для параметра $pParent значимое значение (сам этот узел).

  4. Второй переопределяющий шаблон соответствует любому текстовому узлу, который являетсяпотомок любого node элемента.Здесь, в зависимости от того, идентифицирует ли значение $pParent прародителя сопоставляемого текстового узла, мы выводим соответственно значение текстового узла или просто "-".

  5. Решение о том, что выводить, принимается без использования каких-либо явных условных инструкций.Вместо этого мы используем функцию XPath concat() с двумя аргументами, один из которых является непустой строкой.Чтобы убедиться в этом свойстве, мы используем логическую переменную $vSameParent, которая указана таким образом, что ее значение равно true() именно тогда, когда прародитель совпадающего текстового узла идентичен узлу, содержащемуся в $pParent.Наконец, мы используем тот факт, что при использовании в качестве числа логическое значение true() преобразуется в 1, а логическое значение false() преобразуется в 0.

Обновление : Это новое решение для модифицированной проблемой OP:

<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="kNodeByNum" match="node" use="@num"/>

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

 <xsl:template match="prot">
  <xsl:copy>
   <xsl:copy-of select="@*"/>
   <xsl:apply-templates select=
   "../prot/node
            [generate-id()
            =
             generate-id(key('kNodeByNum', @num)[1])
            ]
   ">
    <xsl:with-param name="pNum" select="node/@num"/>
   </xsl:apply-templates>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="node/text()">
  <xsl:param name="pNum" select="/.."/>

  <xsl:variable name="vSameNum" select=
  "../@num = $pNum"/>

  <xsl:value-of select=
  "concat(substring('-', 1 +$vSameNum),
                    self::node()[$vSameNum]
                   )
  "/>
 </xsl:template>
</xsl:stylesheet>

, когда это преобразование применяется к предоставленному XML-документу :

<data>
    <prot seq="AAA">
        <node num="4">1345</node>
    </prot>
    <prot seq="BBB">
        <node num="7">6666</node>
    </prot>
    <prot seq="CCC">
        <node num="10">3e33</node>
    </prot>
    <prot seq="DDD">
        <node num="4">1345</node>
    </prot>
    <prot seq="EEE">
        <node num="10">3e33</node>
    </prot>
</data>

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

<data>
   <prot seq="AAA">
      <node num="4">1345</node>
      <node num="7">-</node>
      <node num="10">-</node>
   </prot>
   <prot seq="BBB">
      <node num="4">-</node>
      <node num="7">6666</node>
      <node num="10">-</node>
   </prot>
   <prot seq="CCC">
      <node num="4">-</node>
      <node num="7">-</node>
      <node num="10">3e33</node>
   </prot>
   <prot seq="DDD">
      <node num="4">1345</node>
      <node num="7">-</node>
      <node num="10">-</node>
   </prot>
   <prot seq="EEE">
      <node num="4">-</node>
      <node num="7">-</node>
      <node num="10">3e33</node>
   </prot>
</data>

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

...