xmlstarlet edit/update 修改元素标签

xmlstarlet edit/update modifies element tags

我正在使用 xmlstarlet 对 xml 文件进行一些修改(我们称之为 test.xml),但我 运行 遇到了以下问题我的更新声明(注意:我也是 xmlstarlet 的新手!)。

这是我正在使用的 xml 的示例:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<LIST>
    <STUFF>
        <xSTUFF>
            <ITEM>
                <ITEM_DATA>
                    <ITEM_ATTRIBUTE>Item_ID</ITEM_ATTRIBUTE>
                    <ATTRIBUTE_DATA>X-123</ATTRIBUTE_DATA>
                </ITEM_DATA>
                <ITEM_DATA>
                    <ITEM_ATTRIBUTE>Color</ITEM_ATTRIBUTE>
                    <ATTRIBUTE_DATA>Purple</ATTRIBUTE_DATA>
                </ITEM_DATA>
                <ITEM_DATA>
                    <VULN_ATTRIBUTE>Weight</ITEM_ATTRIBUTE>
                    <ATTRIBUTE_DATA>5</ATTRIBUTE_DATA>
                </ITEM_DATA>
                <INSTOCK>No</INSTOCK>
                <LOCATION></LOCATION>
                <PRICE></PRICE>
                <ONSALE></ONSALE>
                <DISCOUNT></DISCOUNT>
            </ITEM>
            <ITEM>
                <ITEM_DATA>
                    <ITEM_ATTRIBUTE>Item_ID</ITEM_ATTRIBUTE>
                    <ATTRIBUTE_DATA>X-124</ATTRIBUTE_DATA>
                </ITEM_DATA>
                <ITEM_DATA>
                    <ITEM_ATTRIBUTE>Color</ITEM_ATTRIBUTE>
                    <ATTRIBUTE_DATA>Red</ATTRIBUTE_DATA>
                </ITEM_DATA>
                <ITEM_DATA>
                    <VULN_ATTRIBUTE>Weight</ITEM_ATTRIBUTE>
                    <ATTRIBUTE_DATA>3</ATTRIBUTE_DATA>
                </ITEM_DATA>
                <INSTOCK>Yes</INSTOCK>
                <LOCATION>IsleA</LOCATION>
                <PRICE>2.99</PRICE>
                <ONSALE>No</ONSALE>
                <DISCOUNT>No</DISCOUNT>
            </ITEM>
        </xSTUFF>
    </STUFF>
</LIST>

有多个项目,每个项目都有唯一的项目 ID。我正在尝试更新给定商品 ID 的 INSTOCK、LOCATION、PRICE,有时还更新 ONSALE 和 DISCOUNT 字段。以其中一个为例,我正在尝试以下操作:

xmlstarlet ed --inplace -u "//LIST/STUFF/xSTUFF/ITEM/ITEM_DATA[ATTRIBUTE_DATA='X-123']/../INSTOCK" -v Yes test.xml

这似乎可行,但出于某种原因,在匹配项元素内去除了下方所有内容的前导元素标签,因此我的输出文件最终看起来像这样(注意缺少的 LOCATION、PRICE、ONSALE 和 DISCOUNT 前导标签):

