#xml #xslt #saxon
#xml #xslt #saxon
Вопрос:
Учитывая следующий XML:
<root>
<group>
<e1>001</e1>
<e2>beep</e2>
<e2>bop</e2>
<e2>ork</e2>
<e2>ah</e2>
<e2>ah</e2>
</group>
<group>
<e1>002</e1>
<e2>beep</e2>
<e2>bop</e2>
<e2>ork</e2>
<e2>ah</e2>
<e2>ah</e2>
</group>
<group>
<e1>003</e1>
<e2>beep</e2>
<e2>bop</e2>
<e2>ork</e2>
<e2>ah</e2>
<e2>ah</e2>
</group>
<group>
<e1>004</e1>
<e2>beep</e2>
<e2>bop</e2>
<e2>ork</e2>
<e2>ah</e2>
<e2>ah</e2>
</group>
</root>
Обратите внимание, что элементы ‘e2’ в каждом элементе ‘group’ одинаковы, и это гарантируется в исходном документе.
Я пытаюсь использовать XSLT для выполнения следующих шагов:
- сохраните копию набора элементов ‘e2’,
- удалите все элементы ‘group’,
- создайте набор элементов группы по умолчанию со вставленным в него набором e2s
Желаемый результат будет выглядеть следующим образом:
<root>
<group>
<e1>default1</e1>
<e2>beep</e2>
<e2>bop</e2>
<e2>ork</e2>
<e2>ah</e2>
<e2>ah</e2>
</group>
<group>
<e1>default2</e1>
<e2>beep</e2>
<e2>bop</e2>
<e2>ork</e2>
<e2>ah</e2>
<e2>ah</e2>
</group>
</root>
Значения ‘e1’ в исходном документе не имеют значения, а значения ‘e2’ в выходном документе известны заранее и статичны. Динамическими являются только значения ‘e2’, и мне нужно убедиться, что все они есть.
Я уже использовал шаблон, подобный этому, ранее, когда заменял все элементы некоторыми жестко запрограммированными значениями:
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*"/>
</xsl:copy>
</xsl:template>
<!-- Empty Template eliminates all but first 'group' element. -->
<xsl:template match="//group[preceding::group]"></xsl:template>
<xsl:template match="//group">
<xsl:element name="group">
<e1>default1</e1>
<!-- e2 elements inserted here somehow? -->
</xsl:element>
<xsl:element name="group">
<e1>default2</e1>
<!-- e2 elements inserted here somehow? -->
</xsl:element>
</xsl:template>
Я попытался сохранить эти элементы в переменной, но в выходной html ничего не было вставлено:
<xsl:variable name="e2Elements" select="//group[1]/e2"></xsl:variable>
<xsl:template match="//group">
<xsl:element name="group">
<e1>default1</e1>
<xsl:copy-of select="$e2Elements" />
</xsl:element>
</xsl:template>
Но я не уверен, как вставить элементы e2 в значения. Я использую SaxonHE9.8N и имею доступ к пространству имен exslt и xslt2.0
Комментарии:
1. Вы упомянули элементы e3, это опечатка?
2. Да, извините. Редактирование выполнено.
Ответ №1:
Ваше решение фактически создает ненужную копию элементов. Вы можете сделать это, не копируя их, вот так:
<xsl:variable name="e3Elements" select="//group[1]/e2" />
Другая неэффективность заключается в том, что <xsl:template match="group[preceding::group]"/>
использование предыдущей оси всегда дорого, но особенно в шаблоне. Очевидным улучшением является замена его на previous-sibling (поиск по оси previous-sibling выполняется намного быстрее, чем поиск по предыдущей оси). Но на самом деле вы можете сделать лучше: сделайте это правилом по умолчанию для групп ( <xsl:template match="group"/>
), а другое правило — соответствующим только первой группе ( <xsl:template match="group[1]">...
).
Но на самом деле, также нет необходимости сопоставлять первый элемент group, потому что вы не используете какие-либо из его данных.
Со стилистической точки зрения, <group>
предпочтительнее, чем <xsl:element name="group">
просто потому, что это гораздо более читабельно.
Это было бы моим решением на XSLT 3.0:
<xsl:transform version="3.0" .... expand-text="yes">
<xsl:template match="/">
<xsl:variable name="e3Elements" select="//group[1]/e2"/>
<xsl:for-each select="'default1', 'default2'">
<group>
<e1>{.}</e1>
<xsl:copy-of select="$e3Elements"/>
</group>
</xsl:for-each>
</xsl:template>
</xsl:transform>
Комментарии:
1. В качестве примечания я использую xsl:element, потому что это позволило мне указать пространство имен, чтобы сгенерированные элементы не имели атрибутов xmlns: xxx. Фактический код, с которым я работаю, имеет очень большое пространство имен.
Ответ №2:
Оказалось, что мне нужно было сделать мою переменную копией элемента, используя copy-of. Ниже приведено мое решение:
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*"/>
</xsl:copy>
</xsl:template>
<xsl:variable name="e3Elements">
<xsl:copy-of select="//group[1]/e2" />
</xsl:variable>
<xsl:template match="group[preceding::group]"></xsl:template>
<xsl:template match="group">
<xsl:element name="group">
<xsl:element name="e1">default1</xsl:element>
<xsl:copy-of select="$e3Elements"></xsl:copy-of>
</xsl:element>
<xsl:element name="group">
<xsl:element name="e1">default2</xsl:element>
<xsl:copy-of select="$e3Elements"></xsl:copy-of>
</xsl:element>
</xsl:template>
Комментарии:
1. Вам не нужно использовать
xsl:copy-of
в вашей переменной. На самом деле вы можете объявить это как<xsl:variable name="e3Elements" select="//group[1]/e2" />
.