XML::Hash::XS(3pm)

πŸ“¦
version 0.64 Β· 5 versions
, in debian trixie, ubuntu resolute

πŸ’– XML::Hash::XS πŸ’–

okay so bestie, lemme put you onto XML::Hash::XS β€” it's a Perl module that does hash β†’ XML and XML β†’ hash conversion, and it's written in C so it's fast fast. ✨ It's XML::Hash::XS(3pm) from the User Contributed Perl Documentation, and honestly? iconic. πŸ’…


✨ NAME ✨

XML::Hash::XS β€” Simple and fast hash to XML and XML to hash conversion written in C. no cap, that's literally the whole thing. πŸ¦‹


🌸 SYNOPSIS 🌸

The functional way, bestie:

use XML::Hash::XS;

my $xmlstr = hash2xml \%hash;
hash2xml \%hash, output => $fh;

my $hash = xml2hash $xmlstr;
my $hash = xml2hash \$xmlstr;
my $hash = xml2hash 'test.xml', encoding => 'cp1251';
my $hash = xml2hash $fh;
my $hash = xml2hash *STDIN;

Or the OOP way, if that's more your vibe:

use XML::Hash::XS qw();

my $conv   = XML::Hash::XS->new(utf8 => 0, encoding => 'utf-8')
my $xmlstr = $conv->hash2xml(\%hash, utf8 => 1);
my $hash   = $conv->xml2hash($xmlstr, encoding => 'cp1251');

πŸ‘‘ DESCRIPTION πŸ‘‘

This module implements simple hash to XML and XML to hash conversion written in C. 🧚

During conversion it uses the minimum of memory β€” the XML or the hash gets written directly, without building a DOM. it's giving efficiency, it's giving lewk. πŸ’…

Some features are optional and only show up if you have the right libraries installed:

  • XML::LibXML library is required in order to build DOM
  • ICU or iconv library is required in order to perform charset conversions

πŸ”₯ FUNCTIONS πŸ”₯

πŸ’œ hash2xml $hash, [ %options ]

$hash is a reference to a hash, babes.

hash2xml
    {
        node1 => 'value1',
        node2 => [ 'value21', { node22 => 'value22' } ],
        node3 => \'value3',
        node4 => sub { return 'value4' },
        node5 => sub { return { node51 => 'value51' } },
    },
    canonical => 1,
    indent    => 2,
;

will convert to:

<?xml version="1.0" encoding="utf-8"?>
<root>
  <node1>value1</node1>
  <node2>value21</node2>
  <node2>
    <node22>value22</node22>
  </node2>
  <node3>value3</node3>
  <node4>value4</node4>
  <node5>
    <node51>value51</node51>
  </node5>
</root>

and (with use_attr=1):

hash2xml
    {
        node1 => 'value1',
        node2 => [ 'value21', { node22 => 'value22' } ],
        node3 => \'value3',
        node4 => sub { return 'value4' },
        node5 => sub { return { node51 => 'value51' } },
    },
    use_attr  => 1,
    canonical => 1,
    indent    => 2,
;

will convert to:

<?xml version="1.0" encoding="utf-8"?>
<root node1="value1" node3="value3" node4="value4">
  <node2>value21</node2>
  <node2 node22="value22"/>
  <node5 node51="value51"/>
</root>

πŸ¦‹ xml2hash $xml, [ %options ]

$xml can be a string, a reference to a string, a file handle, or a tied file handle β€” she's flexible like that:

xml2hash '<root>text</root>';
# output: 'text'

xml2hash '<root a="1" b="2">text</root>';
# output: { a => '1', b => '2', content => 'text' }

open(my $fh, '<', 'test.xml');
xml2hash $fh;

xml2hash *STDIN;

πŸ’… OPTIONS πŸ’…

  • doc [ => 0 ] β€” # hash2xml. if doc is '1', then the returned value is an XML::LibXML::Document. πŸ“„
  • root [ = 'root' ] β€” # hash2xml. the root node name.
  • version [ = '1.0' ] β€” # hash2xml. the XML document version.
  • encoding [ = 'utf-8' ] β€” # hash2xml+xml2hash. the XML input/output encoding.
  • indent [ = 0 ] β€” # hash2xml. if indent is greater than "0", the XML output gets indented according to its hierarchic structure. This value determines the number of spaces. πŸŽ€

if indent is "0", the XML output will all be on one line. one line! efficient!

  • output [ = undef ] β€” # hash2xml. the XML output method.

if output is undefined, the XML document is dumped into a string.

if output is an FH, the XML document writes directly to a filehandle or a stream.

  • canonical [ = 0 ] β€” # hash2xml. if canonical is "1", the converter will write hashes sorted by key. πŸ”€

if canonical is "0", the order of the elements will be pseudo-randomly.

  • use_attr [ = 0 ] β€” # hash2xml. if use_attr is "1", the converter will use the attributes.

if use_attr is "0", the converter will use tags only.

  • content [ = undef ] β€” # hash2xml+xml2hash. if defined, that's the key name for the text content (used only if use_attr=1).
  • force_array [ = undef ] β€” # xml2hash. this option is similar to "ForceArray" from the XML::Simple module: https://metacpan.org/pod/XML::Simple#ForceArray-=%3E-1-%23-in-important.
  • force_content [ = 0 ] β€” # xml2hash. this option is similar to "ForceContent" from the XML::Simple module: https://metacpan.org/pod/XML::Simple#ForceContent-=%3E-1-%23-in-seldom-used.
  • merge_text [ = 0 ] β€” # xml2hash. setting this option to "1" will cause adjacent text nodes to merge. 🫢
  • xml_decl [ = 1 ] β€” # hash2xml. if xml_decl is "1", the output will start with the XML declaration '<?xml version="1.0" encoding="utf-8"?>'.

