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
执行查询。
查询本身有一些问题:在
concat 函数的语法是 concat(a,b,c)
;您的调用省略了逗号。
在匹配中,xpaths 是相对于匹配节点的。但是 concat 中的第一个元素:
/modelField/model/field/value
是绝对的,所以它只能从根开始匹配,而事实并非如此。你需要一个相对表达式:
modelField/model/field/value
或
./modelField/model/field/value
最后一个 xpath:
/field[@Body]/value
不会被找到,因为 field
不是根元素,没有 /
它也不会匹配,因为 field
不是匹配的节点。在这里,您可以如上所述拼出匹配节点的路径,或者使用 //
到 select 任何子节点:
.//field[@Body]/value
但是,说明符 [@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
我有一个 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
执行查询。
查询本身有一些问题:在
concat 函数的语法是
concat(a,b,c)
;您的调用省略了逗号。在匹配中,xpaths 是相对于匹配节点的。但是 concat 中的第一个元素:
/modelField/model/field/value
是绝对的,所以它只能从根开始匹配,而事实并非如此。你需要一个相对表达式:
modelField/model/field/value
或
./modelField/model/field/value
最后一个 xpath:
/field[@Body]/value
不会被找到,因为
field
不是根元素,没有/
它也不会匹配,因为field
不是匹配的节点。在这里,您可以如上所述拼出匹配节点的路径,或者使用//
到 select 任何子节点:.//field[@Body]/value
但是,说明符
[@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