XSLT: Как изменить значение атрибута во время <xsl: copy>? - PullRequest
56 голосов
/ 05 марта 2009

У меня есть документ XML, и я хочу изменить значения для одного из атрибутов.

Сначала я скопировал все с ввода на вывод, используя:

<xsl:template match="@*|node()">
  <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>

А теперь я хочу изменить значение атрибута "type" в любом элементе с именем "property".

Ответы [ 8 ]

61 голосов
/ 06 марта 2009

Эта проблема имеет классическое решение : Использование и переопределение шаблона идентификации является одним из наиболее фундаментальных и мощных шаблонов проектирования 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>
36 голосов
/ 05 марта 2009

Проверено на простом примере, отлично работает:

<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>

Отредактировано, чтобы включить предложение Томалака.

7 голосов
/ 13 декабря 2013

Первые два ответа не будут работать, если в корневом элементе есть определение 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 голосов
/ 05 марта 2009

Вам нужен шаблон, который будет соответствовать вашему целевому атрибуту, и ничего больше.

<xsl:template match='XPath/@myAttr'>
  <xsl:attribute name='myAttr'>This is the value</xsl:attribute>
</xsl:template>

Это дополнение к «копировать все», которое у вас уже есть (и фактически всегда присутствует по умолчанию в XSLT). При более конкретном совпадении оно будет использоваться в предпочтении.

2 голосов
/ 07 сентября 2009

У меня был похожий случай, когда я хотел удалить один атрибут из простого узла и не мог понять, какая ось позволит мне прочитать имя атрибута. В конце концов, все, что мне нужно было сделать, это использовать

@*[name(.)!='AttributeNameToDelete']

1 голос
/ 16 декабря 2013

Если ваш исходный 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-документ содержит элемент с именем «property», с атрибутом с именем «type», который нуждается в замене, но все еще не знает пространство имен документ. Я добавил это в основном, чтобы показать, как можно упростить собственное решение.

1 голос
/ 05 марта 2009

Для следующего 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>
0 голосов
/ 09 декабря 2016

Я также столкнулся с той же проблемой, и я решил ее следующим образом:

<!-- 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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...