Search code examples
xmlperlxml-libxml

How to generate an unary XML tag with Perl LibXML::XML


I got an XML template which looks like this (excerpt):

<isSomething>_xxx_</isSomething>
<someValue>_xxx_</someValue>

My code is going through the template and replacing the _xxx_ placeholders using XML::LibXML's findnode and setData methods respectively.

In the end it looks like this:

<isSomething>true</isSomething>
<someValue>123.45</someValue>

This does the job, however the recipient system would be more happy if the isSomething tag would look like this in case its true:

<isSomething/>

And would be completely omitted in case it's false. Is there a way to do that with XML::LibXML or should i apply the mighty regex power on the string output?


Solution

  • To remove a node, use removeChild. To produce an empty element, remove its text child:

    #! /usr/bin/perl
    use warnings;
    use strict;
    
    use XML::LibXML;
    
    my $xml = 'XML::LibXML'->load_xml( string => << '__XML__');
    <root>
      <node id="a">
        <isSomething>_xxx_</isSomething>
        <someValue>_xxx_</someValue>
      </node>
      <node id="b">
        <isSomething>_xxx_</isSomething>
        <someValue>_xxx_</someValue>
      </node>
    </root>
    __XML__
    
    my %nodes = ( a => [ 'true',  1 ],
                  b => [ 'false', 2 ],
                );
    
    for my $node ($xml->documentElement->findnodes('//node')) {
        my $id = $node->{id};
        my ($is_something, $value) = @{ $nodes{$id} };
    
        my ($s) = $node->findnodes('isSomething[.="_xxx_"]');
        if ('true' eq $is_something) {
            my ($text) = $s->findnodes('text()');
            $s->removeChild($text);
        } else {
            $node->removeChild($s);
        }
    
        my ($v) = $node->findnodes('someValue/text()[.="_xxx_"]');
        $v->setData($value);
    }
    print $xml;