วิธีใช้ XML :: XPath เพื่อรับโหนดพาเรนต์

ฉันต้องการแยกวิเคราะห์ไฟล์ XML โดยใช้ xPaths หลังจากได้รับโหนดแล้ว ฉันอาจต้องทำการค้นหา xPath บนโหนดหลัก รหัสปัจจุบันของฉันที่ใช้ XML::XPath คือ:

my $xp = XML::XPath->new(filename => $XMLPath);
# get all foo or foos node with a name
my $Foo = $xp->find('//foo[name] | //foos[name]');
if (!$Foo->isa('XML::XPath::NodeSet') || $Foo->size() == 0) {
    # no foo found
    return undef;
} else {
    # go over each and get its bar node
    foreach my $context ($Foo->get_nodelist) {
        my $FooName = $context->find('name')->string_value;
        $xp = XML::XPath->new( context => $context );
        my $Bar = $xp->getNodeText('bar');
        if ($Bar) {
            print "Got $FooName with $Bar\n";
        } else {
            # move up the tree to get data from parent
            my $parent = $context->getParentNode;
            print $parent->getNodeType,"\n\n";
        }
    }
}

เป้าหมายของฉันคือการได้รับแฮชของชื่อองค์ประกอบ foo และค่าโหนดลูกของ bar หาก foo ไม่มีโหนดของแท่ง ก็ควรได้รับจากโหนดแม่ของ foo หรือ foos

สำหรับ XML นี้:

<root>
    <foos>
        <bar>GlobalBar</bar>
        <foo>
            <name>number1</name>
            <bar>bar1</bar>
        </foo>
        <foo>
            <name>number2</name>
        </foo>
    </foos>
</root>

ฉันคาดหวัง:

number1->bar1 
number2->GlobalBar

เมื่อใช้โค้ดข้างต้น ฉันได้รับข้อผิดพลาดเมื่อพยายามรับโหนดหลัก:

ไม่สามารถเรียกเมธอด "getNodeType" กับค่าที่ไม่ได้กำหนดได้

ความช่วยเหลือใด ๆ จะได้รับการชื่นชมมาก!


person Dror    schedule 08.06.2009    source แหล่งที่มา


คำตอบ (2)


ตามที่ Chas กล่าวไว้ คุณไม่ควรสร้างวัตถุ XML::XPath ที่สอง (เอกสารกล่าวถึงสิ่งนี้ด้วย) คุณสามารถส่งผ่านบริบทเป็นพารามิเตอร์ที่สองของเมธอด find* หรือเพียงแค่เรียกเมธอดบนโหนดบริบท เหมือนที่คุณทำเพื่อรับ $FooName

นอกจากนี้คุณยังมีการเรียกใช้เมธอดบางอย่างที่ไม่ได้ทำในสิ่งที่คุณคิด (getNodeType จะไม่ส่งคืนชื่อองค์ประกอบ แต่เป็นตัวเลขที่แสดงถึงประเภทโหนด)

โดยรวมแล้วโค้ดที่อัปเดตด้านล่างดูเหมือนว่าจะให้สิ่งที่คุณต้องการ:

#!/usr/bin/perl

use strict;
use warnings;

use XML::XPath;

my $xp = XML::XPath->new(filename => "$0.xml");
# get all foo or foos node with a name
my $Foo = $xp->find('//foo[name] | //foos[name]');
if (!$Foo->isa('XML::XPath::NodeSet') || $Foo->size() == 0) {
    # no foo found
    return undef;
} else {
    # go over each and get its bar node
    foreach my $context ($Foo->get_nodelist) {
        my $FooName = $context->find('name')->string_value;
        my $Bar = $xp->findvalue('bar', $context); # or $context->findvalue('bar');
        if ($Bar) {
                print "Got $FooName with $Bar\n";
        } else {
                # move up the tree to get data from parent
                my $parent = $context->getParentNode;
                print $parent->getName,"\n\n";
        }
    }
}

สุดท้ายนี้ คำเตือน: XML::XPath ไม่ได้รับการดูแลอย่างดี และคุณน่าจะดีกว่าถ้าใช้ XML::LibXML แทน รหัสจะคล้ายกันมาก

person mirod    schedule 08.06.2009
comment
ขอบคุณ - ตอนนี้มันทำงานได้ดีแล้ว ฉันจะดู XML:LibXML ตามที่คุณแนะนำ - person Dror; 09.06.2009

คุณจะเห็นข้อผิดพลาดนั้นเมื่อคุณพยายามเรียกใช้เมธอดบน undef สาเหตุที่พบบ่อยที่สุดในการเรียกเมธอดบน undef คือไม่สามารถตรวจสอบว่าเมธอด Constructor สำเร็จหรือไม่ เปลี่ยน

$xp = XML::XPath->new( context => $context );

to be

$xp = XML::XPath->new( context => $context )
    or die "could not create object with args ( context => '$context' )";
person Chas. Owens    schedule 08.06.2009
comment
ขอบคุณสำหรับคำตอบ แต่นี่ไม่ใช่ปัญหา ฉันรู้ว่าฉันได้รับข้อความแสดงข้อผิดพลาดจากการพยายามเรียกใช้เมธอด undef คำถามคือฉันทำอะไรผิด - เหตุใดฉันจึงไม่สามารถรับโหนดพาเรนต์ได้ อย่างไรก็ตาม ตัวสร้างก็โอเค ฉันใช้โค้ดของคุณแล้ว แต่ไม่ได้รับข้อความใด ๆ - person Dror; 08.06.2009
comment
ในทางกลับกัน ฉันจะบอกว่าปัญหาของคุณคือคุณกำลังทำลายวัตถุดั้งเดิมโดยมอบหมายให้ $xp เป็นครั้งที่สอง - person Chas. Owens; 08.06.2009
comment
ฉันยังใหม่กับ Perl คุณสามารถให้ตัวอย่างวิธีที่ถูกต้องในการจัดการงานปัจจุบันได้หรือไม่? - person Dror; 08.06.2009