2016-05-18 9 views
3

Mit diesem Code:nur - mit der Bedingung unter Verwendung von XML :: Twig

#!/usr/bin/env perl 
use 5.014; 
use warnings; 
use XML::Twig; 

my $twig = XML::Twig->parse(\*DATA); 
$twig->set_pretty_print('indented_a'); 

# 1st search 
# this prints OK the all <files> nodes where the <type> == 'release' 
$_->print for ($twig->findnodes('//type[string()="release"]/..')); 

# 2nd search  
# try to get first matched only 
my $latest = $twig->findnodes('(//type[string()="release"])[1]/..'); 
$latest->print; 

__DATA__ 
<root> 
    <files> 
     <type>beta</type> 
     <ver>3.0</ver> 
    </files> 
    <files> 
     <type>alpha</type> 
     <ver>3.0</ver> 
    </files> 
    <files> 
     <type>release</type> 
     <ver>2.0</ver> 
    </files> 
    <files> 
     <type>release</type> 
     <ver>1.0</ver> 
    </files> 
</root> 

Die obigen Drucke

<files> 
    <type>release</type> 
    <ver>2.0</ver> 
    </files> 
    <files> 
    <type>release</type> 
    <ver>1.0</ver> 
    </files> 
error in xpath expression (//type[string()="release"])[1]/.. around (//type[string()="release"])[1]/.. at /opt/anyenv/envs/plenv/versions/5.24.0/lib/perl5/site_perl/5.24.0/XML/Twig.pm line 3648. 

Die wollte Ausgabe von der zweiten Suche

<files> 
     <type>release</type> 
     <ver>2.0</ver> 
    </files> 

z der erste <files> Knoten, wo die <type> eq 'release'.

Laut this answer sollte der verwendete XPath-Ausdruck (//type[string()="release"])[1]/..' funktionieren, aber anscheinend habe ich wieder etwas Wichtiges verpasst.

Könnte jemand bitte helfen?

Antwort

4

XML::Twig unterstützt nicht die vollständige XPath-Syntax. Die Dokumentation für die get_xpath Methode (die gleichen wie findnodes) sagt diese

Eine Teilmenge der XPath abgekürzten Syntax bedeckt ist:

tag 
tag[1] (or any other positive number) 
tag[last()] 
tag[@att] (the attribute exists for the element) 
tag[@att="val"] 
tag[@att=~ /regexp/] 
tag[att1="val1" and att2="val2"] 
tag[att1="val1" or att2="val2"] 
tag[string()="toto"] (returns tag elements which text (as per the text method) 
        is toto) 
tag[string()=~/regexp/] (returns tag elements which text (as per the text 
         method) matches regexp) 
expressions can start with/(search starts at the document root) 
expressions can start with . (search starts at the current element) 
// can be used to get all descendants instead of just direct children 
* matches any tag 

So Teilausdrücke in Klammern werden nicht unterstützt, und Sie kann nur ein einzelnes Prädikat angeben

Es ist auch wichtig, dass im skalaren Kontext findnodes immer nur die Anzahl der gefundenen Knoten zurückgibt. Sie müssen es in Listenkontext verwenden, um den Knoten selbst abrufen, was bedeutet, dass eine einfachere Art und Weise nur das erste passende Element zu finden ist

my ($latest) = $twig->findnodes('//type[string()="release"]/..'); 

die

funktioniert gut zu schreiben Wenn Sie wirklich die volle Leistung benötigen von XPath, dann können Sie stattdessen XML::Twig::XPath verwenden. Dieses Modul verwendet entweder XML::XPath oder das ausgezeichnete XML::XPathEngine, um die vollständige XPath-Syntax durch Überladen von findnodes bereitzustellen. (Die anderen Methoden get_xpath und find_nodes weiterhin die reduzierte XML::Twig Variation verwenden.)

findnodes in Skalarkontext jetzt gibt ein Objekt, das Array XML::XPathEngine::NodeSet Indexierungsüberlastet.So können Sie

my $latest = $twig->findnodes('//type[string()="release"]/..'); 
$latest->[0]->print; 

oder nur

my ($latest) = $twig->findnodes('//type[string()="release"]/..'); 

wie oben schreiben.

Schließlich würde ich es vorziehen /root/files[type[string()="release"]] den Vorzug vor der Hinter parent::node() zu sehen, aber das ist rein persönliche

+0

JA! Mit 'XML :: Twig :: XPath' und den' my ($ neustest) = $ twig-> findnodes ('/ root/files [type [string() = "release"]]) "löst ich meine Bedürfnisse . Vielen Dank! ;) – cajwine

+0

@cajwine: Ich hoffe, ich habe klargestellt, dass wenn man nur ein Prädikat verwendet, wie 'my ($ latest) = $ twig-> findnodes ('/ root/files/type [string() =" release " ]/.. ') ', dann funktioniert der Standard' XML :: Twig' gut – Borodin

+0

Ja, nur beides ausprobiert. Für die Verwendung der '/ root/files [type [string() =" release "]]' '(von Ihrer letzten Aussage) brauche ich den XPath. Und für '/ root/files/type [string() =" release "]/..' reicht der einfache 'XML :: Twig'. Wunderbare Antwort! ;) – cajwine

2

XML :: Twig unterstützt nicht den gesamten XPath. Der Ausdruck funktioniert korrekt in XML::LibXML.

Sie können die Struktur selbst in Perl gehen:

my $latest = ($twig->findnodes('//type[string()="release"]'))[0]->parent; 
+0

perl-walking - ja - aber es sagt, 'Die Methode kann nicht„Eltern“auf einem undefinierten value' nennen wenn hier nicht "Release" Typ ist (zum Beispiel nur "Beta") - so muss der Rückgabewert getestet werden. Deshalb habe ich versucht den (erweiterten) Xpath zu benutzen. Vielen Dank. :) – cajwine

3

XML :: Zweig nicht alle XPath nicht unterstützt, aber XML :: Twig :: XPath der Fall ist.

So use XML::Twig::XPath;, dann my $twig = XML::Twig::XPath->parse(... und voilà ... können Sie jetzt bekommen, um die Festsetzung der $latest=... Linie, die sein sollte:

my $latest = ($twig->findnodes('(//type[string()="release"])[1]/..'))[0]; 

(so, wie Sie es haben $ neueste ist ein XML::XPathEngine::NodeSet, müssen Sie nimm das erste Element dieser Menge).

+0

Dies ist ziemlich off-topic, aber es wäre schön, wenn 'XML :: Twig :: XPath' eine Möglichkeit hätte, welches Helper-Modul zu verwenden, wenn beide installiert sind, auf die gleiche Art wie' Text :: CSV' tut. Oder zumindest einen Weg zu entdecken, welcher ausgewählt wurde. Es mag zunächst nur darum gehen, 'my $ XPATH' in' unser $ XPATH' zu ändern? – Borodin

+1

Keine Frage, nur ein "Dankeschön" für das nette 'XML :: Twig' Paket! :) – cajwine

+0

@borodin XML :: XPathEngine wird verwendet, wenn vorhanden. XML :: XPath ist nur eine Option, weil es in der Vergangenheit die erste war, die verwendet wurde, bevor ich den XPath-Teil zum Erstellen von XML :: XPathEngine abgezweigt habe. – mirod

Verwandte Themen