XML::Spice(3)
π XML::Spice π
okay so bestie, welcome to the XML::Spice(3) manpage β it's filed under User Contributed Perl Documentation, which is basically the group chat of Perl docs, and it's giving generating XML has never been so Perly! π
β¨
πΈ NAME πΈ
XML::Spice β generating XML has never been so Perly! π
β¨ SYNOPSIS β¨
use XML::Spice qw(html head title body h1 p a);
print
html(
head(
title("my great page"),
),
body(
h1("my great page"),
p("this is my great page, made with ",
a("spice", { href => "http://en.wikipedia.org/wiki/Spice/" }),
),
),
);
look at that. clean. readable. immaculate. π¦
π DESCRIPTION π
so. XML::Spice is yet another XML generation module (yes bestie, there are many, we don't talk about them). but this one? this one tries to take some of the pain out of generating XML by making it more like Perl. π
unless you've got a really good module for producing XML for your particular use (like, a module for interfacing with a specific web service, that kind of thing), you've probably found that you end up resorting to code like this:
my $xml = q{<foo><bar><baz /></bar><quux /></foo>};
and like β sure! it works great, and ngl you can't beat it for speed, but it quickly becomes difficult to work with. π your syntax highlighting probably just displays it as one giant string. you can't easily see mismatched brackets or other bugs until your code runs and tries to parse the whole thing. and the second you start adding attributes and character data into the mix, it rapidly moves towards being impossible to read. π
instead of that, you could use XML::Spice and write the exact same thing in Perl:
my $xml = foo(bar(baz()), quux());
the way that you can add liberal amounts of whitespace to convey structure without it making your output larger?? iconic. you get Perl checking to make sure you haven't left anything out. you can use all the power of Perl to generate and include data without having to pepper your code with interpolated strings or concatenation operators. and you get a guarantee that the XML produced is valid. periodt. π
π BASIC USAGE π
if you use (or import) XML::Spice without any arguments, it will export a single function x() into your package. this is the only real function in XML::Spice, and it's used to implement everything else. the main character, fr fr. β
x() generates a single element, which in turn can contain attributes, character data, sub-elements (via additional calls to x()), and more. the general format for x() is:
my $xml = x("element", ...);
the first argument is required, and is always the name of the element to generate. so x("foo") produces <foo/>. simple as that bestie.
generally though, you'll want to use the more readable named functions to do the work. you get these by providing arguments to XML::Spice when you use it (or call import). for example:
use XML::Spice qw(foo bar baz);
this will export three functions into your package β foo(), bar() and baz() β and won't import x(). calling these functions produces the same results as calling x() with the name as the first argument, that is:
my $xml = foo(...);
produces identical results to:
my $xml = x("foo", ...);
x() returns an XML::Spice::Chunk object, which when stringified (ie printed or interpolated into a string) produces the XML of its input. generally you won't care, you'll just stringify it and be done with it. there are however some rather clever things that can be done by having the return value be an object instead of a normal string; see "ADVANCED USAGE" for details. π§
π ARGUMENTS π
x() can take zero or more additional arguments. these arguments define what else gets added to the element. what happens depends on what you pass. π«Ά
π attributes
attributes are added to the element by passing a hash reference, eg:
img({ src => "hello.jpg" });
produces:
<img src='hello.jpg' />
if you pass multiple hash references, their contents are combined, with the value from the last hash passed being used in the case of a conflict. last one wins, no cap. π
π sub-elements
sub-elements are included in an element by passing the output from another call to x(), eg:
foo(bar());
produces:
<foo><bar /></foo>
π character data
character data is added to the element by passing simple strings, eg:
p("this is my paragraph");
produces:
<p>this is my paragraph</p>
these arguments can be mixed as much as you like, eg:
p("Visit my ", a({ href => "http://homepage.com/"}, "homepage"), " for more information.");
produces:
<p>Visit my <a href='http://homepage.com/'>homepage</a> for more information.</p>
other things can be passed to x(); those are described in "ADVANCED USAGE". π
π₯ ADVANCED USAGE π₯
π Dynamic tree generation
the most important thing to understand about the XML::Spice::Chunk objects returned by x() is that they do nothing until they are stringified to produce XML output. this makes it possible to pass code references or even other objects to x() and have them dynamically generate data to be included in the produced XML. lazy evaluation but make it fashion. β¨
if a code reference is passed to x(), it is called when the resultant XML::Spice::Chunk is stringified and its output is included at the position that the code reference was at, eg:
p("the time is ", sub { scalar localtime });
would produce something like:
<p>the time is Sat Sep 26 22:32:57 2009</p>
XML::Spice::Chunk will recursively evaluate the result from the code reference until it gets down to basic strings and hash references as described in "BASIC USAGE". this is great for producing lists of things, eg:
my $list = ul(sub {
my @results;
opendir my $dir, "images";
push @results, li($_) for grep { m/\.png$/ } readdir $dir;
closedir $dir;
return @results;
});
when $list is stringified, the sub will be called and would return a list of XML::Spice::Chunk objects. these in turn will be stringified until eventually only strings are left and output like the following is produced:
<ul><li>foo.png</li><li>bar.png</li><li>baz.png</li></ul>
had the sub itself returned a code reference, then that in turn would have been called and its output used. it's recursion, bestie. it's giving inception. π
β οΈ the code reference is called every time the XML::Spice::Chunk object is stringified. if the computed result will not change, consider caching the result.
to support this, the following things may be passed to x() (and thus returned by code references or objects):
undefβ ifundefis passed tox(), it is ignored. that is:
foo("bar", undef, "baz");
is exactly equivalent to:
foo("bar", "baz");
and produces:
<foo>barbaz</foo>
- array reference β if an array reference is passed to
x(), it is flattened. that is:
foo("bar", [ baz(), "quux" ]);
is exactly equivalent to:
foo("bar", baz(), "quux");
and produces:
<foo>bar<baz />quux</foo>
π Result cache
a chunk is only evaluated the first time it is stringified. the result is cached and each subsequent stringification will return the cached result. if you wanted to reuse a chunk (eg if it has a coderef in it that does a database lookup ποΈ), you can call its forget() method to remove the cached result. the next time it is stringified it will be reevaluated from scratch. fresh start, clean slate, it's giving glow up. π¦
π PRETTY PRINTING π
XML::Spice can produce pretty-printed output. because the additional whitespace subtly changes the semantics of the generated XML this is only intended as a debugging feature. also it disables the result cache. so like, use it to look at, not to ship. π
to use it, you'll need the XML::Tidy::Tiny module installed. then to enable it, set (and localise!) $XML::Spice::PRETTY_PRINT to a true value before stringifying a chunk. β¨
β TODO β
- Optimised namespace declarations β if two sub-chunks declare the same namespace, then move the declaration to the parent chunk. efficiency slay. π
π BUGS AND LIMITATIONS π
this module guarantees that the XML it produces will be valid and semantically equivalent to the input you give it, but it makes no guarantees and gives no control over things like use of entity encoding vs. CDATA sections, declaration of namespaces and use of prefixes, and so forth. the method used may change between releases, producing different results. if you require exact control over the details of the XML produced, then this module is not for you. no hard feelings bestie, it's just not the vibe. π
π«Ά SUPPORT π«Ά
π Bugs / Feature Requests
please report any bugs or feature requests through the issue tracker at <https://github.com/robn/XML-Spice/issues>. you will be notified automatically of any progress on your issue. we got you. π
π Source Code
this is open source software. the code repository is available for public review and contribution under the terms of the license.
<https://github.com/robn/XML-Spice>
git clone https://github.com/robn/XML-Spice.git
π AUTHORS π
- Robert Norris <rob@eatenbyagrue.org>
πΈ COPYRIGHT AND LICENSE πΈ
this software is copyright (c) 2006-2016 by Robert Norris.
this is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
okay that's the whole thing, bestie! π this page was brought to you by perl v5.42.0, dated 2025-07-25, filed under XML::Spice(3) β now go forth and generate some extremely valid XML. mwah. πβ¨π¦
π¦ where this page comes from
- fedora 43: perl-XML-Spice 0.05-27.fc43 Β· GPL-1.0-or-later OR Artistic-1.0-Perl
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms