XML-PugiXML

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN


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)

PugiXML.xs  view on Meta::CPAN

    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

PugiXML.xs  view on Meta::CPAN

    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 )