XPath มี (text (), 'some string') ไม่ทำงานเมื่อใช้กับโหนดที่มีโหนดย่อย Text มากกว่าหนึ่งโหนด


258

ฉันมีปัญหาเล็กน้อยกับ Xpath ที่มี dom4j ...

ให้บอกว่า XML ของฉันคือ

<Home>
    <Addr>
        <Street>ABC</Street>
        <Number>5</Number>
        <Comment>BLAH BLAH BLAH <br/><br/>ABC</Comment>
    </Addr>
</Home>

ให้บอกว่าฉันต้องการค้นหาโหนดทั้งหมดที่มี ABC ในข้อความที่ระบุองค์ประกอบราก ...

ดังนั้น xpath ที่ฉันต้องการจะเขียนก็คือ

//*[contains(text(),'ABC')]

อย่างไรก็ตามนี่ไม่ใช่สิ่งที่ Dom4j ส่งคืน .... นี่คือปัญหา dom4j หรือความเข้าใจของฉันว่า xpath ทำงานอย่างไร เนื่องจากข้อความค้นหานั้นส่งคืนเฉพาะองค์ประกอบถนนและไม่ใช่องค์ประกอบความคิดเห็น

DOM ทำให้องค์ประกอบความคิดเห็นเป็นองค์ประกอบประกอบที่มีสี่แท็กสองแท็ก

[Text = 'XYZ'][BR][BR][Text = 'ABC'] 

ฉันจะสมมติว่าแบบสอบถามควรจะคืนองค์ประกอบเพราะมันควรจะหาองค์ประกอบและเรียกใช้มีอยู่ในนั้น แต่มันไม่ ...

แบบสอบถามต่อไปนี้จะส่งคืนองค์ประกอบ แต่จะส่งกลับมากกว่าองค์ประกอบนั้นก็จะส่งกลับองค์ประกอบหลักเช่นกัน ... ซึ่งเป็นสิ่งที่ไม่พึงประสงค์สำหรับปัญหา ...

//*[contains(text(),'ABC')]

ไม่มีใครรู้แบบสอบถาม xpath ที่จะกลับมาเพียงองค์ประกอบ<Street/>และ<Comment/>?


เท่าที่ฉันบอกได้ก็//*[contains(text(),'ABC')]แค่คืน<Street>องค์ประกอบเท่านั้น มันไม่ได้กลับบรรพบุรุษของใด ๆหรือ<Street> <Comment>
Ken Bloom

คำตอบ:


706

<Comment>แท็กมีโหนดข้อความสองและสอง<br>โหนดเป็นเด็ก

นิพจน์ xpath ของคุณคือ

//*[contains(text(),'ABC')]

เพื่อทำลายมัน

  1. * เป็นตัวเลือกที่ตรงกับองค์ประกอบใด ๆ (เช่นแท็ก) - มันจะส่งกลับชุดโหนด
  2. []มีเงื่อนไขที่ดำเนินการในแต่ละโหนดแต่ละโหนดในชุดว่า มันตรงกับถ้าใด ๆ ของแต่ละโหนดมันทำงานตรงกับเงื่อนไขภายในวงเล็บ
  3. text()เป็นตัวเลือกที่ตรงกับโหนดข้อความทั้งหมดที่เป็นลูกของโหนดบริบท - มันส่งกลับชุดโหนด
  4. containsเป็นฟังก์ชั่นที่ทำงานกับสตริง ถ้ามันจะถูกส่งผ่านชุดโหนดชุดโหนดจะถูกแปลงเป็นสตริงโดยกลับสตริงค่าของโหนดในโหนชุดที่เป็นครั้งแรกในการสั่งซื้อเอกสาร ดังนั้นจึงสามารถจับคู่เพียงโหนดข้อความของคุณครั้งแรกใน<Comment>องค์ประกอบ - BLAH BLAH BLAHคือ เนื่องจากนั่นไม่ตรงกันคุณจะไม่ได้รับ<Comment>ผลลัพธ์

คุณต้องเปลี่ยนสิ่งนี้เป็น

//*[text()[contains(.,'ABC')]]
  1. * เป็นตัวเลือกที่ตรงกับองค์ประกอบใด ๆ (เช่นแท็ก) - มันจะส่งกลับชุดโหนด
  2. ด้านนอก[]เป็นเงื่อนไขที่ทำงานกับแต่ละโหนดในชุดโหนด - ที่นี่มันทำงานกับแต่ละองค์ประกอบในเอกสาร
  3. text()เป็นตัวเลือกที่ตรงกับโหนดข้อความทั้งหมดที่เป็นลูกของโหนดบริบท - มันส่งกลับชุดโหนด
  4. ด้านใน[]เป็นเงื่อนไขที่ทำงานกับแต่ละโหนดในชุดนั้น - ที่นี่แต่ละโหนดข้อความ แต่ละโหนดข้อความแต่ละจุดเริ่มต้นสำหรับเส้นทางใด ๆ ในวงเล็บและยังสามารถเรียกอย่างชัดเจนว่า.ภายในวงเล็บ มันตรงกับถ้าใด ๆ ของแต่ละโหนดมันทำงานตรงกับเงื่อนไขภายในวงเล็บ
  5. containsเป็นฟังก์ชั่นที่ทำงานกับสตริง ที่นี่จะถูกส่งผ่านโหนดข้อความแต่ละรายการ ( .) เนื่องจากมันถูกส่งผ่านโหนดข้อความที่สองใน<Comment>แท็กทีละรายการมันจะเห็น'ABC'สตริงและสามารถจับคู่ได้

