XPath เพื่อเลือกหลายแท็ก


132

ด้วยรูปแบบข้อมูลที่เรียบง่ายนี้:

<a>
    <b>
        <c>C1</c>
        <d>D1</d>
        <e>E1</e>
        <f>don't select this one</f>
    </b>
    <b>
        <c>C2</c>
        <d>D2</d>
        <e>E1</e>
        <g>don't select me</g>
    </b>
    <c>not this one</c>
    <d>nor this one</d>
    <e>definitely not this one</e>
</a>

คุณจะเลือกCs, Ds และEs ทั้งหมดที่เป็นลูกของBองค์ประกอบได้อย่างไร?

โดยทั่วไปสิ่งที่ชอบ:

a/b/(c|d|e)

ในสถานการณ์ของตัวเองแทนเพียงa/b/แบบสอบถามที่นำไปสู่การเลือกเหล่านั้นC, D, Eโหนดเป็นจริงค่อนข้างซับซ้อนดังนั้นฉันต้องการที่จะหลีกเลี่ยงการทำเช่นนี้:

a/b/c|a/b/d|a/b/e

เป็นไปได้หรือไม่

คำตอบ:


208

คำตอบที่ถูกต้องคือ :

/a/b/*[self::c or self::d or self::e]

โปรดทราบว่าสิ่งนี้

a/b/*[local-name()='c' or local-name()='d' or local-name()='e']

เป็นทั้งยาวเกินไปและไม่ถูกต้อง นิพจน์ XPath นี้จะเลือกโหนดเช่น:

OhMy:c

NotWanted:d 

QuiteDifferent:e

2
'หรือ' ใช้ไม่ได้กับ for-each คุณจะต้องใช้เส้นแนวตั้งแทน '|'
Guasqueño

8
@ Guasqueño orเป็นตัวดำเนินการเชิงตรรกะ - ทำงานกับค่าบูลีนสองค่า ตัวดำเนินการสหภาพ XPath |ทำงานบนโหนดสองชุด สิ่งเหล่านี้แตกต่างกันมากและมีกรณีการใช้งานเฉพาะสำหรับแต่ละกรณี การใช้| สามารถแก้ปัญหาเดิมได้ แต่ส่งผลให้เข้าใจนิพจน์ XPath ที่ยาวขึ้นและซับซ้อนและท้าทายมากขึ้น นิพจน์ที่ง่ายกว่าในคำตอบนี้ซึ่งใช้ตัวorดำเนินการสร้างชุดโหนดที่ต้องการและสามารถระบุได้ในแอตทริบิวต์ "select" ของการ<xsl:for-each>ดำเนินการ XSLT แค่ลองดู.
Dimitre Novatchev

4
@JonathanBenn ใครก็ตามที่ "ไม่สนใจเนมสเปซ" จริงๆแล้วไม่สนใจ XML และไม่ใช้ XML การใช้local-name()จะถูกต้องก็ต่อเมื่อเราต้องการเลือกองค์ประกอบทั้งหมดที่มีชื่อท้องถิ่นนั้นไม่ว่าจะอยู่ในเนมสเปซองค์ประกอบใดก็ตามนี่เป็นกรณีที่หายากมาก - โดยทั่วไปคนทั่วไปจะสนใจเกี่ยวกับความแตกต่างระหว่าง: kitchen:tableและsql:table, หรือระหว่างarchitecture:column, sql:column, array:column,military:column
Dimitre Novatchev

3
@DimitreNovatchev คุณสร้างจุดที่ดี ฉันใช้ XPath สำหรับการตรวจสอบ HTML ซึ่งเป็นกรณีขอบที่เนมสเปซไม่สำคัญนัก ...
Jonathan Benn

2
นั่นคือสุดยอด คุณคิดขึ้นมาจากไหน?
Keith Tyler

46

คุณสามารถหลีกเลี่ยงการทำซ้ำได้ด้วยการทดสอบแอตทริบิวต์แทน:

a/b/*[local-name()='c' or local-name()='d' or local-name()='e']

ตรงกันข้ามกับความคิดเห็นที่เป็นปฏิปักษ์ของ Dimitre ข้างต้นไม่ถูกต้องในสุญญากาศที่ OP ไม่ได้ระบุการโต้ตอบกับเนมสเปซ self::แกนเข้มงวด namespace, local-name()ไม่ได้ ถ้าความตั้งใจของ OP คือการจับc|d|eโดยไม่คำนึงถึงเนมสเปซ (ซึ่งฉันขอแนะนำว่าเป็นสถานการณ์ที่น่าจะเป็นไปได้ตามธรรมชาติของหรือปัญหา) นั่นคือ "คำตอบอื่นที่ยังมีคะแนนโหวตในเชิงบวกอยู่บ้าง" ซึ่งไม่ถูกต้อง

คุณไม่สามารถสรุปได้หากไม่มีคำจำกัดความแม้ว่าฉันจะยินดีมากที่จะลบคำตอบของฉันว่าไม่ถูกต้องอย่างแท้จริงหาก OP ชี้แจงคำถามของเขาว่าฉันไม่ถูกต้อง


3
พูดในฐานะบุคคลที่สามที่นี่ - โดยส่วนตัวฉันพบว่าคำแนะนำของ Dimitre เป็นแนวทางปฏิบัติที่ดีกว่ายกเว้นในกรณีที่ผู้ใช้มีเหตุผลที่ชัดเจน (และดี) ในการดูแลชื่อแท็กที่ไม่เกี่ยวข้องกับเนมสเปซ หากใครทำสิ่งนี้กับเอกสารที่ฉันผสมในเนื้อหาที่มีเนมสเปซต่างกัน (น่าจะตั้งใจให้ toolchain อื่นอ่าน) ฉันจะพิจารณาว่าพฤติกรรมของพวกเขาไม่เหมาะสมอย่างยิ่ง ที่กล่าวว่าข้อโต้แย้งคือ - ตามที่คุณแนะนำ - ไม่เป็นที่ยอมรับสักหน่อย
Charles Duffy

4
สิ่งที่ฉันกำลังมองหา เนมสเปซ XML แบบที่ใช้ในชีวิตจริงเป็นเรื่องยุ่งเหยิง สำหรับการขาดความสามารถในการระบุสิ่งต่างๆเช่น / a / b / ( : c | : d | * e) โซลูชันของคุณคือสิ่งที่จำเป็น Purists สามารถโต้แย้งได้ทั้งหมดที่พวกเขาต้องการ แต่ผู้ใช้ไม่สนใจว่าแอปจะหยุดทำงานเพราะอะไรก็ตามที่สร้างไฟล์อินพุตของพวกเขาทำให้เนมสเปซเสียหาย พวกเขาแค่ต้องการให้มันทำงาน
Ghostrider

7
ฉันมีเพียงความคิดที่คลุมเครือว่าความแตกต่างระหว่างสองคำตอบนี้คืออะไรและไม่มีใครสนใจที่จะอธิบาย "เนมสเปซ จำกัด " หมายความว่าอย่างไร ถ้าฉันใช้local-name()หมายความว่ามันจะจับคู่แท็กกับเนมสเปซใด ๆ หรือไม่? ถ้าฉันใช้self::เนมสเปซจะต้องตรงกับอะไร? ฉันจะจับคู่ได้OhMy:cอย่างไร?
meustrus

15

ทำไมไม่a/b/(c|d|e)? ฉันเพิ่งลองใช้ไลบรารี Saxon XML (ห่อหุ้มด้วยความดีของ Clojure) และดูเหมือนว่าจะใช้งานได้ abc.xmlคือเอกสารอธิบายโดย OP

(require '[saxon :as xml])
(def abc-doc (xml/compile-xml (slurp "abc.xml")))
(xml/query "a/b/(c|d|e)" abc-doc)
=> (#<XdmNode <c>C1</c>>
    #<XdmNode <d>D1</d>>
    #<XdmNode <e>E1</e>>
    #<XdmNode <c>C2</c>>
    #<XdmNode <d>D2</d>>
    #<XdmNode <e>E1</e>>)

8
ใช่ แต่นั่นคือ XPath 2.0

นี้ทำงานได้ดีสำหรับฉัน. ดูเหมือนว่า XPath 2.0 จะเป็นค่าเริ่มต้นสำหรับการแยกวิเคราะห์ HTML ใน lxml บน Python 2
Martin Burch

-1

ไม่แน่ใจว่าจะช่วยได้ไหม แต่ด้วย XSL ฉันจะทำสิ่งที่ชอบ:

<xsl:for-each select="a/b">
    <xsl:value-of select="c"/>
    <xsl:value-of select="d"/>
    <xsl:value-of select="e"/>
</xsl:for-each>

และ XPath นี้จะไม่เลือกลูกทั้งหมดของโหนด B:

a/b/*

ขอบคุณ Calvin แต่ฉันไม่ได้ใช้ XSL และจริงๆแล้วมีองค์ประกอบอื่น ๆ อีกมากมายภายใต้ B ซึ่งฉันไม่ต้องการเลือก ฉันจะปรับปรุงตัวอย่างของฉันให้ชัดเจนขึ้น
nickf

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