XML-PugiXML
view release on metacpan or search on metacpan
0.01 2026-03-04
- Initial release
- Fast XML parsing via pugixml (8-12x faster than XML::LibXML)
- Document: load_file, load_string, save_file, to_string, reset
- DOM navigation: root, child, children, parent, first_child, last_child,
next_sibling, previous_sibling, find_child_by_attribute
- Attributes: attr, attrs, append_attr, prepend_attr, set_attr, remove_attr
- Modification: append_child, prepend_child, insert_child_before/after,
append_copy, prepend_copy, insert_copy_before/after, remove_child
- Special nodes: append_cdata, append_comment, append_pi
- XPath: select_node, select_nodes, compile_xpath with
evaluate_node/nodes/string/number/boolean
- Node info: name, value, text, type, path, hash, offset_debug, valid
- Attribute types: as_int, as_uint, as_llong, as_ullong, as_double, as_bool
- Parse/format constants for fine-grained control
- UTF-8 support
- Reference-counted memory model (nodes keep document alive)
PUGI_CONST("FORMAT_INDENT", format_indent);
PUGI_CONST("FORMAT_NO_DECLARATION", format_no_declaration);
PUGI_CONST("FORMAT_RAW", format_raw);
PUGI_CONST("FORMAT_WRITE_BOM", format_write_bom);
PUGI_CONST("FORMAT_INDENT_ATTRIBUTES", format_indent_attributes);
PUGI_CONST("FORMAT_NO_EMPTY_ELEMENT_TAGS", format_no_empty_element_tags);
PUGI_CONST("PARSE_DEFAULT", parse_default);
PUGI_CONST("PARSE_MINIMAL", parse_minimal);
PUGI_CONST("PARSE_PI", parse_pi);
PUGI_CONST("PARSE_COMMENTS", parse_comments);
PUGI_CONST("PARSE_CDATA", parse_cdata);
PUGI_CONST("PARSE_WS_PCDATA", parse_ws_pcdata);
PUGI_CONST("PARSE_WS_PCDATA_SINGLE", parse_ws_pcdata_single);
PUGI_CONST("PARSE_ESCAPES", parse_escapes);
PUGI_CONST("PARSE_EOL", parse_eol);
PUGI_CONST("PARSE_DECLARATION", parse_declaration);
PUGI_CONST("PARSE_DOCTYPE", parse_doctype);
PUGI_CONST("PARSE_FULL", parse_full);
PUGI_CONST("NODE_NULL", node_null);
PUGI_CONST("NODE_DOCUMENT", node_document);
PUGI_CONST("NODE_ELEMENT", node_element);
PUGI_CONST("NODE_PCDATA", node_pcdata);
PUGI_CONST("NODE_CDATA", node_cdata);
PUGI_CONST("NODE_COMMENT", node_comment);
PUGI_CONST("NODE_PI", node_pi);
PUGI_CONST("NODE_DECLARATION", node_declaration);
PUGI_CONST("NODE_DOCTYPE", node_doctype);
#undef PUGI_CONST
}
MODULE = XML::PugiXML PACKAGE = XML::PugiXML::Node
CHECK_NODE_ALIVE(self);
CHECK_NODE_ALIVE(ref_node);
CHECK_SAME_DOC(self, ref_node);
xml_node child = self->node.insert_child_after(name, ref_node->node);
RETVAL = wrap_node(aTHX_ child, self->doc_sv);
}
OUTPUT:
RETVAL
SV*
append_cdata(XML::PugiXML::Node self, nul_safe_pv content)
CODE:
{
CHECK_NODE_ALIVE(self);
xml_node cdata = self->node.append_child(node_cdata);
if (cdata) {
cdata.set_value(content);
}
RETVAL = wrap_node(aTHX_ cdata, self->doc_sv);
}
OUTPUT:
RETVAL
SV*
append_comment(XML::PugiXML::Node self, nul_safe_pv content)
CODE:
{
CHECK_NODE_ALIVE(self);
xml_node comment = self->node.append_child(node_comment);
lib/XML/PugiXML.pm view on Meta::CPAN
Insert child element before or after a reference node.
=item append_copy($source), prepend_copy($source)
Clone and append/prepend a node (deep copy).
=item insert_copy_before($source, $ref), insert_copy_after($source, $ref)
Clone and insert node before/after reference.
=item append_cdata($content)
Add a CDATA section with the given content.
=item append_comment($content)
Add a comment node with the given content.
=item append_pi($target, $data?)
Add a processing instruction (e.g., C<< <?target data?> >>).
t/01-basic.t view on Meta::CPAN
$doc->load_string('<root xmlns:ns="http://example.com"><ns:item ns:attr="val"/></root>');
my $item = $doc->root->first_child;
is($item->name, 'ns:item', 'namespaced element name');
is($item->attr('ns:attr')->value, 'val', 'namespaced attr value');
}
#--------------------------------------------------
# CDATA and Comment tests
#--------------------------------------------------
# Test append_cdata
{
my $doc = XML::PugiXML->new;
$doc->load_string('<root/>');
my $cdata = $doc->root->append_cdata('<script>alert(1)</script>');
ok($cdata, 'append_cdata returns node');
is($cdata->type, 4, 'CDATA node type is 4');
like($doc->to_string, qr/<!\[CDATA\[<script>alert\(1\)<\/script>\]\]>/, 'CDATA in output');
}
# Test append_comment
{
my $doc = XML::PugiXML->new;
$doc->load_string('<root/>');
my $comment = $doc->root->append_comment('This is a comment');
ok($comment, 'append_comment returns node');
is($comment->type, 5, 'comment node type is 5');
t/01-basic.t view on Meta::CPAN
# Text node (PCDATA)
my $text = $doc->root->first_child;
is($text->type, 3, 'text node type is 3');
}
# Test reading existing CDATA
{
my $doc = XML::PugiXML->new;
$doc->load_string('<root><![CDATA[<raw>&data</raw>]]></root>');
is($doc->root->text, '<raw>&data</raw>', 'CDATA content read correctly');
my $cdata_node = $doc->root->first_child;
is($cdata_node->type, 4, 'existing CDATA node type is 4');
}
#--------------------------------------------------
# New functionality tests
#--------------------------------------------------
# Test prepend_child
{
my $doc = XML::PugiXML->new;
$doc->load_string('<root><b/><c/></root>');
t/01-basic.t view on Meta::CPAN
$doc->load_string('<root>text</root>');
my $text_node = $doc->root->first_child;
is($text_node->type, 3, 'got text node');
ok(!$text_node->set_name('x'), 'set_name on text node returns false');
}
# value() on CDATA node
{
my $doc = XML::PugiXML->new;
$doc->load_string('<root><![CDATA[data]]></root>');
my $cdata = $doc->root->first_child;
is($cdata->value, 'data', 'value() on CDATA node returns content');
}
# previous_sibling with name filter no match
{
my $doc = XML::PugiXML->new;
$doc->load_string('<root><a/><b/><c/></root>');
my $c = $doc->root->last_child;
ok(!defined $c->previous_sibling('z'), 'previous_sibling(name) no match returns undef');
}
t/01-basic.t view on Meta::CPAN
$doc->load_string('<root><!--hello--></root>', XML::PugiXML::PARSE_COMMENTS());
my @comments = grep { $_->type == 5 } $doc->root->children;
is(scalar @comments, 1, 'PARSE_COMMENTS makes comments accessible');
is($comments[0]->value, 'hello', 'PARSE_COMMENTS comment content');
}
# PARSE_CDATA functional test
{
my $doc = XML::PugiXML->new;
$doc->load_string('<root><![CDATA[raw]]></root>', XML::PugiXML::PARSE_CDATA());
my @cdata = grep { $_->type == 4 } $doc->root->children;
is(scalar @cdata, 1, 'PARSE_CDATA makes CDATA nodes accessible');
is($cdata[0]->value, 'raw', 'PARSE_CDATA content');
}
# save_file clears $@ on success
{
my ($fh, $tmp) = tempfile(SUFFIX => '.xml', UNLINK => 1);
close $fh;
my $doc = XML::PugiXML->new;
$doc->load_string('<bad>'); # sets $@
ok($@, '$@ set before save_file');
$doc->load_string('<root/>');
t/01-basic.t view on Meta::CPAN
is(XML::PugiXML::NODE_PCDATA(), 3, 'NODE_PCDATA');
is(XML::PugiXML::NODE_CDATA(), 4, 'NODE_CDATA');
is(XML::PugiXML::NODE_COMMENT(), 5, 'NODE_COMMENT');
is(XML::PugiXML::NODE_PI(), 6, 'NODE_PI');
is(XML::PugiXML::NODE_DECLARATION(), 7, 'NODE_DECLARATION');
is(XML::PugiXML::NODE_DOCTYPE(), 8, 'NODE_DOCTYPE');
my $doc = XML::PugiXML->new;
$doc->load_string('<root>text</root>');
is($doc->root->type, XML::PugiXML::NODE_ELEMENT(), 'element type matches constant');
is($doc->root->first_child->type, XML::PugiXML::NODE_PCDATA(), 'pcdata type matches constant');
}
#--------------------------------------------------
# New format/parse constants
#--------------------------------------------------
{
ok(defined XML::PugiXML::FORMAT_INDENT_ATTRIBUTES(), 'FORMAT_INDENT_ATTRIBUTES defined');
ok(defined XML::PugiXML::PARSE_WS_PCDATA_SINGLE(), 'PARSE_WS_PCDATA_SINGLE defined');
my $doc = XML::PugiXML->new;
( run in 3.693 seconds using v1.01-cache-2.11-cpan-995e09ba956 )