Thread unbekanntes XML-File in CSV
(8 answers)
Opened by mordur. at 2014-01-16 14:45
Mal aus der Hüfte geschossen:
Code (perl): (dl
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 #!/usr/bin/perl use strict; use warnings; use XML::Simple; use Text::CSV; my $in_file=shift(@ARGV); my $out_file=shift(@ARGV); my $namesep='>'; my $print_names=1; # parse XML my $tree=XMLin($in_file, KeyAttr => {}, KeepRoot => 1); my @names; my @lines; # finde alle Namen # und löse den Baum auf: my @stack=([$tree,'',{}]); while(@stack) { my ($t,$p,$d)=@{shift(@stack)}; if(ref($t) eq 'HASH') { # daten der ebene einsammeln # kinder überspringen my $end=1; my %data=%$d; while( my ($k,$v)=each(%$t)){ if(ref($v)){ $end=0; } else{ my $pp="$p$namesep$k"; # unbekannten namen zur Liste push(@names,[$k,$pp]) unless( grep{ $_->[1] eq $pp }@names); $data{$pp}=$v; } } # es gibt keine kinder # Datensatz vollständig if($end) { push(@lines,\%data); } # Kinder auf den Stack while( my ($k,$v)=each(%$t)) { my $pp="$p$namesep$k"; if(ref($v) eq 'ARRAY') { for my $vv (@$v) { if(ref($vv)) { push(@stack,[$vv,$pp,{%data}]); } else { # unbekannten namen zur Liste push(@names,[$k,$pp]) unless( grep{ $_->[1] eq $pp }@names); push(@lines,{%data, $pp => $vv}); } } } elsif(ref($v) eq 'HASH') { push(@stack,[$v,$pp,{%data}]); } } } elsif(ref($t) eq 'ARRAY') { die("Fehler im Baum! $p\n"); } } # CSV schreiben: open( my $fh, ">:unix:encoding(UTF-8)", $out_file) or die "Can't open $out_file $!\n"; my $csv = Text::CSV->new({ binary => 1, eol => "\n", sep_char => ';' }); # Titelzeile schreiben wenn gewollt if($print_names > -1) { my @n = map{$_=~s/^\Q$namesep\E//s; $_}map{$_->[$print_names]}@names; $csv->print ($fh,\@n); } # Daten schreiben for my $data (@lines) { my @line; for my $n (@names) { push(@line,$data->{$n->[1]} // ''); } $csv->print ($fh,\@line); } close($fh); Das Script erzeugt aus einer beliebigen XML-Datei CVS Daten. Aber es ist nicht immer möglich das eine Zeile "Vollständig" ist. Dazu müsste man die Logik noch erweitern und Spezialfälle besonders behandeln. Manche Strukturen sind gar nicht aufzulösen. Speziell wenn mehere sub-Bäume Listen enthalten. (Siehe dein Beispiel) Man kann versuchen für jede Kombination aus den Listen eine Zeile zu erzeugen. Das ist aber nicht immer sinnvoll, da die Daten keine direkte Beziehung haben. Als Dummes Beispiel: Dein XML-Datensatz zu einem UserAccont, enthält sowohl eine Liste von Buchtiteln, eine Liste von Kontonummer und eine Liste von Gerichten. Wollte man das in ein CSV übertragen in dem jede Spalte einen "Vollständigen" Datensatz enthält müsste jeder Buchtitel mit jeder Kontonummer mit jedem Gericht kombiniert werden. Was völliger Unsinn ist. :-) |