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(...) が正しい値に置き換えられますが、データ要素が 1 つしかないファイルでは機能しません。また、明らかなように、このコードはカスタム属性配列にはなりません。何でも印刷します。

私は何を間違っていますか?

アップデート:

上記のループ コードを次のように変更します。

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による「注文日」も、既存のダンパー出力からわかるように、強制的に配列参照になっていることです。

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

したがって、次の 2 つのいずれかを行う必要があります。

  • order-date が常に単一の値である場合、最初の配列値をハードコードで出力します。

    print $o->{"order-date"}->[0], ",";
    
  • order-date が常に単一の値である場合は、より詳細なForceArray指示を渡してコンストラクターの引数を変更してください。

    XML::SimpleForceArray=>1 POD は、単純なオプションとは別に、強制したい限定されたタグのリストを配列に渡すこともできることをForceArray => [ "custom-attributes", "custom-attribute" ]示しています (例: )

    • 複数のタグを持つことができる場合order-dateは、以下の他の複数のタグで既に行っているように、単にループで印刷します。

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


また、ネストされたループにいくつかのバグがあります。

最初のループは

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

そして、2 番目のループは、そのサブ構造をループする必要があります。

    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 に答える