2012-06-20 2 views
4

У меня есть плоский структурированный XML-файл, как показано ниже:Flat на вложенную структуру на основе значения атрибута с помощью XSLT

<rs> 
    <r id="r1" lev="0"/> 
    <r id="r2" lev="1"/> 
    <r id="r3" lev="0"/> 
    <r id="r4" lev="1"/> 
    <r id="r5" lev="2"/> 
    <r id="r6" lev="3"/> 
    <r id="r7" lev="0"/> 
    <r id="r8" lev="1"/> 
    <r id="r9" lev="2"/> 
</rs> 

, которые мне нужно преобразовать в гнездовой один. Правило - это что-то, все r[number(@lev) gt 0] следует вложить в r[number(@lev) eq 0]. И выход был бы что-то подобное:

<rs> 
    <r id="r1"> 
     <r id="r2"/> 
    </r> 
    <r id="r3"> 
     <r id="r4"> 
      <r id="r5"> 
       <r id="r6"/> 
      </r> 
     </r> 
    </r> 
    <r id="r7"> 
     <r id="r8"> 
      <r id="r9"/> 
     </r> 
    </r> 
</rs> 

То, что я пытался это следующее преобразование:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    exclude-result-prefixes="xs" 
    version="2.0"> 

    <xsl:output indent="yes"/> 

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

    <xsl:template match="r"> 
     <xsl:variable name="lev" select="number(@lev)" as="xs:double"/> 
     <r> 
      <xsl:copy-of select="@id"/> 
      <xsl:apply-templates select="following-sibling::r[not(number(@lev) eq $lev) 
             and 
             count(preceding-sibling::r[number(@lev) eq $lev]) eq 1]"/> 
     </r> 
    </xsl:template> 

</xsl:stylesheet> 

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

ответ

1

Dimitre обычно дает ответы на вопросы, используя XSLT 1.0, если не запрошено иное. Это может быть правильной догадкой, но я считаю, что стоит отметить, что XSLT 2.0 теперь довольно широко доступен и используется, и что код для группировки проблем в XSLT 2.0 намного проще (он не всегда может быть намного короче, но он гораздо более читаемым). В отличие от Dimitre, у меня нет времени или желания давать красивые полные и проверенные решения по каждому вопросу, но если вы хотите увидеть решение XSLT 2.0 для этой проблемы, то в документе, который я написал несколько лет назад, есть один:

http://www.saxonica.com/papers/ideadb-1.1/mhk-paper.xml

Поиск рекурсивного имя шаблона = "процесс уровня".

+0

Спасибо, сэр, за то, что он показал мне правильное направление. – Cylian

+0

Michael Kay: Я признаю все, что вы говорите о моих ответах, и несете полную ответственность за это :) –

+0

Я просто хочу, чтобы XSLT 2 был так же широко использован, насколько он был доступен. Ни один из основных браузеров или инфраструктура .NET еще не поддерживает его, без сторонних библиотек. – Flynn1179

2

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

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

<xsl:key name="kRByLevelAndParent" match="r" 
    use="concat(generate-id(preceding-sibling::r 
          [not(@lev >= current()/@lev)][1]), 
          @lev 
         )"/> 

<xsl:template match="/*"> 
    <rs> 
    <xsl:apply-templates select="key('kRByLevelAndParent', '0')"/> 
    </rs> 
</xsl:template> 

<xsl:template match="r"> 
    <r id="{@id}"> 
    <xsl:apply-templates select= 
    "key('kRByLevelAndParent', 
     concat(generate-id(), @lev+1) 
     )"/> 
    </r> 
</xsl:template> 
</xsl:stylesheet> 

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

<rs> 
    <r id="r1" lev="0"/> 
    <r id="r2" lev="1"/> 
    <r id="r3" lev="0"/> 
    <r id="r4" lev="1"/> 
    <r id="r5" lev="2"/> 
    <r id="r6" lev="3"/> 
    <r id="r7" lev="0"/> 
    <r id="r8" lev="1"/> 
    <r id="r9" lev="2"/> 
</rs> 

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

<rs> 
    <r id="r1"> 
     <r id="r2"/> 
    </r> 
    <r id="r3"> 
     <r id="r4"> 
     <r id="r5"> 
      <r id="r6"/> 
     </r> 
     </r> 
    </r> 
    <r id="r7"> 
     <r id="r8"> 
     <r id="r9"/> 
     </r> 
    </r> 
</rs> 

Объяснение:

Позиционная группировку с использованием составного ключа - для всех своих «детей» элемент является первым предшествующими двойниками, так что его атрибут lev меньше, чем их соответствующего lev атрибута.

+0

Работает должным образом. Большое спасибо, сэр! – Cylian

+0

@ Cylian: Добро пожаловать. –

+0

Еще один вопрос, так как невозможно использовать '' xsl: key'' в '' variable'', есть ли другой способ выполнить задание без использования '' xsl: key''? – Cylian

0

Как мне нужно применить преобразование во временных переменных, использование xsl:key не помогло бы. И если мне нужно использовать Решение Dimitre Мне пришлось изменить существующий код.

И, очевидно, это была моя ошибка, о которой я не особо рассказывал в этом вопросе в моем вопросе.

От ссылки //programlisting[contains(.,'xsl:template name="process-level"')] от Dr.Кей Я заключил решение, может быть какой-то другой человек может использовать его позже:

Стилевая

<xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    exclude-result-prefixes="xs"> 
    <xsl:output indent="yes"/> 

    <xsl:template match="/*"> 
     <rs> 
      <xsl:call-template name="process-level"> 
       <xsl:with-param name="context" 
        select="r"/> 
       <xsl:with-param name="level" 
        select="0"/> 
      </xsl:call-template> 
     </rs> 
    </xsl:template> 

    <xsl:template name="process-level"> 
     <xsl:param name="context" required="yes" as="element()*"/> 
     <xsl:param name="level" as="xs:double"/> 
     <xsl:for-each-group select="$context" 
      group-starting-with="*[number(@lev) eq $level]"> 
      <xsl:element name="{name()}"> 
       <!--<xsl:variable name="position" as="xs:double"> 
        <xsl:number level="any" count="*[starts-with(local-name(), 'r')]"/> 
       </xsl:variable>--> 
       <xsl:copy-of select="@id"/> 
       <xsl:call-template name="process-level"> 
        <xsl:with-param name="context" select="current-group()[position() != 1]"/> 
        <xsl:with-param name="level" select="$level + 1"/> 
       </xsl:call-template> 
      </xsl:element> 
     </xsl:for-each-group> 
    </xsl:template> 

</xsl:stylesheet> 

Входной XML

<rs> 
    <r id="r1" lev="0"/> 
    <r id="r2" lev="1"/> 
    <r id="r3" lev="0"/> 
    <r id="r4" lev="1"/> 
    <r id="r5" lev="2"/> 
    <r id="r6" lev="3"/> 
    <r id="r7" lev="0"/> 
    <r id="r8" lev="1"/> 
    <r id="r9" lev="2"/> 
</rs> 

И

<rs> 
    <r id="r1"> 
     <r id="r2"/> 
    </r> 
    <r id="r3"> 
     <r id="r4"> 
     <r id="r5"> 
      <r id="r6"/> 
     </r> 
     </r> 
    </r> 
    <r id="r7"> 
     <r id="r8"> 
     <r id="r9"/> 
     </r> 
    </r> 
</rs> 
Смежные вопросы