【问题标题】:What XSLT copies an XML file, deleting one attribute and modifying the value of another?什么 XSLT 复制 XML 文件,删除一个属性并修改另一个属性的值?
【发布时间】:2021-03-26 19:52:20
【问题描述】:

什么 XSLT 会复制我的 XML 文档,通过更改一个属性值并删除另一个属性来修改特定路径的元素,并克服我遇到的错误消息?

我的 XSLT:

<?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" indent="no" encoding="UTF-8" />

    <xsl:template match="node()|@*">
      <xsl:copy>
        <xsl:apply-templates select="node()|@*"/>
      </xsl:copy>
    </xsl:template>
    
    <xsl:template match="/document/body/table/cell[@a2='delete me'][@a3='change me']" >
        <!-- Not trying to change @a3 yet, just delete @a2. -->
        <xsl:copy-of select="@*[name(.)!='a2']|node()" />
    </xsl:template>

</xsl:stylesheet>

我的输入 XML 文档(从真实的 LibreOffice 文档示例简化):

<?xml version="1.0" encoding="UTF-8"?>
<document>
  <body>
    <otherstuff/>
    <table>
      <cell id="1" a1="keep me" a2="delete me" a3="change me">
        cell contents #1
      </cell>
      <cell id="2" a1="keep me too">
        cell contents #2
      </cell>
      <not_cell id="3" a1="keep me" a2="delete me" a3="change me">
        not_cell contents #3
      </not_cell>
    </table>
    <otherstuff/>
  </body>
</document>

我要更改的是具有a2="delete me"a3="change me" 属性的cell 元素应该更改。我想删除属性a2,并将a3 的值更改为a3="changed"cell 元素的所有其余部分均未更改。 XML 文档的所有其他部分均未更改。

预期的输出文档:与输入相同,除了&lt;cell id='1' …/&gt;

<cell id="1" a1="keep me" a3="changed">

注意&lt;not_cell id="3" a2="delete me" …/&gt; 没有被触及,因为它不是cell 元素。

当我将这个 XSLT 应用到这个文档时,我得到了什么:

  % xsltproc Error-Cannot-add-an-attribute-node.xslt Doc.xml 
  runtime error: file Error-Cannot-add-an-attribute-node.xslt line 49 element copy-of
  Cannot add an attribute node to a non-element node.

这似乎是说copy-of 表达式正在尝试将属性应用于属性或元素文本。我不能很好地跟踪 XSLT 的执行,以了解它到底哪里出错了。你能解释一下吗?

进行这种转换的正确 XSLT 模式(最好是 XSLT-1.0)是什么?

(可能是答案,XSLT: How to change an attribute value during xsl:copy?,将引导我找到正确的 XSLT 模式,但我看不出来。而且,它没有说明为什么我正在尝试的 XSLT 会导致该错误消息。)

【问题讨论】:

    标签: xml xslt xslt-1.0


    【解决方案1】:

    如果要给元素添加属性,必须先创建元素——例如:

    <xsl:template match="cell">
        <xsl:copy>
            <xsl:copy-of select="@*[name(.)!='a2'] | node()" />
        </xsl:copy>
    </xsl:template>
    

    但是,如果您只想删除(或修改)一个属性,您可以简单地匹配该属性本身:

    <xsl:template match="@a2[parent::cell]"/>
    

    【讨论】:

    • 为了澄清,第二个示例通过在其主体中没有任何内容(尤其是没有xsl:copyxsl:apply-templates)来删除属性?也就是说,它通过匹配属性来删除然后什么都不做?
    • 是的,没错。如果你想修改属性,你需要匹配它,(重新)创建它并用新的内容填充它。
    【解决方案2】:

    使用模式更改元素中的多个内容。

      <xsl:template match="cell">
        <xsl:copy>
          <!-- Use a mode to change multiple things within an element.  -->
          <xsl:apply-templates select="node()|@*" mode="cell"/>
        </xsl:copy>
      </xsl:template>
    
      <!--  *** Begin cell mode *** -->
    
      <!--  You need a mode identity.  -->
      <xsl:template match="node()|@*" mode="cell">
        <xsl:copy>
          <xsl:apply-templates select="node()|@*" mode="cell"/>
        </xsl:copy>
      </xsl:template>
    
      <!-- use mode to delete me -->
      <xsl:template match="@a2" mode="cell"/>
    
      <!-- use mode to change me -->
      <xsl:template match="@a3" mode="cell">
        <xsl:attribute name="a3">
          <xsl:value-of select="'something new'"/>
        </xsl:attribute>    
      </xsl:template>
    
      <!-- Don't do anything for keep me.  It's handled my the mode identity. -->
    
      <!-- *** End cell mode *** -->
    
     <xsl:template match="node()|@*">
        <xsl:copy>
          <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
      </xsl:template>
    

    【讨论】:

    • 注意:您可以使用这样的模式更改文本节点,但前提是文本节点存在。可以肯定的是,在单元格模板中的 apply-templates 语句之后,测试文本节点。如果不存在,则在此处添加文本。
    • 谢谢!清晰的解释,以及易于修改和重用的示例 XSLT 工作表。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-29
    • 2015-09-20
    • 1970-01-01
    • 1970-01-01
    • 2016-10-11
    • 1970-01-01
    相关资源
    最近更新 更多