*** 编辑:标签实际上被重新格式化为自动关闭标签,下面更新了结果。谢谢 Daniel Haley。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<LIST>
    <STUFF>
        <xSTUFF>
            <ITEM>
                <ITEM_DATA>
                    <ITEM_ATTRIBUTE>Item_ID</ITEM_ATTRIBUTE>
                    <ATTRIBUTE_DATA>X-123</ATTRIBUTE_DATA>
                </ITEM_DATA>
                <ITEM_DATA>
                    <ITEM_ATTRIBUTE>Color</ITEM_ATTRIBUTE>
                    <ATTRIBUTE_DATA>Purple</ATTRIBUTE_DATA>
                </ITEM_DATA>
                <ITEM_DATA>
                    <VULN_ATTRIBUTE>Weight</ITEM_ATTRIBUTE>
                    <ATTRIBUTE_DATA>5</ATTRIBUTE_DATA>
                </ITEM_DATA>
                <INSTOCK>Yes</INSTOCK>
                <LOCATION/>
                <PRICE/>
                <ONSALE/>
                <DISCOUNT/>
            </ITEM>
            <ITEM>
                <ITEM_DATA>
                    <ITEM_ATTRIBUTE>Item_ID</ITEM_ATTRIBUTE>
                    <ATTRIBUTE_DATA>X-124</ATTRIBUTE_DATA>
                </ITEM_DATA>
                <ITEM_DATA>
                    <ITEM_ATTRIBUTE>Color</ITEM_ATTRIBUTE>
                    <ATTRIBUTE_DATA>Red</ATTRIBUTE_DATA>
                </ITEM_DATA>
                <ITEM_DATA>
                    <VULN_ATTRIBUTE>Weight</ITEM_ATTRIBUTE>
                    <ATTRIBUTE_DATA>3</ATTRIBUTE_DATA>
                </ITEM_DATA>
                <INSTOCK>Yes</INSTOCK>
                <LOCATION>IsleA</LOCATION>
                <PRICE>2.99</PRICE>
                <ONSALE>No</ONSALE>
                <DISCOUNT>No</DISCOUNT>
            </ITEM>
        </xSTUFF>
    </STUFF>
</LIST>

我猜我遗漏了一些简单的东西,因为我对 xmlstarlet 完全陌生,所以非常感谢任何帮助!

为了防止空元素被自闭,您可以将 XSLT 与 tr xmlstarlet 命令一起使用,并将输出方法设置为 HTML

XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes" method="html"/>
  <xsl:strip-space elements="*"/>

  <xsl:param name="id"/>
  <xsl:param name="newValue"/>

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

  <xsl:template match="INSTOCK">
    <xsl:choose>
      <xsl:when test="../ITEM_DATA/ATTRIBUTE_DATA=$id">
        <xsl:copy>
          <xsl:value-of select="$newValue"/>
        </xsl:copy>
      </xsl:when>
      <xsl:otherwise>
        <xsl:call-template name="ident"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

命令行

xml tr test.xsl -s id="X-123" -s newValue="Yes" input.xml

输出

<LIST><STUFF><xSTUFF><ITEM><ITEM_DATA><ITEM_ATTRIBUTE>Item_ID</ITEM_ATTRIBUTE><ATTRIBUTE_DATA>X-123</ATTRIBUTE_DATA></ITEM_DATA><ITEM_DATA><ITEM_ATTRIBUTE>Color</ITEM_ATTRIBUTE><ATTRIBUTE_DATA>Purple</ATTRIBUTE_DATA></ITEM_DATA><ITEM_DATA><ITEM_ATTRIBUTE>Weight</ITEM_ATTRIBUTE><ATTRIBUTE_DATA>5</ATTRIBUTE_DATA></ITEM_DATA><INSTOCK>Yes</INSTOCK><LOCATION></LOCATION><PRICE></PRICE><ONSALE></ONSALE><DISCOUNT></DISCOUNT></ITEM><ITEM><ITEM_DATA><ITEM_ATTRIBUTE>Item_ID</ITEM_ATTRIBUTE><ATTRIBUTE_DATA>X-124</ATTRIBUTE_DATA></ITEM_DATA><ITEM_DATA><ITEM_ATTRIBUTE>Color</ITEM_ATTRIBUTE><ATTRIBUTE_DATA>Red</ATTRIBUTE_DATA></ITEM_DATA><ITEM_DATA><ITEM_ATTRIBUTE>Weight</ITEM_ATTRIBUTE><ATTRIBUTE_DATA>3</ATTRIBUTE_DATA></ITEM_DATA><INSTOCK>Yes</INSTOCK><LOCATION>IsleA</LOCATION><PRICE>2.99</PRICE><ONSALE>No</ONSALE><DISCOUNT>No</DISCOUNT></ITEM></xSTUFF></STUFF></LIST>

