XML :: LibXML - найти (и зарегистрировать) пространства имен, используемые в документе - PullRequest
1 голос
/ 12 сентября 2011

У меня проблема с анализом Xml от стороннего производителя; есть несколько разных версий отправляемого им xml с перекрывающимися пространствами имен;


Версия 1

Foo = "урна: bar.org/version-1"

далеко = "урна: gle.org/version-1"


Версия 2

Foo = "бар: a.org/version-2"

далеко = "гле: a.org/version-2"


Ранее (когда мне приходилось обрабатывать только одну версию) я регистрировал пространства имен из жестко закодированного хэша, например:

#!/usr/bin/perl

use strict;
use XML::LibXML ;

my $cfg->{namespace} = {
    foo=>"urn:bar.org/version-1",
    far=>"urn:gle.org/version-1",
};

my $parser = XML::LibXML->new({recover => '1'});

my $doc = $parser->parse_string($inputHash->{$key}->{xml});

my $xc = XML::LibXML::XPathContext->new( $doc->documentElement() );

for my $ns (keys %{$cfg->{namespace}})
{
    $xc->registerNs($ns => $cfg->{namespace}->{$ns});
}

очевидно, это будет работать только на версии 1 ..

Изучил документацию LibXML, но не может найти способ извлечь пространства имен, используемые в документе, и зарегистрировать их; кто-нибудь может дать мне указатель, пожалуйста?

Псевдо-док:

<?xml version="1.0"?>
<foo:Parent xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:foo="bar:a.org/version-2">
    <far:Child xmlns:gle="gle:a.org/version-2">
    {horrific structure with more ns declarations all of which need registering}
    </far:Child>
</foo:Parent>

Ответы [ 3 ]

7 голосов
/ 12 сентября 2011

Исходя из этого ответа , я бы использовал выражение //namespace::* XPath, чтобы найти пространства имен. Попробуйте этот пример кода и посмотрите, поможет ли он:

use strict;
use warnings;
use XML::LibXML;

my $cfg;
my $xml = XML::LibXML->load_xml( location => <your xml>);
foreach my $node ($xml->findnodes('//namespace::*')) {

    $cfg->{namespace}{$node->getLocalName()} = $node->getValue();
}

Это должно заполнить ваш хэш тем, что вам нужно. Кроме того, это выражение XPath не всегда поддерживается. Я протестировал это с использованием LibXML 1.70 и dll версии 20703, и у меня это сработало.

0 голосов
/ 12 сентября 2011

вы итерируете по дереву, ища пространства имен, пока не найдете его, скажем, используя getElementsByTagName или что-то подобное

#!/usr/bin/perl --

use strict;
use warnings;

use XML::Twig;

my $xml = <<'__XML__';
<?xml version="1.0"?>
<!-- initially, the default namespace is "books" -->
<book xmlns='urn:loc.gov:books'
      xmlns:isbn='urn:ISBN:0-395-36341-6'>
    <title>Cheaper by the Dozen</title>
    <isbn:number>1568491379</isbn:number>
    <notes>
      <!-- make HTML the default namespace for some commentary -->
      <p xmlns='urn:w3-org-ns:HTML'>
          This is a <i>funny</i> book!
      </p>
    </notes>
</book>
__XML__

{
    my $t = XML::Twig->new(
        start_tag_handlers => {
            _all_ => sub {
                my $tag = $_[1]->tag;
                my $nsp = $_[1]->ns_prefix||'';
                print "$tag => $nsp\n";
                print map {
                    join ' ',
                        "\t", $_,' => ', $_[1]->att($_), "\n"
                    } grep /xmlns/ , $_[1]->att_names ;
            },
        },
    );
    $t->parse($xml);
}
__END__
book =>
         xmlns  =>  urn:loc.gov:books
         xmlns:isbn  =>  urn:ISBN:0-395-36341-6
title =>
isbn:number => isbn
notes =>
p =>
         xmlns  =>  urn:w3-org-ns:HTML
i =>
0 голосов
/ 12 сентября 2011

Я бы попробовал XML :: LibXML :: Node :: lookupNamespaceURI, то есть

$cfg->{namespace}->{foo} = $node->lookupNamespaceURI("foo");

для некоторого достаточно глубокого узла, чтобы он имел объявление.Самым простым узлом для использования был бы документ, но он не будет работать, если пространство имен не объявлено в верхней части;самый общий подход будет проходить через все узлы и обрабатывать случай повторного использования одного и того же префикса для разных пространств имен в соответствии с логикой вашего приложения ...

...