将 xml 转换为 csv

将 xml 转换为 csv

我需要使用脚本将 xml 转换为 csv。我找到了 xmlstarlet 的解决方案,但它不可用,所以我返回到点 0。这是我的 xml。

<root>
  <record>
  <id_localisation>8PJ</id_localisation>
  <data>
   <id_client>50C</id_client>  
      <mail>[email protected]</mail>
      <adress>10  </adress>
      <num_tel>001</num_tel>
      <key>C</key>
     <contact>
        <name>toto</name>
        <birth>01/30/009</birth>
        <city>London</city>
      </contact>
  </data> 
  <data>
  <id_client>25C</id_client> 
      <mail>[email protected]</mail>
      <adress>20</adress>
      <num_tel>02200</num_tel>
      <key>D1</key>
      <contact>
        <name>tata</name>
        <birth>02/08/2004</birth>
        <city>Spa</city>
      </contact>
  </data> 
</record>
  <record>
  <id_localisation>ESP31</id_localisation>
  <data>
   <id_client>70D</id_client>  
            <mail>[email protected]</mail>
          <adress>7Bcd</adress>
          <num_tel>5555</num_tel>
          <key>D2</key>
      <contact>
        <name>titi</name>
        <birth>05/07/2014</birth>
        <city>StMarine</city>
      </contact>
  </data>
  <data>
        <id_client>10D</id_client>
          <mail>[email protected]</mail>
          <adress>888</adress>
          <num_tel>881.0</num_tel>
          <key>D3</key>
      <contact>
        <name>awk</name>
        <birth>05/08/1999</birth>
        <city>Bahrein</city>
      </contact>
  </data>
 </record>
 </root>

和我需要的csv,请注意标题是输出的

id_localisation;id_client;key
8PJ;50C;C
 8PJ;25C;D1
 ESP1;70D;D2
 ESP1;10D;D3

我无法安装任何库,但我可以使用 awk、perl、bash,所以我对解决方案持开放态度。

答案1

好的。这里有一个非常基本的问题。 XML 是一种具有详细规范的复杂语言。如果没有库,这会很困难 - XML 从根本上来说是一个需要正确解析的东西。

例如使用XML::Twig使用 Perl,你会得到:

use strict;
use warnings;

use XML::Twig;

my $twig = XML::Twig->new()->parsefile ( 'your_xml_file.xml' );

print "id_localisation;id_client;key\n";
foreach my $record ( $twig->root->children('record') ) {
    foreach my $data ( $record->children('data') ) {
        print join( ";",
            $record->first_child_text('id_localisation'),
            $data->first_child_text('id_client'),
            $data->first_child_text('key'),
            ),
            "\n";
    }
}

您可能会发现它XML::Twig已经可用 - 这是相当常见的“默认安装”。所以先检查一下。

有一种肮脏的黑客方式来做这件事,但我真的不建议这样做。我的意思是真的真的,因为它会产生脆弱且可怕的代码。

我无法用更强烈的措辞重申这一点。在执行此操作之前,请先查看:https://stackoverflow.com/a/1732454/2566198

和: https://stackoverflow.com/a/28913945/2566198

请注意,在某些情况下,您可以“按用户”安装 perl 模块,这在这里可能特别相关。

并认识到您的代码故意违反了 XML 规范 - 将其视为文本文件。这是各种糟糕的事情,尤其是因为您可以对 XML 执行一些完全有效的操作,从而打破这一点。

但是,如果您绝对必须:

use strict;
use warnings;

open ( my $input_xml, "<", 'your_xml_file.xml' ) or die $!;
my $loca = "";
print "id_localisation;id_client;key\n";

for (<$input>) {
    my ($value) = (m/>(\w+)</);
    if (m/id_localisation/) { $loca = $value; }
    if (m/id_client/)       { print "$loca;$value;"; }
    if (m/key/)             { print "$value\n"; }
}
close($input);

将打印:

id_localisation;id_client;key
8PJ;50C;C
8PJ;25C;D1
ESP31;70D;D2
ESP31;10D;D3

如果您打算将其用于任何类型的生产功能,那么首先要戳那些不让您安装东西的人,让他们安装您需要的库。这听起来像是一项艰苦的工作,但请相信我 - 没有什么比试图解决有一天神秘地损坏的问题更糟糕的了,因为上游数据格式发生了变化(以规范有效的方式),但下游脚本没有实现标准。

答案2

我会尝试以下 xsl 文件

<?xml version="1.0"?>

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

<xsl:output type="text" indent="yes" omit-xml-declaration="yes" />

<xsl:template match="//record">

<xsl:for-each select="data"  >
<xsl:value-of select="../id_localisation" />;<xsl:value-of select="id_client" />;<xsl:value-of select="key "/><xsl:text>;
</xsl:text>
</xsl:for-each>
</xsl:template>
    
</xsl:stylesheet>

与使用

xsltproc sample.xsl sample.xml

答案3

使用 ltXML2 工具包(爱丁堡大学)中的 lxprintf,例如:

$ lxprintf -e data "%s;%s;%s\n" ../id_localisation id_client key test.xml
8PJ;50C;C
8PJ;25C;D1
ESP31;70D;D2
ESP31;10D;D3

使用 XSLT2 很好,但对于这种提取来说有点过分了。


XML 常见问题解答:http://xml.silmaril.ie/

答案4

使用xmlstarlet

$ echo 'id_localisation;id_client;key'; xmlstarlet sel -t -m '//record/data' -v 'concat(../id_localisation,";",id_client,";",key)' -nl file.xml
id_localisation;id_client;key
8PJ;50C;C
8PJ;25C;D1
ESP31;70D;D2
ESP31;10D;D3

标头由 输出echo,并使用 XPath 查询提取数据,该查询将每个record/data节点id_localisation的父record节点值与当前节点的id_clientkey值连接起来。

这是可行的,只要提取的数据不包含嵌入的分号或换行符,CSV 解析器就能够读取输出。


使用xq(部分yq来自https://kislyuk.github.io/yq/):

$ xq -r '[ "id_localisation", "id_client", "key" ], (.root.record[] | .id_localisation as $id | .data[] | [ $id, .id_client, .key ]) | @csv' file.xml
"id_localisation","id_client","key"
"8PJ","50C","C"
"8PJ","25C","D1"
"ESP31","70D","D2"
"ESP31","10D","D3"

这使用jq表达式来创建 CSV 表。它首先创建一个包含标头的数组,然后从 XML 结构中提取所需的数据,每个data节点一个数组。然后格式化程序@csv将这些数组转换为 CSV 记录。

要将分隔符从普通逗号更改为分号,请使用CSV 解析器csvformat中的例如csvkit

$ xq -r '...as before...' file.xml | csvformat -D ';'
id_localisation;id_client;key
8PJ;50C;C
8PJ;25C;D1
ESP31;70D;D2
ESP31;10D;D3

相关内容