与 xmlstarlet 一起使用的 XML/XPath 表达式是什么,用于将元素移动到第一个元素?

What is the XML/XPath expression to use with xmlstarlet to move an element to be the first element?

我有一个 XML 文件类似于:

<?xml version="1.0"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
  <Document>
    <Folder>
      <name>Assets and Risks</name>
      <Placemark>
        <name>Asset_4000</name>
        <description>Address: 2286</description>
        <Point>
          <coordinates>xxx.xxx,yyy.yyy,0</coordinates>
        </Point>
      </Placemark>
      <Placemark>
        <name>Risk_2000</name>
        <description>Address: 32</description>
        <Point>
          <coordinates>xxx.xxx,yyy.yyy,0</coordinates>
        </Point>
      </Placemark>
    </Folder>
    <Folder>
      <name>The second folder</name>
    </Folder>
  </Document>
</kml>

我想使用 xmlstarlet(最好是所有命令行而不是 XSLT)将 Risk_2000 的地标移动到同一文件夹中的第一个地标(即 Asset_4000 的地标之前) ).

我知道第一部分是:

xmlstarlet edit --move "//_:kml/_:Document/_:Folder[_:name=\"Assets and Risks\"]/_:Placemark[_:name=\"Risk_2000\"]" **But What Goes Here**

感谢任何指导。

以下解决方案有效 - 但是,由于 xmlstarlet 的 --move 的工作方式,而不是移动“Risk_2000 的地标成为同一文件夹中的第一个地标",您必须将当前的第一个地标移动到第一个 Folder 元素的底部。您还必须声明默认名称空间。

所以尝试:

xml edit -N x="http://www.opengis.net/kml/2.2"  \
--move "//x:Placemark[.//.='Asset_4000']" "//x:Folder[1]" file.xml 

输出(给定上面的 xml)应该是您的预期输出。

xmlstarlet edit--move 的目的地必须是单个节点, 所以变通:

  • 在位置N插入临时元素
  • 从源元素更新它
  • 删除源元素
  • 重命名临时元素

(对于 non-GNU/Linux 平台调整引用和行继续 以下命令中的字符。)

xmlstarlet edit -N v='http://www.opengis.net/kml/2.2' \
    --insert '//v:Folder[v:name="Assets and Risks"]/v:Placemark[1]' \
        --type elem --name 'Placemark_TMP' --value '' \
    --update '$xstar:prev' --expr '../v:Placemark[v:name="Risk_2000"]/node()' \
    --delete '$xstar:prev/../v:Placemark[v:name="Risk_2000"]' \
    --rename '$xstar:prev' --value 'Placemark' \
  file.xml | xmlstarlet format --nsclean

xmlstarlet edit 代码可以使用方便的 $xstar:prev (又名 $prev) 节点引用最近创建的节点 -i / --insert-a / --append-s / --subnode 选项。例子 $xstar:prev 中给出了 doc/xmlstarlet.txt 和 源代码的 examples/ed-backref*.

--expr '../v:Placemark[…]/node()' 深度复制元素的 子节点但不是其属性 (background)。 有问题的元素没有属性,否则使用 XPath --expr '../v:Placemark[…]/node() | ../v:Placemark[…]/@*'.

--nsclean 步骤删除了冗余的命名空间声明。

因为 xmlstarlet 支持 可能有 set:leadingset:trailing 功能,但我没有研究它。

对于基于样式表的方法,请参见 .


更新 2021-09-29

上面的代码可以使用

改写如下
  • 简短命令和选项
  • --var <name> <xpath> 选项(在 doc/xmlstarlet.txt 但不在用户指南中)
  • 默认命名空间shortcut_:

并添加 | $src/@* 以包含属性:

xmlstarlet ed \
    --var dir '//_:Folder[_:name="Assets and Risks"]' \
    --var tgt '$dir/_:Placemark[1]' \
    --var src '$dir/_:Placemark[_:name="Risk_2000"]' \
    -i '$tgt' -t elem -n 'Placemark_TMP' -v '' \
    -u '$xstar:prev' -x '$src/node() | $src/@*' \
    -d '$src' \
    -r '$xstar:prev' -v 'Placemark' \
    file.xml | xmlstarlet fo -N

根据建议 函数 set:leadingset:trailing 可能是相关的,或者可能不值得,但是当

printf '%s\n' '<doc><c><nm>C2</nm><e1/><e2/><e3/><e4/></c></doc>' |
xmlstarlet ed -O --pf --var T '/doc/c[nm="C2"]' -u '$T' -x 'set:trailing($T/*, e2)'

产出

<doc><c><e1/><e2/><e3/><e4/><e3/><e4/></c></doc>

我会暂缓考虑:<nm>C2</nm> 可能已经走了 但我有预感它坐在 src/xml_edit.c。 然而,从那里取下来是另一回事。