请注意,您没有得到 XML 声明 (<?xml ...?>),即使我在 xsl:output 上设置了 indent="yes",XML最终都在一条线上。

XML 仍然是格式正确的,因为 XML 声明在 XML 1.0 实例上不是必需的。

另一种选择是使用具有不同处理器的 XSLT 2.0/3.0。这样你就可以使用输出方法 xhtml.

这是一个在命令行中使用 XSLT 3.0 和 Saxon-HE 9.8(free/open 源代码)的 Java 版本的示例...

XSLT 3.0

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xhtml" standalone="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:param name="id" required="yes"/>
  <xsl:param name="newValue" required="yes"/>

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:template match="ITEM[ITEM_DATA/ATTRIBUTE_DATA=$id]/INSTOCK">
    <xsl:copy>
      <xsl:value-of select="$newValue"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

命令行

java -cp "C:/apps/saxon/saxon9he.jar" net.sf.saxon.Transform -s:"input.xml" -xsl:"test.xsl" id="X-123" newValue="Yes"

输出

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><LIST>
   <STUFF>
      <xSTUFF>
         <ITEM>
            <ITEM_DATA>
               <ITEM_ATTRIBUTE>Item_ID</ITEM_ATTRIBUTE>
               <ATTRIBUTE_DATA>X-123</ATTRIBUTE_DATA>
            </ITEM_DATA>
            <ITEM_DATA>
               <ITEM_ATTRIBUTE>Color</ITEM_ATTRIBUTE>
               <ATTRIBUTE_DATA>Purple</ATTRIBUTE_DATA>
            </ITEM_DATA>
            <ITEM_DATA>
               <ITEM_ATTRIBUTE>Weight</ITEM_ATTRIBUTE>
               <ATTRIBUTE_DATA>5</ATTRIBUTE_DATA>
            </ITEM_DATA>
            <INSTOCK>Yes</INSTOCK>
            <LOCATION></LOCATION>
            <PRICE></PRICE>
            <ONSALE></ONSALE>
            <DISCOUNT></DISCOUNT>
         </ITEM>
         <ITEM>
            <ITEM_DATA>
               <ITEM_ATTRIBUTE>Item_ID</ITEM_ATTRIBUTE>
               <ATTRIBUTE_DATA>X-124</ATTRIBUTE_DATA>
            </ITEM_DATA>
            <ITEM_DATA>
               <ITEM_ATTRIBUTE>Color</ITEM_ATTRIBUTE>
               <ATTRIBUTE_DATA>Red</ATTRIBUTE_DATA>
            </ITEM_DATA>
            <ITEM_DATA>
               <ITEM_ATTRIBUTE>Weight</ITEM_ATTRIBUTE>
               <ATTRIBUTE_DATA>3</ATTRIBUTE_DATA>
            </ITEM_DATA>
            <INSTOCK>Yes</INSTOCK>
            <LOCATION>IsleA</LOCATION>
            <PRICE>2.99</PRICE>
            <ONSALE>No</ONSALE>
            <DISCOUNT>No</DISCOUNT>
         </ITEM>
      </xSTUFF>
   </STUFF>
</LIST>

注意 XML 声明和 <LIST> 开始标记之间没有换行符。如果这是一个问题(不应该是),您可以将以下模板添加到 XSLT。

<xsl:template match="/">
  <xsl:text>&#xA;</xsl:text>
  <xsl:apply-templates/>
</xsl:template>

此外,如果您最终能够使用当前输出,则可以稍微简化 xmlstarlet 命令中的 XPath:

/LIST/STUFF/xSTUFF/ITEM[ITEM_DATA/ATTRIBUTE_DATA='X-123']/INSTOCK