Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

XML::Simple: list all element attributes

by element22 (Novice)
on Apr 08, 2015 at 05:47 UTC ( [id://1122771]=perlquestion: print w/replies, xml ) Need Help??

element22 has asked for the wisdom of the Perl Monks concerning the following question:

I have a simple XML file:

<?xml version="1.0" encoding="Windows-1252"?> <App> <node name="1st node" text="Text of 1st node" /> <node name="2nd node" text="Text of 2nd node" /> <node name="3rd node" text="Text of 3rd node" /> ... </App>

I want to use XML::Simple to list all the "nodes" and their "text" attributes as follows:

1st node: Text of 1st node 2nd node: Text of 2nd node 3rd node: Text of 3rd node ...

What I have doesn't work, I get no listing at all:

use XML::Simple; use Data::Dumper; my $input="app.xml"; my $xml = new XML::Simple(KeyAttr =>1,ForceArray =>1); my $data = $xml->XMLin($input); foreach my $node (@{$data->{App}{node}}) { print $node->{name}.": ".$node->{text}."\n"; }

Replies are listed 'Best First'.
Re: XML::Simple: list all element attributes
by frozenwithjoy (Priest) on Apr 08, 2015 at 06:03 UTC

    When I add this:

    use Data::Printer; p $data;

    I get this out:

    \ { node [ [0] { name "1st node", text "Text of 1st node" }, [1] { name "2nd node", text "Text of 2nd node" }, [2] { name "3rd node", text "Text of 3rd node" } ] }

    There is no key called 'App', so your @{$data->{App}{node}} is an empty list.

    If I change your for loop to this:

    foreach my $node (@{$data->{node}}) { print $node->{name}.": ".$node->{text}."\n"; }

    I get what you were expecting:

    1st node: Text of 1st node 2nd node: Text of 2nd node 3rd node: Text of 3rd node

    If you want to keep the root key, you can use the following and keep your original loop.

    my $xml = XML::Simple->new( KeyAttr => 1, ForceArray => 0, KeepRoot => + 1 );

      Thanks, it works now. A quick follow-up question. I've just noticed that a few "nodes" have child "nodes" with similar attributes:

      <?xml version="1.0" encoding="Windows-1252"?> <App> <node name="1st node" text="Text of 1st node" /> <node name="2nd node" text="Text of 2nd node"> <node name="subnode A" text="Text of subnode A" /> <node name="subnode B" text="Text of subnode B" /> </node> <node name="3rd node" text="Text of 3rd node" /> ... </App>

      I'd like to print this out as follows:

      1st node: Text of 1st node 2nd node: Text of 2nd node subnode A: Text of subnode A subnode B: Text of subnode B 3rd node: Text of 3rd node

      Without getting into any scary recursion, how would I modify the loop to print out the subnodes, too?

      foreach my $node (@{$data->{node}}) { print $node->{name}.": ".$node->{text}."\n"; if ( ... ) { # detect the existence of a subnode here somehow foreach my $subnode (@{$data->{node}{subnode}}) { print "\t".$subnode->{name}.": ".$subnode->{text}."\n"; } } }

        What you are looking for is: if ( $node->{node} )

        However, is a little recursion OK?

        print_nodes($data); sub print_nodes { my $data = shift; foreach my $node ( @{ $data->{node} } ) { print $node->{name} . ": " . $node->{text} . "\n"; if ( $node->{node} ) { print_nodes($node); } } }

        This should work regardless of how deep the nodes go; however, it doesn't indent:

        1st node: Text of 1st node 2nd node: Text of 2nd node subnode A: Text of subnode A subnode B: Text of subnode B 3rd node: Text of 3rd node

        EDITED TO ADD INDENTATION:

        print_nodes( $data, 0 ); sub print_nodes { my ( $data, $depth ) = @_; my $tab_width = 4; foreach my $node ( @{ $data->{node} } ) { my $indent = " " x $depth x $tab_width; print $indent . $node->{name} . ": " . $node->{text} . "\n"; if ( $node->{node} ) { print_nodes( $node, $depth + 1 ); } } }
        1st node: Text of 1st node 2nd node: Text of 2nd node subnode A: Text of subnode A subnode B: Text of subnode B 3rd node: Text of 3rd node
Re: XML::Simple: list all element attributes
by choroba (Cardinal) on Apr 08, 2015 at 08:20 UTC
    Using XML::XSH2, a wrapper around XML::LibXML:
    open file.xml ; for //node { $depth = count(ancestor::*) - 1 ; echo :s { ' ' x $depth } @name ': ' @text ; }
    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1122771]
Approved by Athanasius
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others cooling their heels in the Monastery: (5)
As of 2024-04-19 20:31 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found