Обернуть группу узлов XML - PullRequest
4 голосов
/ 06 сентября 2010

Я работаю с PHP5, и мне нужно преобразовать XML в следующую форму:

<list>
    <item label="(1)">some text</item>
    <item label="(2)">
        <anotherNode>some text</anotherNode
        <item label="a">some text</item>
        <item label="b">some text</item>          
    </item>
</list>

На что-то вроде этого:

<list>
    <item label="(1)">some text</item>
    <item label="(2)">
        <anotherNode>some text</anotherNode>
        <list> <!-- opening new wrapper node-->
            <item label="a">some text</item>
            <item label="b">some text</item>
        </list> <!-- closing new wrapper node-->
    </item>
</list> 

Как вы можете видеть выше, мне нужно добавить узел-обертку для любых узлов 'item', которые еще не обернуты узлом 'list'.

Каковы возможные решения для преобразования исходного XML в целевой XML?

ОБНОВЛЕНИЕ:

Примечание 1: Любой отдельный узел или группа <item> узлов должны быть обернуты узлом <list>, если он еще не обернут.

Примечание 2: Порядок содержания необходимо поддерживать.

Примечание 3: Если есть <item> узлы до и после <anotherNode>. Это должно преобразовать это:

<list>
    <item label="(1)">some text</item>
    <item label="(2)">
        <item label="a">some text</item>
        <item label="b">some text</item>          
        <anotherNode>some text</anotherNode>
        <item label="c">some text</item>
        <item label="d">some text</item>          
    </item>
</list>

в это:

<list>
    <item label="(1)">some text</item>
    <item label="(2)">
        <list> <!-- opening new wrapper node-->
            <item label="a">some text</item>
            <item label="b">some text</item>          
        </list> <!-- closing new wrapper node-->
        <anotherNode>some text</anotherNode>
        <list> <!-- opening new wrapper node-->
            <item label="c">some text</item>
            <item label="d">some text</item>
        </list> <!-- closing new wrapper node-->
    </item>
</list>

Спасибо

Ответы [ 3 ]

4 голосов
/ 06 сентября 2010

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

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

 <xsl:template match="item/item[1]">
  <list>
   <xsl:apply-templates mode="copy"
    select=".| following-sibling::item"/>
  </list>
 </xsl:template>

 <xsl:template match="item" mode="copy">
  <xsl:call-template name="identity"/>
 </xsl:template>

 <xsl:template match="item/item[not(position()=1)]"/>
</xsl:stylesheet>

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

<list>
    <item label="(1)">some text</item>
    <item label="(2)">
        <anotherNode>some text</anotherNode>
        <item label="a">some text</item>
        <item label="b">some text</item>
    </item>
</list>

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

<list>
   <item label="(1)">some text</item>
   <item label="(2)">
      <anotherNode>some text</anotherNode>
      <list>
         <item label="a">some text</item>
         <item label="b">some text</item>
      </list>
   </item>
</list>

Примечание :

  1. Использование и переопределение правила идентификации .

  2. Подавление определенных элементов.

  3. Обработка некоторых элементов в другом режиме .

Обновление

ОП добавил дополнительные требования:

" Если до anothernode и после него есть item элементов, то каждая такая группа item элементов должна быть заключена в отдельный list"

<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="kfollnonitem" match="item"
  use="generate-id(preceding-sibling::*[not(self::item)][1])"/>

 <xsl:key name="kprecnonitem" match="item"
  use="generate-id(following-sibling::*[not(self::item)][1])"/>

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

 <xsl:template match="*[not(self::list)]/item[1]">
  <list>
   <xsl:apply-templates mode="copy"
    select="key('kprecnonitem',
                 generate-id(following-sibling::*[not(self::item)][1])
                 )"/>
  </list>
 </xsl:template>

 <xsl:template match=
  "*[not(self::list) and item]/*[not(self::item)]">
  <xsl:call-template name="identity"/>

  <list>
    <xsl:apply-templates mode="copy"
     select="key('kfollnonitem', generate-id())"/>
  </list>
 </xsl:template>

 <xsl:template match="item" mode="copy">
  <xsl:call-template name="identity"/>
 </xsl:template>

 <xsl:template match="item/item[not(position()=1)]"/>
</xsl:stylesheet>

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

<list>
    <item label="(1)">some text</item>
    <item label="(2)">
        <item label="a">some text</item>
        <item label="b">some text</item>
        <anotherNode>some text</anotherNode>
        <item label="c">some text</item>
        <item label="d">some text</item>
    </item>
