разделение и выравнивание узлов с помощью XSLT
У меня не может быть вложенных областей, поэтому мне нужно сгладить их и объединить их атрибуты класса, чтобы я мог отслеживать, какие классы являются родителями.
Вот упрощенный вход :
<body>
<h1 class="section">Title</h1>
<p class="main">
ZZZ
<span class="a">
AAA
<span class="b">
BBB
<span class="c">
CCC
<preserveMe>
eeee
</preserveMe>
</span>
bbb
<preserveMe>
eeee
</preserveMe>
</span>
aaa
</span>
</p>
</body>
Вот желаемый результат
<body>
<h1 class="section">Title</h1>
<p class="main">
ZZZ
<span class="a">
AAA
</span>
<span class="ab">
BBB
</span>
<span class="abc">
CCC
<preserveMe>
eeee
</preserveMe>
</span>
<span class="ab">
bbb
<preserveMe>
eeee
</preserveMe>
</span>
<span class="a">
aaa
</span>
</p>
</body>
Вот самое близкое, к чему я пришел (я действительно Новичок в этом, так что даже на то, чтобы добраться до этого места, у меня ушло много времени...)
<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="/">
<p>
<xsl:apply-templates/>
</p>
</xsl:template>
<xsl:template match="*/span">
<span class='{concat(../../@class,../@class,@class)}'>
<xsl:value-of select='.'/>
</span>
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
Вы можете увидеть результат моей неудачной попытки и насколько он далек от того, что я действительно хотел, если вы запустите его себе. В идеале я хотел бы получить решение, которое принимает произвольное число вложенных уровней и может также обрабатывать прерывистые гнезда (span, span, notSpan, span...).
Edit: я добавил теги внутри вложенной структуры по запросу комментаторов ниже. Кроме того, я с помощью XSLT В1.0, но я мог бы использовать другие версии, если это необходимо, я полагаю.
Edit 2: я понял, что мой пример был чрезмерно упрощен по сравнению с тем, что мне на самом деле нужно преобразовать. А именно, я не могу потерять классы из других тегов; только пролеты можно комбинировать.
4 ответа:
Как я уже упоминал во вступительных комментариях, это далеко не тривиально. Вот еще один подход, который вы можете рассмотреть:
XSLT 1.0
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> <xsl:strip-space elements="*"/> <!-- identity transform --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <xsl:template match="p"> <xsl:copy> <xsl:apply-templates select="@*|node()|.//span/text()"/> </xsl:copy> </xsl:template> <xsl:template match="span/text()"> <span> <xsl:attribute name="class"> <xsl:for-each select="ancestor::span"> <xsl:value-of select="@class"/> </xsl:for-each> </xsl:attribute> <xsl:apply-templates select="preceding-sibling::*"/> <xsl:value-of select="." /> <xsl:if test="not(following-sibling::text())"> <xsl:apply-templates select="following-sibling::*"/> </xsl:if> </span> </xsl:template> <xsl:template match="span"/> </xsl:stylesheet>
Это в значительной степени похоже на то, что было предложено ранее Лингамурти CS-но Вы увидите разницу со следующим тестовым входом:
XML
<body> <h1 class="section">Title</h1> <p class="main"> ZZZ <preserveMe>0</preserveMe> <span class="a"> AAA <span class="b"> BBB <span class="c"> CCC <preserveMe>c</preserveMe> </span> bbb <preserveMe>b</preserveMe> </span> aaa </span> <preserveMe>1</preserveMe> </p> </body>
Я надеюсь, что следующая таблица стилей поможет:
<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="*"/> <!-- Identity transform template --> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <xsl:template match="p"> <xsl:copy> <xsl:apply-templates select="@* | text() | .//text()[parent::span]"/> </xsl:copy> </xsl:template> <xsl:template match="text()[parent::span]"> <span> <xsl:attribute name="class"> <xsl:call-template name="class-value"/> </xsl:attribute> <xsl:value-of select="."/> <xsl:apply-templates select="following-sibling::node()[1][not(self::text()) and not(self::span)]"/> </span> </xsl:template> <xsl:template name="class-value"> <xsl:for-each select="ancestor::span/@class"> <xsl:value-of select="."/> </xsl:for-each> </xsl:template> </xsl:stylesheet>
Вот ты где .. Я сделал это рекурсивно вложенным шаблоном span, который принимает два парама, первый-это текущий класс span для объединения классов и текущий узел span. Затем обработайте вложенные промежутки.
Поэтому я просто называю шаблон для корневых промежутков в нашем случае span под тегом p.
<xsl:template match="/"> <hmtl> <body> <p> <xsl:for-each select='.//p/span'> <xsl:call-template name='nested-span'> <xsl:with-param name='cclass' select='./@class'></xsl:with-param> <xsl:with-param name='cspan' select='.'></xsl:with-param> </xsl:call-template> </xsl:for-each> </p> </body> </hmtl> </xsl:template> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <xsl:template name="nested-span"> <xsl:param name='cclass'/> <xsl:param name='cspan' as='node()' /> <span> <xsl:attribute name='class' select='$cclass'/> <xsl:value-of select='$cspan/text()[1]' /> <xsl:if test="not(exists(./span))"> <xsl:if test='string-length($cspan/text()[2]) > 0 '> <xsl:value-of select='$cspan/text()[2]' /> </xsl:if> <xsl:apply-templates select="./*[local-name() != 'span']"/> </xsl:if> </span> <xsl:for-each select='$cspan/span'> <xsl:call-template name='nested-span'> <xsl:with-param name='cclass' select='concat($cclass, ./@class)' ></xsl:with-param> <xsl:with-param name='cspan' select='.'></xsl:with-param> </xsl:call-template> </xsl:for-each> <xsl:if test="exists(./span)"> <span> <xsl:attribute name='class' select='$cclass'/> <xsl:if test='string-length($cspan/text()[2]) > 0 '> <xsl:value-of select='$cspan/text()[2]' /> </xsl:if> <xsl:apply-templates select="./*[local-name() != 'span']"/> </span> </xsl:if> </xsl:template>
И вот результат
<hmtl> <body> <p> <span class="a"> AAA </span> <span class="ab"> BBB </span> <span class="abc"> CCC <preserveMe> eeee </preserveMe> </span> <span class="ab"> bbb <preserveMe> eeee </preserveMe> </span> <span class="a"> aaa </span> </p> </body> </hmtl>
Я надеюсь, что это может помочь
Как вы сказали, XSLT 2.0 может быть жизнеспособным вариантом, я попробовал подход, основанный на группировке узлов:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> <xsl:strip-space elements="*"/> <xsl:template match="* | @* | text()"> <xsl:copy> <xsl:apply-templates select="* | @* | text()"/> </xsl:copy> </xsl:template> <xsl:template match="span"> <xsl:for-each-group select="* | text()" group-adjacent="name() = 'span'"> <xsl:choose> <xsl:when test="current-group()/self::span"> <!-- a group of span elements: nothing to do yet --> <xsl:apply-templates select="current-group()"/> </xsl:when> <xsl:otherwise> <!-- a group of text nodes and no-span elements: create span --> <span class="{string-join((ancestor::span/@class), '')}"> <xsl:apply-templates select="current-group()"/> </span> </xsl:otherwise> </xsl:choose> </xsl:for-each-group> </xsl:template> </xsl:stylesheet>
Основные пункты:
- дочерние элементы элемента
span
, как текстовые узлы, так и другие элементы, группируются в зависимости от того, являются ли ониspan
s или нет- выдает тот же результат решения Майкла