Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

XML::Simple usage question

by gri6507 (Deacon)
on Jul 13, 2012 at 20:32 UTC ( [id://981718]=perlquestion: print w/replies, xml ) Need Help??

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

Fellow monks,

Let's say that I have an XML file similar to

<a> <b type="1"/> <b type="2"/> <c/> <b type="3"/> </a>
When I run my simple test program
use warnings; use strict; use XML::Simple qw(:strict); use XML::SAX::Expat; use Data::Dumper; my $xmlParser = XML::Simple->new( ForceArray => 1, # force the contents of all elements to be an + array KeepRoot => 1, # keep the root tag SuppressEmpty => '', # represent empty elements as empty strings KeyAttr => {}, # do not fold any tags ); my $info = $xmlParser->XMLin('<a><b type="1"/><b type="2"/><c/><b type +="3"/></a>') || die "Problem: $!\n"; print Dumper($info);
I get the expected output, which tells me that 'a' is an array of hashes with 'b' and 'c' as the two elements. The problem is that there is no way to identify the order of <b> and <c> tags simply by looking at the $info structure. In other words, I can't tell that <b type="2"/> is the direct predecessor of the <c> tag.

Is there some setting in XML::Simple which would give me $info in perhaps an array or arrays or some other structure so that I could see the relative order of the peer tags?

Replies are listed 'Best First'.
Re: XML::Simple usage question
by tobyink (Canon) on Jul 13, 2012 at 21:11 UTC

    Congratulations! You've completed the first level! Are you ready to play level two? XML::LibXML.

    You appear to have outgrown the facilities that XML::Simple offers. You may wish to read Stepping up from XML::Simple to XML::LibXML.

    use 5.010; use XML::LibXML 1.94; my $xml = XML::LibXML->load_xml(IO =>\*DATA); foreach my $element ($xml->findnodes("/a/*")) { say "Got ", $element->tagName; if ($element->tagName eq 'b') { say " type is ", $element->{type}; } } __DATA__ <a> <b type="1"/> <b type="2"/> <c/> <b type="3"/> </a>

    Output is...

    Got b
      type is 1
    Got b
      type is 2
    Got c
    Got b
      type is 3
    
    perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'
      Thanks for the reply! I've taken the time between posting and your response to dive much deeper into XML than I have before. What I think I want can be accomplished with XPath. I believe your solution does something similar.

      Here's what I came up with so far. Please chime in on my approach. First, though, I have to clarify that my original XML file example was not 100% representative of my structure. It looks more like this

      <a> <b id='id1' type="1"/> <b id='id2' type="1.5"/> <b id='id3' type="2"/> <c id='id4'/> <b id='id5' type="3"/> </a>
      Then, I can use XML::XPath to get what I want using the following
      use warnings; use strict; use XML::XPath; use XML::XPath::XMLParser; my $xmlFile = "try.xml"; # create an object to parse the file and field XPath queries my $xpath = XML::XPath->new(filename => $xmlFile) || die "Problem: $!\ +n"; # look for the preceding sibling node to the one in question my $nodeset = $xpath->find('//*[@id=' . "'id4']/preceding-sibling::*[1 +]"); foreach my $node ( $nodeset->get_nodelist ) { print XML::XPath::XMLParser::as_string( $node ) . "\n"; }
Re: XML::Simple usage question
by dasgar (Priest) on Jul 14, 2012 at 02:52 UTC

    According to the "My element order is getting jumbled up" section of XML::Simple::FAQ, the short answer is no. (see excerpt below)

    If your application really is sensitive to element order, don't use XML::Simple (and don't put order-sensitive values in attributes).

    Since XML::Simple is the only XML parsing module that I personally have used so far, I really can't suggest an alternative.

Re: XML::Simple usage question (steriods)
by Anonymous Monk on Jul 13, 2012 at 23:03 UTC

    XML::Rules, its XML::Simple on steriods, if you need array-o-hash type structures

    #!/usr/bin/perl -- use strict; use warnings; use XML::Rules; use Data::Dump qw/ dd /; my $ta = XML::Rules->new( qw/ stripspaces 8 /, rules => [ _default => 'raw', ], ); my $da = XML::Rules->new( qw/ stripspaces 8 /, rules => [ a => 'content', _default => 'raw', ], ); dd $ta->parse( q(<a><b type="1"/><b type="2"/><c/><b type="3"/></a>) +); dd $da->parse( q(<a><b type="1"/><b type="2"/><c/><b type="3"/></a>) +); dd $da->parse( q(<a><b type="1"/><b type="2"/><c/><b type="3">tada</b +></a>) ); __END__ [ "a", { _content => [ ["b", { type => 1 }], ["b", { type => 2 }], ["c", {}], ["b", { type => 3 }], ], }, ] { a => [ ["b", { type => 1 }], ["b", { type => 2 }], ["c", {}], ["b", { type => 3 }], ], } { a => [ ["b", { type => 1 }], ["b", { type => 2 }], ["c", {}], ["b", { _content => "tada", type => 3 }], ], }

    And xsh is a step further

    $ xsh -q $scratch/> open pm981718.xml /> ls / <?xml version="1.0"?> <a> <b type="1"/> <b type="2"/> <c/> <b type="3">tada</b> </a> /> for /a/* { pwd } /a/b[1] /a/b[2] /a/c /a/b[3] /> for /a/* { pwd; print @type; } /a/b[1] 1 /a/b[2] 2 /a/c /a/b[3] 3 /> for /a/* { pwd; for(@*){ echo xsh:path(.) " = " xsh:current(); }; } +; /a/b[1] /a/b[1]/@type = 1 /a/b[2] /a/b[2]/@type = 2 /a/c /a/b[3] /a/b[3]/@type = 3 /> /> quit saving .../.xsh2_history done

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others perusing the Monastery: (5)
As of 2024-04-19 13:49 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found