#xml #xslt
#xml #xslt
Вопрос:
Мне нужно выполнить цикл XML-документа. Никаких проблем нет.
Проблема возникает, когда мне нужен текст из предыдущей строки, которую я только что пропустил.
XML будет выглядеть примерно так
<lines>
<line>
<id>1</id>
<text>Some fancy text here 1</text>
</line>
<line>
<id></id>
<text>This I need in the next line with a ID</text>
</line>
<line>
<id></id>
<text>Also need this.</text>
</line>
<line>
<id>4</id>
<text>Here we go</text>
</line>
</lines>
Выходной XML-файл должен выглядеть следующим образом
<output>
<line>
<id>1</id>
<note>Some fancy text here 1</note>
</line>
<line>
<id>4</id>
<note>Here we go</note>
<extra>
<note>This I need in the next line with a ID</note>
<note>Also need this.</note>
</extra>
</line>
</output>
XSL, который у меня есть, позволяет просто сортировать строки, для которых не установлен идентификатор.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<output>
<xsl:for-each select="lines/line">
<xsl:if test="id/text() amp;> 0">
<line>
<id>
<xsl:value-of select="id" />
</id>
<note>
<xsl:value-of select="text" />
</note>
</line>
</xsl:if>
</xsl:for-each>
</output>
</xsl:template>
</xsl:stylesheet>
Комментарии:
1.
xsl:for-each
Это не цикл. Это назначение функции для каждого соответствия вашегоselect
.
Ответ №1:
Вам нужно выбрать предыдущий-sibling; здесь есть пример.
В основном синтаксис xpath для вас был бы чем-то вроде (не тестировался):
preceding-sibling::NodeName[1]
Комментарии:
1. Этот xpath собирается получить первый prceding-sibling (чего??). Кажется, что для OP нужны все предшествующие смежные братья и сестры. Не совсем ясно также, как применить это к текущему варианту использования.
Ответ №2:
Я не удержался от того, чтобы полностью просмотреть ваш код 🙂
Далее следует полное решение XSLT 1.0, более функционально ориентированное (без процедурного подхода). На первый взгляд может показаться, что это сложнее увидеть, но, имхо, это очень хороший пример для начала работы с механизмом создания шаблонов XSLT.
Также использовать xsl:for-each
в вашем конкретном случае не так просто, потому что на определенном шаге цикла вы хотите получить все предыдущие смежные дочерние элементы с пустым значением id
, не зная, сколько их априори.
Я также использовал шаблон идентификации, чтобы упростить работу по воссозданию вашей цели.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity template -->
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<!-- match line with empty id and do not output -->
<xsl:template match="line[not(boolean(id/text()))]"/>
<!-- match line with id and build output -->
<xsl:template match="line[boolean(id/text())]">
<xsl:copy>
<xsl:copy-of select="id"/>
<xsl:apply-templates select="text"/>
<extra>
<!-- apply recursive template to the first preceding
sibling adajacent node with empty id -->
<xsl:apply-templates select="(preceding-sibling::*[1])
[name()='line' and not(boolean(id/text()))]/text"
mode="extra"/>
</extra>
</xsl:copy>
</xsl:template>
<!-- change text element to note -->
<xsl:template match="text">
<note>
<xsl:value-of select="."/>
</note>
</xsl:template>
<!-- recursive template for extra note elements -->
<xsl:template match="text" mode="extra">
<note>
<xsl:value-of select="."/>
</note>
<xsl:apply-templates select="(parent::line/preceding-sibling::*[1])
[name()='line' and not(boolean(id/text()))]/text"
mode="extra"/>
</xsl:template>
</xsl:stylesheet>
Примененный к вашему вводу, выдает:
<?xml version="1.0" encoding="UTF-8"?>
<lines>
<line>
<id>1</id>
<note>Some fancy text here 1</note>
<extra/>
</line>
<line>
<id>4</id>
<note>Here we go</note>
<extra>
<note>Also need this.</note>
<note>This I need in the next line with a ID</note>
</extra>
</line>
</lines>
Комментарии:
1. Это отлично работает. Возможно, я немного поторопился с первым примером, поскольку после id 4 у меня могут появиться еще несколько. Может быть несколько строк с номером после каждой другой, и всем этим нужно добавить те же две строки в дополнительное поле. Я буду с этим бороться. Еще раз спасибо