0

这是示例文件,我们需要将值转换为分隔符格式的文件:-

测试.xml

<?xml version="1.0" encoding="UTF-8" ?>
 <testjar>

 <testable>
 <trigger>Trigger1</trigger>
 <message>2012-06-14T00:03.54</message>
 <sales-info>
 <san-a>no</san-a>
 <san-b>no</san-b>
 <san-c>no</san-c>
 </sales-info>
 </testable>


  <testable>
  <trigger>Trigger2</trigger>
  <message>2012-06-15T00:03.54</message>
  <sales-info>
  <san-a>yes</san-a>
  <san-b>yes</san-b>
  <san-c>no</san-c>
  </sales-info>
 </testable>

 </testjar>

每条记录都应从新行开始。示例结果集应该是这样的 sample.txt

Trigger1|2012-06-14T00:03.54|no|no|no  
Trigger2|2012-06-15T00:03.54|yes|yes|no

注意:- xmlstarlet 没有安装在我的服务器上,是否可以在没有 xmlstarlet 的情况下执行此操作?

4

3 回答 3

1

这是一个纯粹的 bash 解决方案:

egrep '<trigger>|<message>|<san-.>' test.xml | sed -e 's/<[^>]*>//g' | while read line; do [ $((++i % 5)) -ne 0 ] && echo -n "$line|" || echo $line ; done

但是,它仅适用于您的示例中格式化的文件(每个元素位于单独的行中),它​​甚至不如涉及正确 XML 解析/转换的其他答案那样灵活/可靠。

虽然可以在一定程度上增强...

于 2012-07-26T13:41:08.450 回答
1

如果您安装了xmlstarlet,您可以尝试:

me@home$ xmlstarlet sel -t -m "//testable" -v trigger -o "|" -v message -o "|" -m sales-info -v san-a -o "|" -v san-b -o "|" -v san-c -n test.xml
Trigger1|2012-06-14T00:03.54|no|no|no
Trigger2|2012-06-15T00:03.54|yes|yes|no

命令分解:

xmlstarlet sel -t 
    -m "//testable"       # match <testable>
      -v trigger -o "|"     # print out value of <trigger> followed by |
      -v message -o "|"     # print out value of <message> followed by | 
      -m sales-info         # match <sales-info>
        -v san-a -o "|"       # print out value of <san-a> followed by |
        -v san-b -o "|"       # print out value of <san-b> followed by | 
        -v san-c              # print out value of <san-c>
    -n                   # print new line
    test.xml             # INPUT XML FILE

要定位在 内变化的标签<testable>,您可以尝试以下返回所有叶节点的文本:

ma@home$ xmlstarlet sel -t -m "//testable" -m "descendant::*[not(*)]" -v 'text()' -i 'not(position()=last())' -o '|' -b -b -n test.xml 
Trigger1|2012-06-14T00:03.54|no|no|no
Trigger2|2012-06-15T00:03.54|yes|yes|no

命令的Beakdown:

xmlstarlet sel -t 
    -m "//testable"                         # match <testable>
      -m "descendant::*[not(*)]"              # match all leaf nodes
        -v 'text()'                             # print text
        -i 'not(position()=last())' -o '|'      # print | if not last item
        -b -b                                   # break out of nested matches
    -n                                      # print new line
    test.xml                                # INPUT XML FILE

如果您无法访问xmlstarlet,请查看您可以使用的其他工具。其他选项包括 xsltproc (请参阅mzjn 的答案)和xpath

如果这些工具不可用,我建议使用更高级的语言(Python、Perl),它可以让您访问适当的 XML 库。

虽然可以使用 手动解析它regex,但这样的解决方案并不理想特别是在输入不一致的情况下。例如,以下(假设你有gawkand sed)接受你的输入,并应该输出预期的输出:

me@home$ gawk 'match($0, />(.*)</, a){printf("%s|",a[1])} /<\/testable>/{print ""}' test.xml | sed 's/.$//'
Trigger1|2012-06-14T00:03.54|no|no|no
Trigger2|2012-06-15T00:03.54|yes|yes|no

但是,如果输入格式发生变化,这将非常失败,因此不是我通常推荐的解决方案

于 2012-07-26T08:56:42.190 回答
1

这是一个 XSLT 样式表,可以满足您的需求(保存在 test.xsl 中):

<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="1.0">

<xsl:output method="text"/>
<xsl:strip-space elements="*"/>

 <xsl:template match="testable">
   <xsl:value-of select='trigger'/><xsl:text>|</xsl:text>
   <xsl:value-of select='message'/><xsl:text>|</xsl:text>
   <xsl:value-of select='sales-info/san-a'/><xsl:text>|</xsl:text>
   <xsl:value-of select='sales-info/san-b'/><xsl:text>|</xsl:text>
   <xsl:value-of select='sales-info/san-c'/><xsl:text>&#xA;</xsl:text>
 </xsl:template>

</xsl:stylesheet>

命令(这里我假设你已经安装了 libxml2 和 libxslt;xsltproc是一个使用这些库的命令行工具):

xsltproc -o sample.txt test.xsl test.xml

sample.txt的内容:

Trigger1|2012-06-14T00:03.54|no|no|no
Trigger2|2012-06-15T00:03.54|yes|yes|no
于 2012-07-26T08:56:50.350 回答