Re: XML Attribute vs Element
by pjotrik (Friar) on Nov 06, 2008 at 15:12 UTC
|
If you want a simple value to be processed as an element, make it an array reference.
#!/usr/bin/perl
use warnings;
use strict;
use XML::Simple;
my $data = {
e => ['element value'],
a => 'attribute value'
};
print XMLout($data, RootName => 'root');
__DATA__
<root a="attribute value">
<e>element value</e>
</root>
| [reply] [d/l] |
|
|
| [reply] |
Re: XML Attribute vs Element
by GrandFather (Saint) on Nov 06, 2008 at 20:00 UTC
|
Please do not link off site code. Include it in your node (in readmore tags if it is large). Off site links are prone to breakage and PerlMonks nodes are read by people for years after their initial posting. So, if at all possible keep your node self contained.
Also note that </br> on its own is not valid HTML or XHTML. You can use <br /> for XHTML, but this is more an HTML site so use <br>, or better still, don't use break tags at all!
Perl reduces RSI - it saves typing
| [reply] |
|
|
#!/usr/bin/perl --
use strict;
use warnings;
use Cwd;
use Win32::OLE;
use XML::Simple;
my $dir = cwd();
opendir(DIR, ".");
my @file = grep(/\.xml$/,readdir(DIR));
closedir(DIR);
use Data::Dumper;
local $Data::Dumper::Indent=1;
#print Dumper($doc);
###################
#my %hash = (
# key1 => 'ArraySite',
# key2 => 'IOPort',
# key3 => 'AddressGroup',
# key4 => 'LSS',
# key5 => 'Array',
# key6 => 'Rank',
# key7 => 'ExtentPool',
# key8 => 'Volume',
# key9 => 'StorageEnclosure',
# key10 => 'VolumeGroup',
# );
#
# for my $key ( keys %hash ) {
# my $value = $hash{$key};
# print "$key => $value\n";
# }
##############
my $xs1 = XML::Simple->new();
my $doc = $xs1->XMLin($file[0], keyattr=>[], ForceContent=>1, ForceArr
+ay=>1);
for my $sub1 ( sort keys %{ $doc } ){
my $count = scalar keys %{ $doc };
print "\n$sub1\t";
for (my $i=0; $i <= $count; $i++) {
for my $sub2 ( sort keys %{ $doc->{$sub1}[$i] } ){
my $count2 = @{$doc->{$sub1}[$i]{$sub2}};
#print "\n\t\t".$count2."\t".$sub2;
for (my $j=0; $j <= $count2; $j++) {
for my $sub3 ( sort keys %{ $doc->{$sub1}[$i]{$sub2}[$
+j] } ){
my $count3 = @{$doc->{$sub1}[$i]{$sub2}[$j]{$sub3}
+};
print "\n\t$sub2\t$sub3($count3)";
for (my $k=0; $k <= $count3; $k++) {
print "\n";
for my $sub4 ( sort keys %{ $doc->{$sub1}[$i]{
+$sub2}[$j]{$sub3}[$k] } ){
print "\n\t\t\t$sub4 : ";
print $doc->{$sub1}[$i]{$
+sub2}[$j]{$sub3}[$k]{$sub4};
}
}
}
}
}
}
}
XML
<A>
<B>
<C>
<element type="k" name="p" online="yes"/>
<element type="i" name="e" online="yes"/>
</C>
<D>
<element type="kd" name="pd" online="no"/>
<element type="id" name="ed" online="yes"/>
<element type="yd" name="zd" online="no"/>
</D>
</B>
<Z>
<C>
<element type="k" name="z" online="yes"/>
<element type="t" name="p" online="yes"/>
</C>
<E>
<element type="kd" name="pd" online="no"/>
<element type="id" name="ed" online="yes"/>
</E>
</Z>
</A>
But I could still use some help... When I add an attribute to lets say tag B my code fails all of a sudden ... Can someone tell me why / advise or point out a solution... The error I get is "Can't use string ("value of attribute") as an ARRAY ref while "strict refs""
Thanks so much !
| [reply] [d/l] [select] |
|
|
When I add an attribute to lets say tag B...
You are getting the error message because XML::Simple creates a data structure with an array for your elements but a simple scalar value for your attribute.
You can see this if you use Data::Dumper to dump $doc after loading your file.
After changing your B tag to <B type="xxx"> your script produced the following error: Can't use string ("xxx") as an ARRAY ref while "strict refs" in use. Here is the structure produced by XMLin:
If you look carefully at what has been created for your tag B, you will see that there are three elements in the contained hash, with keys D, C and type. The values of the first two (D and C) are references to arrays but the value of the third is a string. Your program uses all three as if they were references to arrays. Because you have used use strict; (which is a good thing) perl stops when it gets to the string and reports the error.
You can either write your program to check whether a value is a reference or not, using ref or you can provide parameters to XML::Simple to make it load the elements and tags consistently. Which approach is better will depend on what you are doing. You may want to learn both methods so you can decide which is most appropriate.
| [reply] [d/l] |
|
|
|
|
use warnings;
use strict;
use XML::Parser;
my $xmlStr = <<XML;
<A>
<B>
<C>
<element type="k" name="p" online="yes"/>
<element type="i" name="e" online="yes"/>
</C>
<D>
<element type="kd" name="pd" online="no"/>
<element type="id" name="ed" online="yes"/>
<element type="yd" name="zd" online="no"/>
</D>
</B>
<Z>
<C>
<element type="k" name="z" online="yes"/>
<element type="t" name="p" online="yes"/>
</C>
<E>
<element type="kd" name="pd" online="no"/>
<element type="id" name="ed" online="yes"/>
</E>
</Z>
</A>
XML
use constant kIndentSize => 4;
my $indent = '';
my $xs1 = XML::Parser->new (
Handlers => {
Start => sub {start (\$indent, @_);},
End => sub {end (\$indent, @_);},
Char => sub {char (\$indent, @_);}
});
$xs1->parse ($xmlStr);
sub start {
my ($indent, $expat, $element, %attr) = @_;
print "$$indent$element\n";
$$indent .= ' ' x kIndentSize;
print "$$indent$_ : $attr{$_}\n" for sort keys %attr;
return;
}
sub end {
my ($indent, $expat, $element) = @_;
substr $$indent, -(kIndentSize), kIndentSize, '';
return;
}
sub char {
my ($indent, $expat, $string) = @_;
chomp $string;
$string =~ s/^\s+|\s+$//g;
return unless length $string;
print "$$indent$string\n";
return;
}
Prints:
A
B
C
element
name : p
online : yes
type : k
element
name : e
online : yes
type : i
D
element
name : pd
online : no
type : kd
element
name : ed
online : yes
type : id
element
name : zd
online : no
type : yd
Z
C
element
name : z
online : yes
type : k
element
name : p
online : yes
type : t
E
element
name : pd
online : no
type : kd
element
name : ed
online : yes
type : id
Perl reduces RSI - it saves typing
| [reply] [d/l] [select] |
|
|
|
|
my $count = scalar keys %{ $doc };
to
my $count = scalar @{ $doc->{$sub1} };
And similarly for your other counts.
Update: no, your other counts are fine. And you don't need the scalar as you are assigning to a scalar, so you can do as you have done for the other counts:
my $count = @{ $doc->{$sub1} };
| [reply] [d/l] [select] |
|
|
# Add attribute "xxx" with value "yyy" to
# the first (idx 0) "B" child of node $doc.
$doc->{B}[0]{xxx} = 'yyy';
| [reply] [d/l] |
Re: XML Attribute vs Element
by Jenda (Abbot) on Nov 06, 2008 at 20:23 UTC
|
What is it you actually want to do? Don't talk about the one step, tell us your goal.
| [reply] |
|
|
XML's that needs to be parsed to Excell. The XML file above only needs attributes in the not deepest tags. (A,B,C ..)
In the real XML every tag has attributes. Not only the <element> ones.
Unfortunately the code fails if I add attributes to the outer tags. It's a nice script, because in contrary to every script i've seen so far, is this one without words that are in the XML. No A, B, C, D, ... element, type, ... Thanks to my imagination and the help of perlmonks
How does one solve this in a good way? Some kind of check weather it has nested elements or it is parsing an attribute...
| [reply] |
|
|
| [reply] |
Re: XML Attribute vs Element
by xinelo (Initiate) on Nov 24, 2008 at 09:19 UTC
|
Hi,
I had the same doubt about generating attributes for an element and the reply by pjotrik sorted it out... partially.
I still wonder what I can do to generate an attribute for e rather than for the root element.
That is, I would like my data to look like
<root>
<e a="attribute value">element value</e>
</root>
rather than
<root a="attribute value">
<e>element value</e>
</root>
Any ideas? I am sure it's simple but I'm inexperienced...
Thanks a lot!
xinelo | [reply] [d/l] [select] |