Thread unbekanntes XML-File in CSV (8 answers)
Opened by mordur. at 2014-01-16 14:45

topeg
 2014-01-18 18:01
#173064 #173064
User since
2006-07-10
2611 Artikel
BenutzerIn

user image
Mal aus der Hüfte geschossen:
more (21.6kb):
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. :-)

View full thread unbekanntes XML-File in CSV