</list>

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

<list>
   <item label="(1)">some text</item>
   <item label="(2)">
      <list>
         <item label="a">some text</item>
         <item label="b">some text</item>
      </list>
      <anotherNode>some text</anotherNode>
      <list>
         <item label="c">some text</item>
         <item label="d">some text</item>
      </list>
   </item>
</list>
3 голосов
/ 06 сентября 2010

Эта таблица стилей:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="@*|node()" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()[1]" />
        </xsl:copy>
        <xsl:apply-templates select="following-sibling::node()[1]" />
    </xsl:template>
    <xsl:template match="*[not(self::list)]
                          /item[not(preceding-sibling::*[1][self::item])]">
        <list>
            <xsl:call-template name="identity"/>
        </list>
        <xsl:apply-templates select="following-sibling::node()
                                      [not(self::item)][1]" />
    </xsl:template>
    <xsl:template match="*[not(self::list)]
                          /item[not(following-sibling::*[1][self::item])]">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()[1]" />
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Выход:

<list>
    <item label="(1)">some text</item>
    <item label="(2)">
        <anotherNode>some text</anotherNode>
        <list>
            <item label="a">some text</item>
            <item label="b">some text</item>
        </list>
    </item>
</list>

Также эта таблица стилей:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="kItemByFirstSibling"
             match="item[preceding-sibling::*[1][self::item]]"
             use="generate-id(preceding-sibling::item
                               [not(preceding-sibling::*[1][self::item])][1])"/>
    <xsl:template match="@*|node()" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" />
        </xsl:copy>
    </xsl:template>
    <xsl:template match="*[not(self::list)]/item"/>
    <xsl:template match="*[not(self::list)]
                          /item[not(preceding-sibling::*[1][self::item])]"
                  priority="1">
        <list>
            <xsl:for-each select=".|key('kItemByFirstSibling',generate-id())">
                <xsl:call-template name="identity"/>
            </xsl:for-each>
        </list>
    </xsl:template>
</xsl:stylesheet>

Примечание : в первой таблице стилей используется наиболее мелкозернистая трансверсаль (она обернет любой узел после первого item). Полное рекурсивное преобразование идентичности второй таблицы стилей.

Редактировать : обращение к новому реквизиту с новым вводом, вывод обеих таблиц стилей:

<list>
    <item label="(1)">some text</item>
    <item label="(2)">
        <list>
            <item label="a">some text</item>
            <item label="b">some text</item>
        </list>
        <anotherNode>some text</anotherNode>
        <list>
            <item label="c">some text</item>
            <item label="d">some text</item>
        </list>
    </item>
</list>
0 голосов
/ 07 сентября 2010

Вы не обращались к этому в первоначальном вопросе, поэтому это может не потребоваться. Но если на входе есть несколько последовательностей <item> элементов, которые необходимо обернуть, которые отделены друг от друга другими элементами, например ::1002

<list>
    <item label="(1)">some text</item>
    <item label="(2)">
        <item label="a">some text</item>
        <item label="b">some text</item>          
        <anotherNode>some text</anotherNode>
        <item label="c">some text</item>
        <item label="d">some text</item>          
    </item>
</list>

более ранние ответы, я полагаю, объединят элементы <item> вместе, изменяя их порядок:

<list>
    <item label="(1)">some text</item>
    <item label="(2)">
        <list> <!-- opening new wrapper node-->
            <item label="a">some text</item>
            <item label="b">some text</item>          
            <item label="c">some text</item>
            <item label="d">some text</item>
        </list> <!-- closing new wrapper node-->
        <anotherNode>some text</anotherNode>
    </item>
</list> 

Вы хотите это, или вы хотите обернуть их отдельно, как это?

<list>
    <item label="(1)">some text</item>
    <item label="(2)">
        <list> <!-- opening new wrapper node-->
            <item label="a">some text</item>
            <item label="b">some text</item>          
        </list> <!-- closing new wrapper node-->
        <anotherNode>some text</anotherNode>
        <list> <!-- opening new wrapper node-->
            <item label="c">some text</item>
            <item label="d">some text</item>
        </list> <!-- closing new wrapper node-->
    </item>
</list> 

Если последнее, вероятно, будет проще всего использовать конструкцию XSLT 2.0 <xsl:for-each-group group-adjacent="name()" />. Я не знаю, есть ли в PHP 5 XSLT 2.0, но если вы можете использовать такую ​​штуку, см. эту хорошую статью .

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