1
น่ากลัวฉันนิด ๆ หน่อย ๆ ของ xpath noob ดังนั้นขอผมได้นะ text () เป็นฟังก์ชั่นที่ใช้สำนวนประกอบด้วย (., 'ABC') มีโอกาสที่คุณจะอธิบายได้ไหมว่าฉันไม่ทำแบบนี้ สิ่งที่โง่อีกครั้ง;)
Mike Milkin

28
ฉันได้แก้ไขคำตอบของฉันเพื่อให้คำอธิบายที่ยาว ฉันไม่รู้จริงๆเกี่ยวกับ XPath มากนัก - ฉันเพิ่งทดลองเล็กน้อยจนกว่าฉันจะเจอชุดค่าผสมนั้น เมื่อฉันมีการทำงานร่วมกันฉันเดาว่าเกิดอะไรขึ้นและดูในมาตรฐาน XPathเพื่อยืนยันสิ่งที่ฉันคิดว่าเกิดขึ้นและเขียนคำอธิบาย
Ken Bloom

2
คุณจะทำให้การค้นหาแบบนี้เล็กลงหรือไม่
แซค

@ แซค: โปรดตั้งคำถามนี้ใหม่
user1129682

1
ฉันรู้ว่านี้เป็นหัวข้อเก่า //*[contains(., 'ABC')]แต่ทุกคนสามารถแสดงความคิดเห็นในกรณีที่มีความแตกต่างพื้นฐานควรมีบางกรณีการทดสอบอย่างง่ายระหว่างคำตอบที่ได้รับจากเคนบลูมและ ฉันมักจะใช้รูปแบบที่ได้รับจาก Mike Milkin โดยคิดว่ามันเหมาะสมกว่า แต่การทำcontainsตามบริบทปัจจุบันดูเหมือนว่าจริง ๆ แล้วเป็นสิ่งที่ฉันต้องการบ่อยกว่า
knickum

7

[contains(text(),'')]ผลตอบแทนที่แท้จริงหรือเท็จเท่านั้น มันจะไม่ส่งคืนผลลัพธ์องค์ประกอบใด ๆ


สิ่งนี้จะไม่ทำงานถ้าฉันมี '' หรือ '' เราจะตัดได้อย่างไร?
shareef

contains(text(),'JB-')ไม่ทำงาน! conatainsรับสองสายเป็นอาร์กิวเมนต์ - contains(**string**, **string**)! ข้อความ () ไม่ใช่สตริงเป็นฟังก์ชัน!
AtachiShadow

6

เอกสาร XML:

<Home>
    <Addr>
        <Street>ABC</Street>
        <Number>5</Number>
        <Comment>BLAH BLAH BLAH <br/><br/>ABC</Comment>
    </Addr>
</Home>

การแสดงออก XPath:

//*[contains(text(), 'ABC')]

//*ตรงกับองค์ประกอบที่สืบทอดใด ๆของโหนดรูตต นั่นคือองค์ประกอบใด ๆ ยกเว้นโหนดรูท

[...]เป็นคำกริยาก็กรองโหนดชุด ส่งคืนโหนดซึ่ง...คือtrue:

เพรดิเคตกรองชุดโหนด [... ] เพื่อสร้างชุดโหนดใหม่ สำหรับแต่ละโหนดในชุดโหนดที่จะถูกกรอง PredicateExpr จะถูกประเมิน [... ]; ถ้า PredicateExpr หาค่าเป็นจริงสำหรับโหนดนั้นโหนดจะรวมอยู่ในโหนดชุดใหม่ มิฉะนั้นจะไม่รวม

contains('haystack', 'needle')ผลตอบแทนtrueถ้าhaystack มี needle :

ฟังก์ชั่น: บูลีนมี (สตริงสตริง)

ฟังก์ชั่นการบรรจุส่งกลับจริงถ้าสตริงอาร์กิวเมนต์แรกมีสตริงอาร์กิวเมนต์ที่สองและมิฉะนั้นกลับเท็จ

