XSLT: Как изменить значение атрибута во время <xsl: copy>?
У меня есть XML-документ, и я хочу изменить значения для одного из атрибутов.
Сначала я скопировал все: от ввода до вывода, используя:
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
И теперь я хочу изменить значение атрибута "type"
в любом элементе с именем "property"
.
Ответы
Ответ 1
Протестировано на простом примере, отлично работает:
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="@type[parent::property]">
<xsl:attribute name="type">
<xsl:value-of select="'your value here'"/>
</xsl:attribute>
</xsl:template>
Отредактировано для включения предложения Tomalak.
Ответ 2
Эта проблема имеет классическое решение: использование и переопределение идентификационного шаблона один из самых фундаментальных и мощных шаблонов проектирования XSLT:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pNewType" select="'myNewType'"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="property/@type">
<xsl:attribute name="type">
<xsl:value-of select="$pNewType"/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
При применении к этому XML-документу:
<t>
<property>value1</property>
<property type="old">value2</property>
</t>
получен желаемый результат:
<t>
<property>value1</property>
<property type="myNewType">value2</property>
</t>
Ответ 3
Два первых ответа не будут работать, если в корневом элементе есть определение xmlns:
<?xml version="1.0"?>
<html xmlns="http://www.w3.org/1999/xhtml">
<property type="old"/>
</html>
Все решения не будут работать для вышеуказанного xml.
Возможное решение:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="node()[local-name()='property']/@*[local-name()='type']">
<xsl:attribute name="{name()}" namespace="{namespace-uri()}">
some new value here
</xsl:attribute>
</xsl:template>
<xsl:template match="@*|node()|comment()|processing-instruction()|text()">
<xsl:copy>
<xsl:apply-templates select="@*|node()|comment()|processing-instruction()|text()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Ответ 4
Вам нужен шаблон, который будет соответствовать вашему целевому атрибуту, и ничего больше.
<xsl:template match='XPath/@myAttr'>
<xsl:attribute name='myAttr'>This is the value</xsl:attribute>
</xsl:template>
Это дополнение к "скопировать все", которое у вас уже есть (и на самом деле всегда присутствует по умолчанию в XSLT). Имея более конкретное соответствие, оно будет использоваться в предпочтении.
Ответ 5
У меня был аналогичный случай, когда я хотел удалить один атрибут из простого node и не мог понять, какая ось позволит мне прочитать имя атрибута. В конце концов, все, что мне нужно было сделать, это использовать
@*[name(.)!='AttributeNameToDelete']
Ответ 6
Для следующего XML:
<?xml version="1.0" encoding="utf-8"?>
<root>
<property type="foo"/>
<node id="1"/>
<property type="bar">
<sub-property/>
</property>
</root>
Мне удалось заставить его работать со следующим XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//property">
<xsl:copy>
<xsl:attribute name="type">
<xsl:value-of select="@type"/>
<xsl:text>-added</xsl:text>
</xsl:attribute>
<xsl:copy-of select="child::*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Ответ 7
Если ваш исходный XML-документ имеет собственное пространство имен, вам нужно объявить пространство имен в таблице стилей, назначить ему префикс и использовать этот префикс при обращении к элементам исходного XML-кода: например,
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
<xsl:output method="xml" encoding="utf-8" indent="yes" omit-xml-declaration="yes" />
<!-- identity transform -->
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<!-- exception-->
<xsl:template match="xhtml:property/@type">
<xsl:attribute name="type">
<xsl:text>some new value</xsl:text>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
Или, если вы предпочитаете:
...
<!-- exception-->
<xsl:template match="@type[parent::xhtml:property]">
<xsl:attribute name="type">
<xsl:text>some new value</xsl:text>
</xsl:attribute>
</xsl:template>
...
ДОПОЛНЕНИЕ:
В очень маловероятном случае, когда пространство имен XML не известно заранее, вы можете сделать следующее:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" indent="yes" omit-xml-declaration="yes" />
<!-- identity transform -->
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<!-- exception -->
<xsl:template match="*[local-name()='property']/@type">
<xsl:attribute name="type">
<xsl:text>some new value</xsl:text>
</xsl:attribute>
</xsl:template>
Конечно, очень сложно представить сценарий, в котором вы заранее знаете, что исходный XML-документ содержит элемент с именем "свойство" с атрибутом с именем "тип", который нуждается в замене - но все еще не знает пространства имен документ. Я добавил это в основном, чтобы показать, как ваше собственное решение может быть упрощено.
Ответ 8
Я также столкнулся с той же проблемой, и я решил ее следующим образом:
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!-- copy property element while only changing its type attribute -->
<xsl:template match="property">
<xsl:copy>
<xsl:attribute name="type">
<xsl:value-of select="'your value here'"/>
</xsl:attribute>
<xsl:apply-templates select="@*[not(local-name()='type')]|node()"/>
</xsl:copy>
</xsl:template>