Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

LibXML setNodeName error

by geddie2001 (Novice)
on Jun 26, 2017 at 14:48 UTC ( [id://1193581]=perlquestion: print w/replies, xml ) Need Help??

geddie2001 has asked for the wisdom of the Perl Monks concerning the following question:

I am attempting to write a perl script that reads an xsl document and changes every instance of <xsl:element name =" foobar"> to <foobar> (essentially stripping out the `xsl:element name` so that the code is easier to read)

Here is what I have in `test.xsl`:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Tr +ansform"> <xsl:element name="foo"> <xsl:value-of select="bar"/> </xsl:element> </xsl:stylesheet>
In `replaceelementname.pl`:
use strict; use warnings; use XML::LibXML; my $reader = XML::LibXML->load_xml(location => "test.xsl") or die "can +not read input file\n"; my $xpath_context = XML::LibXML::XPathContext->new($reader); foreach my $element_node ($xpath_context -> findnodes('//xsl:element') +) { my @attributes = $element_node -> attributes(); $element_node -> setNodeName($attributes[0]); }
The output when I run this script is:  bad name at replaceelementname.pl line 12 Any help would be greatly appreciated

Replies are listed 'Best First'.
Re: LibXML setNodeName error
by choroba (Cardinal) on Jun 26, 2017 at 16:26 UTC
    Several issues:
    • You can retrieve the name attribute directly, no need to search for the first attribute.
    • You need the attribute object in order to be able to remove the attribute, anyway.
    • Use the getValue method to retrieve the value of the attribute.
    • Minor: "reader" sounds like a XML::LibXML::Reader instance, not a normal DOM object.

    #!/usr/bin/perl use warnings; use strict; use XML::LibXML; my $xml = '<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Tr +ansform"> <xsl:element name="foo"> <xsl:value-of select="bar"/> </xsl:element> </xsl:stylesheet>'; my $dom = 'XML::LibXML'->load_xml(string => $xml) or die; my $xpc = 'XML::LibXML::XPathContext'->new($dom); for my $element ($xpc->findnodes('//xsl:element')) { my ($name_attr) = $xpc->findnodes('@name', $element); $element->removeAttribute('name'); $element->setNodeName($name_attr->getValue); } print $dom;
    ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
      This is close. The output is:
      <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" versi +on="1.0"> <xsl:foo> <xsl:value-of select="bar"/> </xsl:foo> </xsl:stylesheet>
      but what I was asking for was:
      <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" versi +on="1.0"> <foo> <xsl:value-of select="bar"/> </foo> </xsl:stylesheet>
      Everything you have done so far is great, thank you. If you can help me with this last piece, it will be perfect. I'm sorry for my ignorance. I have a lot to learn when it comes to Perl.

        I remember doing battle with XML::LibXML in a similar situation a few years ago. IIRC, what I found back then is that the problem is namespaces - note how the document originally has only one namespace (http://www.w3.org/1999/XSL/Transform) with only one namespace prefix (xsl). What you're asking to do is move the node into a completely different namespace, specifically in this case the default one. I remember not being able to find the appropriate combinations of API calls to achieve this so that the resulting XML looked clean, and playing around by adding $element->setNamespace(...); to choroba's code seems to confirm this, the best I can do so far is <foo xmlns="...">. It seemed that once a node has been created in a namespace, it's stuck there you can't get it back into the default namespace cleanly. Back then I ended up writing a routine to create a new node in the new namespace. Now since this was a while ago, it's possible I missed the appropriate API function, but even now going through the XML::LibXML docs and Googling a bit I still don't see an obvious way.

        <update2> Caveat: The below will completely ignore any other attributes in the element being replaced, so for example if you've got <xsl:element namespace=... or use-attribute-sets, that will be ignored and lost! </update2>

        Here's what my workaround would look like (based in part on choroba's code):

        use warnings; use strict; use XML::LibXML; my $xml = <<'END_XML'; <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Tr +ansform"> <xsl:element name="foo"> <xsl:value-of select="bar"/> </xsl:element> </xsl:stylesheet> END_XML my $doc = XML::LibXML->load_xml(string => $xml) or die; my $xpc = XML::LibXML::XPathContext->new($doc); for my $el ($xpc->findnodes('//xsl:element')) { my $newel = $doc->createElement($el->getAttribute('name')); $newel->appendChild($_) for $el->childNodes; $el->replaceNode($newel); } print $doc; __END__ <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" versi +on="1.0"> <foo> <xsl:value-of select="bar"/> </foo> </xsl:stylesheet>

        Update: Made description more accurate.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1193581]
Approved by Discipulus
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others browsing the Monastery: (3)
As of 2024-04-20 01:35 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found