Использование XML :: LibXML: Как создать пространства имен и дочерние элементы и заставить их работать вместе? - PullRequest
2 голосов
/ 01 марта 2010

Я пытаюсь сделать кое-что с FOAF и Perl. Я недоволен текущими решениями и хочу накатить свои. Пожалуйста, не ссылайтесь ни на один модуль, кроме XML::LibXML.

Для справки приведен фрагмент из файла FOAF

<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>
....

Теперь, за исключением пробелов, я пытаюсь воссоздать это с помощью XML::LibXML. Однако, к сожалению, я застрял на самой первой строчке. Это просто фокусируется на первой строке:

Я прочитал это, чтобы быть

  1. элемент RDF, в пространстве имен rdf объявляет
    1. атрибут rdf в пространстве имен xmlns со значением http://www.w3.org/1999/02/22-rdf-syntax-ns#
    2. атрибут rdfs в пространстве имен xmlns со значением http://www.w3.org/2000/01/rdf-schema#
    3. атрибут foaf в пространстве имен xmlns со значением http://xmlns.com/foaf/0.1/
    4. атрибут admin в пространстве имен xmlns со значением http://webns.net/mvcb/

Во-первых, вам нужен элемент rdf:RDF, это кажется хитрым. Читая документацию для XML::LibXML::Document Я нашел createElementNS(), но, похоже, это не делает то, что я хочу:

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"/>

Теперь я пытаюсь createElement('rdf:RDF'), и это работает! Я получил корневой элемент rdf:RDF. Это то, как мы должны создавать корневые элементы? Я просто читаю XML неправильно?

Теперь мне нужно создать атрибуты (объявления схемы). Я пробовал плохо документированные XML::LibXML::Document createAttributeNS, но они тоже не работали:

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' );

На самом деле я получаю эту ошибку: "не могу создать новое пространство имен для атрибута!" , что кажется противоречащим тому, что подразумевает название метода, и его описанию в документации: «Создает атрибут, связанный с пространством имен.»

Итак, я думаю, хорошо, я не могу create и attributeNS, может быть, тогда я смогу set и attributeNS. И на этот раз я перехожу к следующему документированному методу XML::LibXML::Element, который выглядит применимым: 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' );

На этот раз я получаю другую ошибку: "атрибут bad ns!" . Поэтому я проверяю некоторые из тестов и нахожу, что для этого требуется значение ключа атрибута, отличное от объявления пространства имен, чтобы делать то, что я хочу ... Что не то, что я хочу.

Вот несколько возможных комбинаций и выходов:

$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"/>

Кажется, ни один из * NS-методов не работает, хотя я знаю, что они связаны с пространствами имен XML. Наконец, я пробую не-NS версию:

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#"/>

У меня такое ужасное чувство, что я не правильно делаю . Я сделал это правильно? Как добавить дочерний элемент с DOM (не используя appendTextChild)?

Все это XML::LibXML очень плохо документировано, но, похоже, лучшее, что может предложить Perl для быстрого создания XML с DOM.

1 Ответ

6 голосов
/ 02 марта 2010

Из памяти (и отредактировано для корректности), вы делаете это так:

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;

То есть вам всегда нужно использовать URI пространства имен и добавлять объявления пространства имен к корневому узлу (это единственное место, где вы указываете префикс пространства имен - я думаю, что libxml будет изобретать свои собственные префиксы, если вы их не предоставите ). Очевидно, с точки зрения обслуживания было бы разумно поместить их в некоторые переменные.

Ваша окончательная (без пространств имен) версия будет работать до тех пор, пока вам не понадобится DOM в памяти для понимания пространства имен. Это работает, так как DOM уровня 1 не знает о пространствах имен: вы можете рассматривать пространства имен как обычные атрибуты, и документ все равно будет правильно сформирован (но, возможно, не будет правильно сформировано пространство имен).

Это работает до определенного момента, но префиксы пространства имен не соответствуют заданным. Обсуждение этого в другом месте .

...