为什么用元素替换一个属性是有效的,而两个属性不是?

时间:2014-07-23 00:28:45

标签: xslt-1.0

identity transform中,我们可以通过

删除属性
<xsl:template match="@myAttrib"/>

这适用于任何输入...我们可以&#34;替换&#34;带有

的元素的属性
<xsl:template match="@myAttrib"><b>my new element</b></xsl:template>

...但仅当输入只有一个属性时才有效。

另一方面,如果我需要替换属性的值,xsl:template行为是相同的,即

<xsl:template match="@myAttrib">newValue</xsl:template>

不替换值,但删除属性并包含&#34; newValue&#34;作为textNode。

  • 为什么&#34;取代价值&#34;无效?
  • 为什么&#34;替换为元素&#34;不是错误吗?
  • 为什么&#34;替换为元素&#34;在&#34;两个属性(每个元素)上下文&#34;是错误吗?

编辑(解释&#34;两个属性&#34;),假设输入

<root>
    <parent myAttrib1="1" myAttrib2="2">
         <child myAttrib="1" myAttrib3="1"/>
    </parent>
    <sibling myAttrib0="1"/>
</root>

只有兄弟元素才有一个属性。

3 个答案:

答案 0 :(得分:1)

很难回答你的问题,因为你的一些假设是错误的。例如:

<xsl:template match="@myAttrib"><b>my new element</b></xsl:template>

适用于任意数量的元素。将(与身份转换模板一起)应用于以下输入时:

<root>
    <parent myAttrib="1">
         <child myAttrib="1"/>
    </parent>
    <sibling myAttrib="1"/>
</root>

结果将是:

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <parent>
      <b>my new element</b>
      <child>
         <b>my new element</b>
      </child>
   </parent>
   <sibling>
      <b>my new element</b>
   </sibling>
</root>

很明显,你的断言“仅当输入只有一个元素时才起作用”是不正确的。

关于:

<xsl:template match="@myAttrib">newValue</xsl:template>

这不会取代myAttrib,因为模板与属性匹配 - 而不是其值(作为旁边:属性的值是不是节点,不能匹配)。就像以前一样,属性匹配,另一个节点输出到位;首先它是一个元素,现在它是一个文本节点。这是唯一的区别。


编辑:

  

“替换过程”是每个元素节点的一个属性节点。

不,那也不是真的。例如,考虑以下输入:

<root>
    <parent red="1" green="2">
         <child red="1" blue="1"/>
    </parent>
    <sibling green="1" blue="1"/>
</root>

以及以下模板:

<xsl:template match="@red | @blue">
    <new/>
</xsl:template>

或:

<xsl:template match="@*[contains(name(), 'r')]">
    <new/>
</xsl:template>

-
顺便说一句,这些例子都不适用于撒克逊人 - 但这是另一个故事。

答案 1 :(得分:1)

  • 为什么&#34;取代价值&#34;无效?

XSLT模板替换了另一件事。在这种情况下,您将使用文本节点替换属性。如果要将属性替换为具有相同名称但内容不同的其他属性,则可以执行以下操作:

<xsl:template match="@myAttrib">
    <xsl:attribute name="{name()}">newValue</xsl:attribute>
</xsl:template>
  • 为什么&#34;替换为元素&#34;不是错误吗?

见上文。 XSLT模板将一件事替换为另一件事。

  • 为什么&#34;替换为元素&#34;在&#34;两个属性(每个元素)上下文&#34;是错误吗?

这可能会在某些情况下导致错误。它不会自动出错。

在添加了其他节点类型之后,XSLT不允许将属性添加到输出流中的父元素。据推测,您的案例中发生的事情是:

  1. 您正在用myAttrib1替换元素。
  2. 您有一个身份模板,将myAttrib2复制为新属性。
  3. 如果在myAttrib2之后处理myAttrib1,则会发生错误。 (不保证处理属性的顺序。)

    这可能很难解决,但这种方法在某些情况下会起作用:

    <xsl:template match="@*[../@myAttrib]" />
    <xsl:template match="@myAttrib">
        <xsl:copy-of select="../@*[(. | current())[2]]" />
        <b>my new element</b>
    </xsl:template>
    

答案 2 :(得分:0)

从迈克尔的回答(@ michael.hor257k)和我在那里的评论中将我理解的内容翻译给另一位读者;谢谢迈克尔!


假设您有一个输入XML,

<root>
    <parent A="1" B="2"> 
         <child C="1" D="1" E="0"/>
    </parent>
    <sibling E="1">text1</sibling>
</root>

这是内部DOM表示的图表,即树:

                       root
                      /    \ 
                parent      sibling
              /      \        \    \
           (A,B)    child     (E)   [text1]
                        \
                       (C,D,E)

元素root是节点,元素parent是节点,属性@A是节点等。文本也是节点...但不是全部tree-itens是节点:图中的一些itens是括号,因为它们是属性节点的集合。

在图中,集合是树项,属性不是。我们可以想象删除或替换树的项目的过程。


“删除节点”任务适用于其XPath指向的任何单个节点。

我们可以想象“删除项目”任务(参见图表),并通过XPath指向项目。

要删除集合项,XPath必须指向集合的所有节点,因此parent/@*会使项目有效,但parent/@A不会(因为域parent/@B)。 XPath sibling/@E指向集合,因为sibling元素只有一个属性。 XPath @E指向两个节点,一个表征集合,另一个不表示。

任务“按文字替换项目X”或“按元素替换项目X”需要指向项目X的XPath。只能替换树形图标。要替换集合项,XPath必须指向集合的所有节点。


总结:属性集合是项目,而不是属性节点;这就是要点(!),并且出现混乱。

在DOM表示中,我们可以访问 nodeValue属性,对于元素和属性,我们可以在两种情况下都改变它:这是另一个混​​乱的来源,因为这个“改变<的概念em> nodeValue属性“在XSLT中不存在。


所以,

为什么“替换值”无效?

XPath sibling/@E指向节点属性E。我们需要类似sibling/@E/nodeValue()之类的东西来指出值并替换它,但这种XPath不存在。

(编辑)重要提示:如xsl:attribute所示,使用{{1}},请参阅本页中@ JLRishe的回答。

为什么“替换为元素”不是错误?

概念是“用其他项目替换项目”。当我们看到“项目树”的图表时有意义。

想象“按元素替换节点”是错误的,因为想象“节点树”是错误的,而通用XPath节点可以是具有多个成员的集合的属性。

为什么在“两个属性(每个元素)上下文”中“替换为元素”是一个错误?

因为具有多个属性的集合的单个属性的XPath,不代表集合。 XPath必须指向集合的所有属性,以便在替换过程中使用。