1

.csv私は以下のような単純なファイルを読み取るPerlスクリプトを持っています-

"header1","header2","header3","header4"
 "12","12-JUL-2012","Active","Processed"
 "13","11-JUL-2012","In Process","Pending"
 "32","10-JUL-2012","Active","Processed"
 "24","08-JUL-2012","Active","Processed"
 .....

.csv目的は、これを次の.xmlようなファイルに変換することです-

<ORDERS>
  <LIST_G_ROWS>
     <G_ROWS>
         <header1>12</header1>
         <header2>12-JUL-2012</header2>
         <header3>Active</header3>
         <header4>Processed</header4>
     </G_ROWS>
     <G_ROWS>
         <header1>13</header1>
         <header2>11-JUL-2012</header2>
         <header3>In Process</header3>
         <header4>Pending</header4>
     </G_ROWS>
....
....
   </LIST_G_ROWS>
</ORDERS>

XML::CSV私の生活を楽にするCPANで利用できるものがあることは知っていXML::LibXMLますが、インストールする代わりに、すでにインストールされているものを利用してXMLを作成したいと思いますXML::CSV。上記のようにCSVを読み取ってXMLファイルを問題なく作成できましたが、XML内の要素のランダムな順序(以下のようなもの)を取得しています。上記のように、要素(子ノード)の順序を.csvファイルと同期させる必要がありますが、それをどのように回避するかはよくわかりません。私はaを使用してhashsort()ますが、ハッシュを使用しても問題は完全には解決しませんでした。

<ORDERS>
  <LIST_G_ROWS>
     <G_ROWS>
         <header3>Active</header3>
         <header1>12</header1>
         <header4>Processed</header4>
         <header2>12-JUL-2012</header2>                          
     </G_ROWS>
 ......

等々。以下は私のperlコードからの抜粋です

use XML::LibXML;
use strict;

my $outcsv="/path/to/data.csv";
my $$xmlFile="/path/to/data.xml";
my $headers = 0;
my $doc = XML::LibXML::Document->new('1.0', 'UTF-8');
my $root = $doc->createElement("ORDERS");
my $list = $doc->createElement("LIST_G_ROWS");
$root->appendChild($list);

open(IN,"$outcsv") || die "can not open $outcsv:  $!\n";
while(<IN>){    
    chomp($_);
    if ($headers == 0)
    {
        $_ =~ s/^\"//g;     #remove starting (")
        $_ =~ s/\"$//g;     #remove trailing (")
        @keys = split(/\",\"/,$_);  #split per ","
        s{^\s+|\s+$}{}g foreach @keys;  #remove leading and trailing spaces from each field
        $headers = 1;       
    }
    else{   
        $_ =~ s/^\"//g;     #remove starting (")
        $_ =~ s/\"$//g;     #remove trailing (")    
        @vals = split(/\",\"/,$_);  #split per ","
        s{^\s+|\s+$}{}g foreach @vals;  #remove leading and trailing spaces from each field

        my %tags = map {$keys[$_] => $vals[$_]} (0..@keys-1);                   
        my $row  = $doc->createElement("G_ROWS");
        $list->appendChild($row);
        for my $name (keys %tags) {
            my $tag = $doc->createElement($name);
            my $value = $tags{$name};
            $tag->appendTextNode($value);               
            $row->appendChild($tag);
        }
    }
}
close(IN);

$doc->setDocumentElement($root);
open(OUT,">$xmlFile") || die "can not open $xmlFile:  $!\n";
print OUT $doc->toString();
close(OUT);
4

3 に答える 3

1

%tagsハッシュを完全に忘れることができます。@keys代わりに、 :のインデックスをループします。

for my $i (0 .. @keys - 1) {
    my $key   = $keys[$i];
    my $value = $values[$i];
    my $tag   = $doc->createElement($key);
    $tag->appendTextNode($value);
    $row->appendChild($tag);
}

そうすれば、キーの順序が保持されます。ハッシュを使用する場合、順序は不確定です。

于 2012-07-31T20:28:12.307 に答える
1

あなたのプログラムは、必要以上に複雑です。利便性と信頼性のためText::CSVに、CSVファイルの解析に使用する必要があります。

以下のプログラムはあなたが必要とすることをします。

use strict;
use warnings;

use Text::CSV;
use XML::LibXML;

open my $csv_fh, '<', '/path/to/data.csv' or die $!;
my $csv = Text::CSV->new;
my $headers = $csv->getline($csv_fh);

my $doc = XML::LibXML::Document->new('1.0', 'UTF-8');
my $orders = $doc->createElement('ORDERS');
$doc->setDocumentElement($orders);
my $list = $orders->appendChild($doc->createElement('LIST_G_ROWS'));

while ( my $data = $csv->getline($csv_fh) ) {

  my $rows = $list->appendChild($doc->createElement('G_ROWS'));

  for my $i (0 .. $#$data) {
    $rows->appendTextChild($headers->[$i], $data->[$i]);
  }
}

print $doc->toFile('/path/to/data.xml', 1);

出力

<?xml version="1.0" encoding="UTF-8"?>
<ORDERS>
  <LIST_G_ROWS>
    <G_ROWS>
      <header1>12</header1>
      <header2>12-JUL-2012</header2>
      <header3>Active</header3>
      <header4>Processed</header4>
    </G_ROWS>
    <G_ROWS>
      <header1>13</header1>
      <header2>11-JUL-2012</header2>
      <header3>In Process</header3>
      <header4>Pending</header4>
    </G_ROWS>
    <G_ROWS>
      <header1>32</header1>
      <header2>10-JUL-2012</header2>
      <header3>Active</header3>
      <header4>Processed</header4>
    </G_ROWS>
    <G_ROWS>
      <header1>24</header1>
      <header2>08-JUL-2012</header2>
      <header3>Active</header3>
      <header4>Processed</header4>
    </G_ROWS>
  </LIST_G_ROWS>
</ORDERS>

アップデート

提供するエキゾチックオプションがText::CSVなければ、オプションが固定されていれば、その機能はかなり単純です。この代替手段は、メソッドcsv_readlineを置き換えるためのサブルーチンを提供します。モジュールとほとんど同じように機能します。Text::CSVreadline

このプログラムの出力は上記と同じです。

use strict;
use warnings;

use XML::LibXML;

open my $csv_fh, '<', '/path/to/data.csv' or die $!;

my $doc = XML::LibXML::Document->new('1.0', 'UTF-8');
my $orders = $doc->createElement('ORDERS');
$doc->setDocumentElement($orders);
my $list = $orders->appendChild($doc->createElement('LIST_G_ROWS'));

my $headers = csv_getline($csv_fh);

while ( my $data = csv_getline($csv_fh) ) {

  my $rows = $list->appendChild($doc->createElement('G_ROWS'));

  for my $i (0 .. $#$data) {
    $rows->appendTextChild($headers->[$i], $data->[$i]);
  }
}

print $doc->toFile('/path/to/data.xml', 1);

sub csv_getline {
  my $fh = shift;
  defined (my $line = <$fh>) or return;
  $line =~ s/\s*\z/,/;
  [ map { /"(.*)"/ ? $1 : $_ } $line =~ /( " [^"]* " | [^,]* ) , /gx ];
}
于 2012-07-31T21:54:14.440 に答える
-2

XMLファイルにダンプするのではなく、そのXML構造を記述する適切なハッシュを使用して構築するXML::LibXmlだけではやり過ぎのようです。XML::SimpleXMLOut

于 2012-07-31T20:28:00.770 に答える