Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical

Simpler than XML::Simple

by Jenda (Abbot)
on Jul 11, 2008 at 17:34 UTC ( [id://697036]=CUFP: print w/replies, xml ) Need Help??

The easiest and best known way to parse a small XML file into a Perl datastructure is to use XML::Simple like this.

use XML::Simple; my $ref = XMLin( $xmlfile);

The catch is that as soon as the XML does not look all the same in all places, you get a structure that's hard to work with. Try to look at the structure you get by parsing this:

<root> <sub> <a>foo</a> <a>bar</a> <b>blah</b> </sub> <sub> <a>baz</a> <b x="4">blah blah</b> </sub> </root>

In the first element of the {sub} array the {a} is an arrayref, in the second it's a string. The {b} on the other hand is a string in the first element and a hash in the second. So if you want to get the content of the <b> tag you have to check whether it's a hashref or the string you want.

The first problem may be solved by the ForceArray option for XMLin(), the second could be solved by ForceContent if only you could specify what tags does it affect the same way you can with ForceArray. (I already sent Grant a patch.) The problem is that you have to know the XML, you have to know what tags may be repeated, what tags have only optional attributes etc. And once you have to do that it's no longer that simple.

Welcome XML::Rules::inferRulesFromExample() and XML::Rules::inferRulesFromDTD(). These two allow you to build a list of rules for XML::Rules out of one or more example XMLs or a DTD and those rules will instruct the module to build a datastructure equivalent to a well set XML::Simple::XMLin. For example like this:

use XML::Rules; my $parser = XML::Rules->new( rules => XML::Rules::inferRulesFromExample( $XML), ); my $ref = $parser->parse( $XML);
And it's up to you whether you generate the ruleset each time or just once (which, if you use the inferRulesFromExample is not only quicker, but also safer). And you may print the generated ruleset and use it as the basis for a more specialised one. Changing the rules to skip some tags, to ignore those optional attributes and always keep just the content, specify a subroutine to be executed for a tag, etc.

my $XML = <<'*END*'; <root> <sub> <a>foo</a> <a>bar</a> <b>blah</b> </sub> <sub> <a>baz</a> <b x="4">blah blah</b> </sub> </root> *END* use XML::Simple; use Data::Dumper; my $ref = XMLin( $XML); print Dumper($ref); __END__
my $XML = <<'*END*'; <root> <sub> <a>foo</a> <a>bar</a> <b>blah</b> </sub> <sub> <a>baz</a> <b x="4">blah blah</b> </sub> </root> *END* use XML::Rules; use Data::Dumper; my $parser = XML::Rules->new( rules => XML::Rules::inferRulesFromExample( $XML), ); my $ref = $parser->parse( $XML); print Dumper($ref);

Replies are listed 'Best First'.
Re: Simpler than XML::Simple
by ambrus (Abbot) on Jul 11, 2008 at 18:07 UTC

    Well, you could use XML::Smart which gives a simple interface, but that interface is always the same.

      There are two reasons why I would not want to. First working with the heavily tie()d datastructure is slower and second it lets you get away with treating a repeatable tag as if it always occured just once. Which means that instead of XML::Simple's default behaviour of noisy failure or apparent stringified reference and XML::Rules::inferRulesFromXxx's consistent generation of array, you get a silent incorrect result.

Re: Simpler than XML::Simple
by eserte (Deacon) on Nov 22, 2010 at 15:32 UTC
    The only solution for this type of problem is XML::Compile.

    Why? Apparently the problem here is to get XML data serialized as perl data structures. I see two use cases: either the producer and the consumer of the XML data is the same system, or it is different systems, for example us and a different party.

    In the first case I don't see why to go the XML path at all. It is much easier to code and probably much more efficient if just a data serialization format is used --- YAML, JSON, Storable, bencode, perl (Data::Dumper) or whatever. Typically this involves one line of code for serializing and deserializing, and should always be faster than doing some XML parsing.

    In the second case there are two parties which need to handle the XML data. In this case, you must have an XML schema, to have a contract how the XML data should look like. And if you have the XML schema, then you can easily use XML::Compile to gain at least the simplicity of serializing and deserializing.

      In the second case there are two parties which need to handle the XML data. In this case, you must have an XML schema

      Heh, good luck with that it in the real world :-) My real-world experience led me to postulate the law of The "Perl end" of XML.

        The [Perl side] inevitably has to bend over and take it.

        I'm furious with you, sir -- furious! -- for making so much pain seem so fscking funny.

        Aw, dang it! The link "Perl End" gives an error page. Got update?

      "Must have an XML schema" ... get real! Yeah, you might have a schema. And the schema migh even be reasonable. Quite often it's not. Ever heard for example of HR-XML? What a ... massive overcomplicated schema. With each company using different parts of the schema. A mess.

      There is never "the only solution".

      Enoch was right!
      Enjoy the last years of Rome.

        Well, the real world ... my experience is that it's hard to get just well-formed and correctly encoded XML files from external parties.

        Did you know that the "trang" tool is capable of creating a schema out of sample data? So I don't see the advantage with using XML::Smart.

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: CUFP [id://697036]
Approved by marto
Front-paged by Arunbear
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others romping around the Monastery: (6)
As of 2024-05-22 00:00 GMT
Find Nodes?
    Voting Booth?

    No recent polls found