Im работает с 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>
Благодаря,
Это преобразование :
<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>
Обратите внимание :
Использование и переопределение правила Identity .
Подавление определенных элементов.
Обработка некоторых элементов с использованием другого режима .
Обновление :
ОП добавил дополнительные требования:
« В случае наличия item
элемента перед anothernode
и после него каждая такая группа 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>
Эта таблица стилей:
<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>
Вы не обращались к этому в исходном вопросе, поэтому это может не потребоваться. Но если вход имеет несколько последовательностей элементов <item>
которые необходимо обернуть, которые отделены друг от друга другими элементами-братьями, например:
<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, но если вы можете использовать такую вещь, см. Эту хорошую статью .