开发者

Using XML::LibXML: How do I create namespaces and child elements and make them work together?

I'm trying to do some stuff with FOAF and Perl. I'm unhappy with the current solutions and I want to roll my own. Please do not reference any module other than XML::LibXML.

For reference here is a snippet from a FOAF file

<rdf:RDF
      xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
      xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
      xmlns:foaf="http://xmlns.com/foaf/0.1/"
      xmlns:admin="http://webns.net/mvcb/">

  <foaf:Person rdf:ID="me">
  <foaf:name>Evan Carroll</foaf:name>
....

Now, excluding whitespace, I'm trying to recreate this with XML::LibXML. However, I'm unfortunately stuck on the very first line. This just focuses on the first line:

I read this to be

  1. element RDF, in namespace rdf declares
    1. attribute rdf in namespace xmlns with value http://www.w3.org/1999/02/22-rdf-syntax-ns开发者_Python百科#
    2. attribute rdfs in namespace xmlns with value http://www.w3.org/2000/01/rdf-schema#
    3. attribute foaf in namespace xmlns with value http://xmlns.com/foaf/0.1/
    4. attribute admin in namespace xmlns with value http://webns.net/mvcb/

Firstly you need an element rdf:RDF, this seems to be tricky. Reading the documentation for XML::LibXML::Document I found createElementNS() but this doesn't seem to do what I want:

use XML::LibXML;
my $doc = XML::LibXML::Document->new( '1.0', 'UTF-8' );
my $foaf = $doc->createElementNS( 'RDF', 'rdf' );
print $foaf->toString; # prints <rdf xmlns="RDF"/>

Now, I try createElement('rdf:RDF') and it works! I got the root element rdf:RDF. Is this how we're supposed to create root elements? Am I just reading XML wrong?

Now, I need to create the attributes (schema declarations). I tried the poorly documented XML::LibXML::Document's createAttributeNS but it didn't work either:

my $doc = XML::LibXML::Document->new( '1.0', 'UTF-8' );
my $foaf = $doc->createElement( 'rdf:RDF' );
$doc->createAttributeNS( 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'rdf' );

In fact I get this error: "can't create a new namespace on an attribute!" which seems contradictory to what the method name implies, and to its description on the docs: "Creates an Attribute bound to a namespace."

So, I figure, ok I can't create an attributeNS, maybe I can set an attributeNS then. And, I proceed with the next documented method this time on XML::LibXML::Element that looks applicable: setAttributeNS.

my $doc = XML::LibXML::Document->new( '1.0', 'UTF-8' );
my $foaf = $doc->createElement( 'rdf:RDF' );
$foaf->setAttributeNS( 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'rdf' );

This time I get a different error: "bad ns attribute!". So I review some of the tests, and find this one requires a attribute key-value other than the namespace declaration to do what I want.. Which isn't what I want.

Here are some possible combinations and outputs:

$foaf->setAttributeNS( http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'rdf:', undef );
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" rdf:=""/>

$foaf->setAttributeNS( 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'rdf:foo', 'bar' );
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" rdf:foo="bar"/>

It seems none of the *NS methods work, even though I know they are related to XML namespaces. Finally, I try the non-NS version:

my $doc = XML::LibXML::Document->new( '1.0', 'UTF-8' );
my $foaf = $doc->createElement( 'rdf:RDF' );
$foaf->setAttribute( 'xmlns:rdf', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#' );
print $foaf->toString; # <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"/>

I get this horrible feeling that I'm not doing this right. Did I do this right? How do I add a child Element with the DOM (not using appendTextChild)?

This whole XML::LibXML is very poorly documented but seems to be the best Perl has to offer for fast XML creation with a DOM.


From memory (and edited for correctness), you do it like this:

use XML::LibXML;
my $doc = XML::LibXML::Document->new( '1.0', 'UTF-8' );
my $foaf = $doc->createElementNS( 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'RDF' );
$doc->setDocumentElement( $foaf );
$foaf->setNamespace( 'http://www.w3.org/1999/02/22-rdf-syntax-ns#' , 'rdf', 1 );
$foaf->setNamespace( 'http://www.w3.org/2000/01/rdf-schema#' , 'rdfs', 0 );
$foaf->setNamespace( 'http://xmlns.com/foaf/0.1/' , 'foaf', 0 );
$foaf->setNamespace( 'http://webns.net/mvcb/' , 'admin', 0 );
my $node = $doc->createElementNS( 'http://xmlns.com/foaf/0.1/', 'Person');
$foaf->appendChild($node);
$node->setAttributeNS( 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'ID', 'me');
my $node2 = $doc->createElementNS( 'http://xmlns.com/foaf/0.1/', 'name');
$node2->appendTextNode('Evan Carroll');
$node->appendChild($node2);
print $doc->toString;

That is, you always need to use the namespace URIs and add the namespace declarations to your root node (this is the only place you specify the namespace prefix - I think libxml will invent its own prefixes if you don't provide them). It would obviously be sensible from the maintenance point of view to put these in some variables.

Your final (non-namespaced) version will work as long as you don't need the in-memory DOM to be namespace aware. This works as the Level 1 DOM does not know about namespaces: you can treat namespaces as ordinary attributes and the document will still be well formed (but possibly not namespace well formed).

This works to a point, but the namespace prefixes are not as given. A discussion of this is elsewhere.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