Structured(3pm)

πŸ“¦
version 1.3+git20220429 Β· 5 versions
, in debian trixie, ubuntu resolute

πŸ’– Structured(3pm) β€” like, the XML::Structured era πŸ’–

okay bestie, sit down, hydrate, because this is <rainbow>XML::Structured</rainbow> β€” a whole module for turning XML into perl data structures and then back again, and it's genuinely lowkey iconic. It lives in the User Contributed Perl Documentation corner of the manual, and no cap, it's giving "simple conversion API from XML to perl structures and back." ✨


🌸 NAME 🌸

XML::Structured β€” a simple conversion API from XML to perl structures and back. periodt.


✨ SYNOPSIS ✨

here's the whole vibe in one glance, bestie:

use XML::Structured;

$dtd = [
    'element' =>
        'attribute1',
        'attribute2',
        [],
        'element1',
        [ 'element2' ],
        [ 'element3' =>
            ...
        ],
        [[ 'element4' =>
            ...
        ]],
];

$hashref = XMLin($dtd, $xmlstring);
$hashref = XMLinfile($dtd, $filename_or_handle);
$xmlstring = XMLout($dtd, $hashref);
XMLoutfile($dtd, $hashref, $filename_or_handle);

like… look at it. it's so clean. πŸ¦‹


πŸ‘‘ DESCRIPTION πŸ‘‘

So <pink>XML::Structured</pink> gives you a way to convert xml data into a predefined perl data structure and back to xml. And here's the thing that makes it different from the modules you already know β€” unlike with modules like <cyan>XML::Simple</cyan>, it is an error if the xml data does not match the provided skeleton (the <mint>"dtd"</mint>). no silent chaos. no surprise hash shapes. we stan a module that holds a boundary. πŸ’…

Another advantage? The order of the attributes and elements is taken from the dtd when converting back to xml. so your output looks exactly how you said it should look. main character energy, fr fr.


πŸŽ€ XMLin() πŸŽ€

The <cyan>XMLin()</cyan> function takes the dtd and a string as arguments and returns a hash reference containing the data. that's it, that's the whole job. 🫢


🌈 XMLinfile() 🌈

This function works like <cyan>XMLin()</cyan>, but takes a filename or a file handle as second argument. same energy, just reading from a file instead of a string. πŸ’–


⭐ XMLout() ⭐

<cyan>XMLout()</cyan> provides the reverse operation to <cyan>XMLin()</cyan> β€” it takes a dtd and a hash reference as arguments and returns an XML string. so you hand it your perl data and it hands you back the xml. iconic behavior honestly.


🧚 XMLoutfile() 🧚

This function works like <cyan>XMLout()</cyan>, but takes a filename or a file handle as third argument. yes bestie, third β€” <cyan>XMLoutfile()</cyan> wants the dtd, the hashref, and the destination. πŸŽ€


πŸ’œ The DTD πŸ’œ

The <mint>dtd</mint> parameter specifies the structure of the allowed xml data. It consists of nested perl arrays. that's the whole secret sauce β€” nested perl arrays all the way down. 🌸


πŸ“ simple attributes and elements πŸ“

The very simple example for a dtd is:

$dtd = [ 'user' =>
             'login',
             'password',
       ];

This dtd will accept/create XML like:

<user login="foo" password="bar"/>

And here's the gag: <cyan>XMLin</cyan> doesn't care if <mint>"login"</mint> or <mint>"password"</mint> are attributes or elements, so this

<user>
  <login>foo</login>
  <password>bar</password>
</user>