if xml_decl is "0", the XML declaration will not be output.

  • trim [ = 0 ] β€” # hash2xml+xml2hash. trim leading and trailing whitespace from text nodes. βœ‚οΈ
  • suppress_empty [ = 0 ] β€” # xml2hash. this option is similar to "SuppressEmpty" from the XMl::Simple module: https://metacpan.org/pod/XML::Simple#SuppressEmpty-=%3E-1-%7C-''-%7C-undef-%23-in+out-handy.
  • utf8 [ = 1 ] β€” # hash2xml+xml2hash. turn on the utf8 flag for strings if enabled.
  • max_depth [ = 1024 ] β€” # xml2hash. maximum recursion depth. don't go deeper than this, bestie, it's not that kind of party. πŸ’€
  • buf_size [ = 4096 ] β€” # hash2xml+xml2hash. buffer size for reading and encoding data.
  • keep_root [ = 1 ] β€” # hash2xml+xml2hash. keep root element.
  • filter [ = undef ] β€” # xml2hash. filter nodes matched by pattern and return a reference to an array of nodes. πŸ”

Sample:

my $xml = <<'XML';
    <root>
       <item1>111</item1>
       <item2>222</item2>
       <item3>333</item3>
    </root>
XML

my $nodes = xml2hash($xml, filter => '/root/item1');
# $nodes = [ 111 ]

my $nodes = xml2hash($xml, filter => ['/root/item1', '/root/item2']);
# $nodes = [ 111, 222 ]

my $nodes = xml2hash($xml, filter => qr[/root/item\d$]);
# $nodes = [ 111, 222, 333 ]

It may be used to parse large XML because it does not require a lot of memory. πŸ’Ύ

  • cb [ = undef ] β€” # xml2hash. this option is used in conjunction with the "filter" option and defines a callback that will be called for each matched node.

Sample:

xml2hash($xml, filter => qr[/root/item\d$], cb => sub {
    print $_[0], "\n";
});
# 111
# 222
# 333
  • method [ = 'NATIVE' ] β€” # hash2xml. experimental support for conversion methods from other libraries. πŸ§ͺ

if method is 'LX' then the conversion result is the same as using the XML::Hash::LX library.

Note: for the 'LX' method the following additional options are available:

attr
cdata
text
comm

🧚 OBJECT SERIALISATION(hash2xml) 🧚

  1. When object has a "toString" method

In this case, the <toString> method of the object is invoked in scalar context. It must return a single scalar that can be directly encoded into XML.

Example:

use XML::LibXML;
local $XML::LibXML::skipXMLDeclaration = 1;
my $doc = XML::LibXML->new->parse_string('<foo bar="1"/>');
print hash2xml({ doc => $doc }, indent => 2, xml_decl => 0);
=>
<root>
  <doc><foo bar="1"/></doc>
</root>
  1. When object has overloaded stringification

In this case, the stringification method of the object is invoked and the result is directly encoded into XML.

Example:

package Test {
    use overload '""' => sub { shift->stringify }, fallback => 1;
    sub new {
        my ($class, $str) = @_;
        bless { str => $str }, $class;
    }
    sub stringify {
        shift->{str}
    }
}
my $obj = Test->new('test string');
print hash2xml({ obj => $obj }, indent => 2, xml_decl => 0);
=>
<root>
  <obj>test string</obj>
</root>
  1. When object has a "iternext" method ("NATIVE" method only)

In this case, the <iternext> method will be invoked a few times until the return value is not undefined.

Example:

my $count = 0;
my $o = bless {}, 'Iterator';
*Iterator::iternext = sub { $count++ < 3 ? { count => $count } : undef };
print hash2xml({ item => $o }, use_attr => 1, indent => 2, xml_decl => 0);
=>
<root>
  <item count="1"/>
  <item count="2"/>
  <item count="3"/>
</root>

This can be used to generate a large XML using minimum memory, example with DBI:

my $sth = $dbh->prepare('SELECT * FROM foo WHERE bar=?');
$sth->execute(...);
my $o = bless {}, 'Iterator';
*Iterator::iternext = sub { $sth->fetchrow_hashref() };
open(my $fh, '>', 'data.xml');
hash2xml({ row => $o }, use_attr => 1, indent => 2, xml_decl => 0, output => $fh);
=>
<root>
  <row bar="..." ... />
  <row bar="..." ... />
  ...
</root>

🌈 BENCHMARK 🌈

Performance benchmark in comparison with some popular modules (hash2xml):

                    Rate     XML::Hash XML::Hash::LX   XML::Simple XML::Hash::XS
XML::Hash     65.0/s            --           -6%          -37%          -99%
XML::Hash::LX 68.8/s            6%            --          -33%          -99%
XML::Simple    103/s           58%           49%            --          -98%
XML::Hash::XS 4879/s         7404%         6988%         4658%            --

Benchmark was done on http://search.cpan.org/uploads.rdf

like... 4879/s vs 65.0/s?? she ATE and left no crumbs. πŸ‘‘


πŸŽ€ AUTHOR πŸŽ€

Yuriy Ustushenko, <yoreek@yahoo.com>


Copyright (C) 2012-2021 Yuriy Ustushenko

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 vibe check bestie, go forth and convert some XML πŸ’–

mwah πŸ’‹ β€” this was perl v5.40.0, dated 2025-01-02, aka the XML::Hash::XS(3pm) era. slay, periodt. ✨

πŸ“¦ where this page comes from