xmlstarlet:使用单个查询查询和连接嵌套子元素

xmlstarlet: Querying and concatenating nested child elements with a single query

我有一个 XML 文件,例如:

    <?xml version="1.0" encoding="utf-8"?>
    <project>
    <data>
        <modelType type="InstantMessage">
            <model type="InstantMessage" id="1" >
                <modelField name="From" type="Party">
                    <model type="Party" id="123456">
                        <field name="Identifier" type="String">
                            <value type="String">foo</value>
                        </field>
                    </model>
                </modelField>
                <multiModelField name="To" type="Party" />
                    <field name="Body" type="String">
                        <value type="String">bar</value>
                    </field>
                    <field name="TimeStamp" type="TimeStamp">
                        <value type="TimeStamp">2016-07-11 13:26:38+02:00</value>
                    </field>
            </model>
        </modelType>
    </data>
    </project>

我确实想通过单个查询生成以下结果:

foo|bar

当嵌套在不同级别时,我不知道如何访问这些字段。我试过类似的东西:

root@machine:/.../# xmlstarlet sel -T -t -m /project/data/modelType/model -v "concat(/modelField/model/field/value'|'/field[@Body]/value)" file.xml

但我一直遇到 xmlstarlet 的语法错误。我从manual 不明白如何使用它。有谁知道在这种情况下如何使用 xmlstarlet?

谢谢,彼得

您的 XML 文件(如图所示)缺少 <project> 的结束标记;这将导致解析错误,从而阻止 xmlstarlet 执行查询。

查询本身有一些问题:在

  1. concat 函数的语法是 concat(a,b,c);您的调用省略了逗号。

  2. 在匹配中,xpaths 是相对于匹配节点的。但是 concat 中的第一个元素:

    /modelField/model/field/value
    

    是绝对的,所以它只能从根开始匹配,而事实并非如此。你需要一个相对表达式:

    modelField/model/field/value
    

    ./modelField/model/field/value
    

    最后一个 xpath:

    /field[@Body]/value
    

    不会被找到,因为 field 不是根元素,没有 / 它也不会匹配,因为 field 不是匹配的节点。在这里,您可以如上所述拼出匹配节点的路径,或者使用 // 到 select 任何子节点:

    .//field[@Body]/value
    
  3. 但是,说明符 [@Body] 不正确。如所写,如果元素具有名为 Body 的属性,则 selector 会成功。您正在尝试将一个元素与名为 name 的属性相匹配,该属性的值为 Body,您可以将其写为 [@name="Body"]。引号是强制性的,这意味着您需要在表达式周围使用单引号或反斜杠转义引号。

综合起来,修复 XML 文件后,您可以使用:

xmlstarlet sel -T \
  -t -m /project/data/modelType/model \
     -v 'concat(modelField/model/field/value,"|",.//field[@name="Body"]/value)' \
  file.xml

调用 concat 并不是真正必要的,因为您可以使用多个 -v 选项,并且 -o 输出固定字符串。您可能会发现以下内容更具可读性:

xmlstarlet sel -T \
  -t -m '/project/data/modelType/model' \
     -v './/field[@name="Identifier"]/value' \
     -o '|' \
     -v './/field[@name="Body"]/value' \
  file.xml