is also valid input (but doesn't get re-created by <cyan>XMLout()</cyan>). ngl, that last bit matters β€” read it twice. πŸ‘€


πŸ¦‹ multiple elements of the same name πŸ¦‹

If an element may appear multiple times, it must be declared as an array in the dtd:

$dtd = [ 'user' =>
             'login',
             [ 'favorite_fruits' ],
       ];

<cyan>XMLin</cyan> will create an array reference as value in this case, even if the xml data contains only one element. yes, even then. Valid XML looks like:

<user login="foo">
  <favorite_fruits>apple</favorite_fruits>
  <favorite_fruits>peach</favorite_fruits>
</user>

As attributes may not appear multiple times, <cyan>XMLout</cyan> will create elements for this case. Note also that all attributes must come before the first element, thus the first array in the dtd ends the attribute list. so as an example, the following dtd

$dtd = [ 'user' =>
             'login',
             [ 'favorite_fruits' ],
             'password',
       ];

will create xml like:

<user login="foo">
  <favorite_fruits>apple</favorite_fruits>
  <favorite_fruits>peach</favorite_fruits>
  <password>bar</password>
</user>

<mint>"login"</mint> is translated to an attribute and <mint>"password"</mint> to an element. see how the array changed everything? the way that one little [ ... ] reshuffled the whole output. 😭

You can use an empty array reference to force the end of the attribute list, e.g.:

$dtd = [ 'user' =>
             [],
             'login',
             'password',
       ];

will translate to

<user>
  <login>foo</login>
  <password>bar</password>
</user>

instead of

<user login="foo" password="bar"/>

empty array = "attributes stop HERE bestie". πŸ›‘


πŸ’… sub-elements πŸ’…

sub-elements are elements that also contain attributes or other elements. They are specified in the dtd as arrays with more than one element. Here is an example:

$dtd = [ 'user' =>
             'login',
             [ 'address' =>
                 'street',
                 'city',
             ],
       ];

Valid xml for this dtd looks like:

<user login="foo">
  <address street="broadway 7" city="new york"/>
</user>

It is sometimes useful to specify such dtds in multiple steps:

$addressdtd = [ 'address' =>
                     'street',
                     'city',
              ];

$dtd = [ 'user' =>
             'login',
             $addressdtd,
       ];

reusable dtd pieces? we love modularity in this house. πŸŽ€


πŸ”₯ multiple sub-elements with the same name πŸ”₯

As with simple elements, one can allow sub-elements to occur multiple times. <cyan>XMLin()</cyan> creates an array of hash references in this case. The dtd specification uses an array reference to an array for this case, for example:

$dtd = [ 'user' =>
             'login',
             [[ 'address' =>
                 'street',
                 'city',
             ]],
       ];

Or, with the <mint>$addressdtd</mint> definition used in the previous example:

$dtd = [ 'user' =>
             'login',
             [ $addressdtd ],
       ];

Accepted XML is:

<user login="foo">
  <address street="broadway 7" city="new york"/>
  <address street="rural road 12" city="tempe"/>
</user>

double brackets, double the drama, double the addresses. πŸ™οΈ


🌟 the _content pseudo-element 🌟

All of the non-whitespace parts between elements get collected into a single <mint>"_content"</mint> element. As example,

<user login="foo">
  <address street="broadway 7" city="new york"/>hello
  <address street="rural road 12" city="tempe"/>world
</user>

would set the _content element to <mint>"hello world\n"</mint> (the dtd must allow a _content element, of course). don't forget that part β€” the dtd has to let it in, otherwise it's a no. πŸ™ˆ

If the dtd is

$dtd = [ 'user' =>
             'login',
             [ $addressdtd ],
             '_content',
       ];

the xml string created by <cyan>XMLout()</cyan> will be:

<user login="foo">
  <address street="broadway 7" city="new york"/>
  <address street="rural road 12" city="tempe"/>
hello world
</user>
⚠️ The exact input cannot be re-created, as the positions and the fragmentation of the content data is lost. so it round-trips the words, but not the layout. keep that in your notes bestie, it's important.

πŸ” SEE ALSO πŸ”

<cyan>XML::Structured</cyan> requires either <cyan>XML::Parser</cyan> or <cyan>XML::SAX</cyan>. you need one of them in the building, no exceptions. ✨


Copyright 2006-2019 Michael Schroeder <mls@suse.de>

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.


and that's the whole page, bestie!! πŸ’– this was Structured(3pm) from the User Contributed Perl Documentation, running under perl v5.40.1 and dated 2025-04-15 β€” go forth and structure your XML, you absolute legend πŸ‘‘πŸ¦‹πŸŒΈ

πŸ“¦ where this page comes from