3

我正在尝试使用 perl 和 XML::Simple 从 XML 文件中提取少量数据到 csv 文件中。

这是数据的编辑版本:

<?xml version="1.0" encoding="UTF-8"?>
<orders xmlns="http://www.demandware.com/xml/impex/order/2006-10-31">
    <order order-no="W100148941">
        <order-date>2011-08-22T16:15:47.000Z</order-date>
        <custom-attributes>
            <custom-attribute attribute-id="basket_notes">bnotes974211</custom-attribute>
            <custom-attribute attribute-id="omOrderID">974211</custom-attribute>
        </custom-attributes>
    </order>
</orders>

使用这个脚本:

#!/usr/bin/perl

use XML::Simple;
use Data::Dumper;

$xml = new XML::Simple;
$data = $xml->XMLin("$ARGV[0]", ForceArray=>1);


print Dumper($data);
foreach $o (@{$data->{order}}) {
    print "$ARGV[1]", ",";
    print "$ARGV[2]", ",";
    print "$ARGV[3]", ",";
    print "$ARGV[4]", ",";
    print $o->{"order-no"}, ",";
    print $o->{"order-date"}, ",";
    foreach my $o ( @{ $data->{'custom-attribute'} } ) {
        print 'in level 1';
        foreach my $attr ( @{ $data->{'custom-attribute'} } ) {
            print 'in level 2';
            if ( $attr->{'attribute-id'} eq 'basket_notes' ) {
                print '"', $data->{'content'}, '"', ",";
            }
        }
    }
    print "\n";
}

得到我这个输出:

,,,,W100148941,ARRAY(0x7f7f63a524c0),

不使用 ForceArray 选项 XMLin 会将上面的 ARRAY(...) 替换为正确的值,但不适用于只有一个数据元素的文件,而且很明显,此代码永远不会进入自定义属性数组打印任何东西。

我究竟做错了什么?

更新:

将上面的循环代码更改为:

foreach $o (@{$data->{order}})
{
print "$ARGV[1]", ",";
print "$ARGV[2]", ",";
print "$ARGV[3]", ",";
print "$ARGV[4]", ",";
print $o->{"order-no"}, ",";
#print $o->{"order-date"}, ",";
print $o->{"order-date"}->[0], ",";
foreach my $o ( @{ $data->{'custom-attributes'} } ) {
    print 'in level 1';
   foreach my $attr ( @{ $o->{'custom-attribute'} } ) {
        print 'in level 2';
        if ( $attr->{'attribute-id'} eq 'omOrderID' ) {
            print '"', $data->{'content'}, '"', ",";
        }
    }
}

print "\n";
}

产生这个:

,,,,W100148941,2011-08-22T16:15:47.000Z,

看起来代码只是没有进入自定义属性循环,我不知道为什么。

4

2 回答 2

3

您的问题是“订单日期” - 由于 ForceArray - 也被迫成为 arrayref,正如您从已经存在的 Dumper 输出中看到的那样:

...
     'order-date' => [
                     '2011-08-22T16:15:47.000Z'
                     ],

因此,您需要做以下两件事之一:

  • 如果 order-date 始终是单个值,则硬编码打印第一个数组值:

    print $o->{"order-date"}->[0], ",";
    
  • 如果 order-date 始终是单个值,请通过传递更详细的说明来更改构造函数参数ForceArray

    XML::Simple POD 表明,除了一个简单的ForceArray=>1选项外,您还可以将要强制执行的有限标签列表传递ForceArray => [ "custom-attributes", "custom-attribute" ]给数组(例如)

    • 如果order-date可以有多个标签,只需在循环中打印它,就像您已经在下面使用其他多个标签一样:

      foreach 我的 $order_date (@{ $data->{'order-date'} }) { print "$order_date,"


此外,您的嵌套循环中有几个错误。

你的第一个循环应该是

foreach my $o ( @{ $data->{'custom-attributes'} } ) { # You had "attribute"

第二个循环应该遍历它的子结构:

    foreach my $attr ( @{ $o->{'custom-attribute'} } ) { # instead of $data->...

撇开这一切不谈,从我相当丰富的经验来看,将 XML 转换为平面文件 (CSV) 是个坏主意,委婉地说。请认真考虑您是否在做正确的事情。

如果没有巧妙的编码,就无法正确或轻松地映射数据;并且稍后解码那个狡猾的编码并不比简单地再次读取 XML 更容易。

  • 如果您需要对其进行转换以便其他程序可以读取它,请保留 XML 或转换为 JSON

  • 如果您需要将其转换为显示给人类,请使用Data::Dumper或其他一些漂亮的打印机

  • 如果您需要将其作为 GUI 展示给人类,请开发一个好的 GUI 以匹配您的数据结构。

于 2012-08-29T00:08:54.400 回答
2

除了来自 DVK 的回答:

我相信你需要封闭你的最外层循环

foreach $o (@{$data->{order}})

在另一个循环中,因为“订单”项目似乎包含在“订单”项目中

    foreach $oo (@{$data->{orders}}) {
       foreach $o (@{$oo->{order}})
       {
       ....
       }
    }  #additional closing for the additional foreach

最好的祝福,

奥利维尔。

于 2012-11-21T10:14:26.747 回答