แต่contains()รับสตริงเป็นพารามิเตอร์ตัวแรก และมันก็ผ่านโหนด ในการจัดการกับทุก ๆ โหนดหรือชุดโหนดที่ส่งผ่านเป็นพารามิเตอร์แรกจะถูกแปลงเป็นสตริงโดยstring()ฟังก์ชัน:

อาร์กิวเมนต์ถูกแปลงเป็นประเภทสตริงราวกับว่าโดยการเรียกฟังก์ชันสตริง

string()กลับมาทำงานstring-valueของโหนดแรก :

ชุดโหนดถูกแปลงเป็นสตริงโดยส่งคืนค่าสตริงของโหนดในชุดโหนดที่เป็นลำดับแรกในเอกสาร หากชุดโหนดว่างเปล่าสตริงว่างจะถูกส่งกลับ

string-valueของโหนดองค์ประกอบ :

สตริง - ค่าของโหนดองค์ประกอบคือการต่อกันของสตริง - ค่าของการสืบทอดโหนดข้อความทั้งหมดของโหนดองค์ประกอบในลำดับเอกสาร

string-valueของโหนดข้อความ :

สตริง - ค่าของโหนดข้อความคือข้อมูลตัวละคร

ดังนั้นโดยทั่วไปstring-valueคือข้อความทั้งหมดที่มีอยู่ในโหนด (การต่อเชื่อมโหนดข้อความที่สืบทอดทั้งหมด)

text() เป็นการทดสอบโหนดที่ตรงกับโหนดข้อความใด ๆ :

ข้อความทดสอบโหนด () เป็นจริงสำหรับโหนดข้อความใด ๆ ตัวอย่างเช่น child :: text () จะเลือกชายด์โหนดข้อความของโหนดบริบท

มีที่กล่าวว่า//*[contains(text(), 'ABC')]ตรงกับองค์ประกอบใด ๆ ( แต่โหนดราก) ABCที่โหนดข้อความแรกที่มี เนื่องจากtext()ส่งคืนชุดโหนดที่มีโหนดข้อความลูกทั้งหมดของโหนดบริบท (สัมพันธ์กับที่นิพจน์ได้รับการประเมิน) แต่contains()รับเพียงอันแรกเท่านั้น ดังนั้นสำหรับเอกสารด้านบนเส้นทางที่ตรงกับStreetองค์ประกอบ

นิพจน์ต่อไปนี้//*[text()[contains(., 'ABC')]]ตรงกับองค์ประกอบใด ๆ ( แต่โหนดราก) ABCที่มีอย่างน้อยหนึ่งโหนดข้อความเด็กที่มี .แสดงถึงโหนดบริบท ในกรณีนี้มันเป็นโหนดข้อความลูกขององค์ประกอบใด ๆ ยกเว้นโหนดรูท ดังนั้นสำหรับเอกสารด้านบนเส้นทางที่ตรงกับStreetและCommentองค์ประกอบ

ตอนนี้//*[contains(., 'ABC')]ตรงกับองค์ประกอบใด ๆ (แต่โหนดรูท) ที่มีABC(ในการเรียงต่อกันของโหนดข้อความลูกหลาน) สำหรับเอกสารด้านบนนั้นตรงกับHomethe Addr, the Street, และCommentองค์ประกอบ ด้วยเหตุนี้จึง//*[contains(., 'BLAH ABC')]ตรงกับHomethe Addr, และCommentองค์ประกอบ


0

ฉันใช้เวลาสักครู่ แต่ในที่สุดก็คิดออก xpath ที่กำหนดเองที่มีข้อความด้านล่างนี้ทำงานได้อย่างสมบูรณ์แบบสำหรับฉัน

//a[contains(text(),'JB-')]

2
contains(text(),'JB-')ไม่ทำงาน! conatainsรับสองสายเป็นอาร์กิวเมนต์ - contains(**string**, **string**)! ข้อความ () ไม่ใช่สตริงเป็นฟังก์ชัน!
AtachiShadow

0

คำตอบที่ได้รับการยอมรับจะส่งคืนโหนดหลักทั้งหมดด้วย ในการรับเฉพาะโหนดจริงด้วย ABC แม้ว่าสตริงจะอยู่หลัง
:

//*[text()[contains(.,'ABC')]]/text()[contains(.,"ABC")]

0
//*[text()='ABC'] 

ผลตอบแทน

<street>ABC</street>
<comment>BLAH BLAH BLAH <br><br>ABC</comment>

3
เมื่อเพิ่มคำตอบให้กับคำถามอายุเก้าขวบที่มีคำตอบห้าข้อที่มีอยู่เป็นสิ่งสำคัญมากที่จะต้องชี้ให้เห็นว่ามุมมองใหม่ที่ไม่เหมือนใครของคำถามนี้คือคำตอบของคุณ
Jason Aller

คำตอบฉันโพสต์ง่ายมาก คิดเหมือนการแบ่งปันซึ่งอาจช่วยผู้เริ่มต้นอย่างฉัน
user3520544
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.