Wie wähle ich unter Verwendung von XPath einen Knoten basierend auf seinem Textinhalt und dem Wert eines Attributs aus?

71

Angesichts dieser XML:

<DocText>
<WithQuads>
    <Page pageNumber="3">
        <Word>
            July
            <Quad>
                <P1 X="84" Y="711.25" />
                <P2 X="102.062" Y="711.25" />
                <P3 X="102.062" Y="723.658" />
                <P4 X="84.0" Y="723.658" />
            </Quad>
        </Word>
        <Word>
        </Word>
        <Word>
            30,
            <Quad>
                <P1 X="104.812" Y="711.25" />
                <P2 X="118.562" Y="711.25" />
                <P3 X="118.562" Y="723.658" />
                <P4 X="104.812" Y="723.658" />
            </Quad>
        </Word>
    </Page>
</WithQuads>

Ich möchte die Knoten finden, die den Text 'Juli' und ein Quad / P1 / X-Attribut größer als 90 haben. In diesem Fall sollten daher keine Übereinstimmungen zurückgegeben werden. Wenn ich jedoch GT (>) oder LT (<) verwende, erhalte ich eine Übereinstimmung für das erste Word-Element. Wenn ich eq (=) verwende, bekomme ich keine Übereinstimmung.

Damit:

//Word[text()='July' and //P1[@X < 90]]

wird wahr zurückgeben, wie wird

//Word[text()='July' and //P1[@X > 90]]

Wie kann ich dies für das P1 @ X-Attribut richtig einschränken?

Stellen Sie sich außerdem vor, ich habe mehrere Seitenelemente für unterschiedliche Seitenzahlen. Wie würde ich die obige Suche zusätzlich einschränken, um Knoten mit text()='July', P1@X < 90und Seite zu finden @pageNumber=3?

marc esher
quelle
Eine wichtige Sache, die bei diesem speziellen XML zu beachten ist und möglicherweise nicht für jeden Leser offensichtlich ist, ist, dass es schwierig ist, Elemente mit XPath abzugleichen, da dieses XML ein gemischtes Inhaltsmodell verwendet. Ich bin kürzlich auf genau dieses Problem gestoßen und wollte, da ich mit meinem XPath verrostet war, zu dem Schluss kommen, dass man nicht mit gemischten Inhaltselementen übereinstimmen kann, bis ich unten die Antwort von Michael Kay gefunden habe. Ich konnte keine andere Referenz finden, die über Fallstricke mit gemischtem Inhalt und XPath spricht.
Michael Sorens
Ihre Frage hat meine Frage beantwortet. Es ist sehr wichtig, "einfache Apostrophe" für Zeichenfolgen in XPath zu verwenden, nicht "doppelte Anführungszeichen". Es ist wirklich sehr wichtig. Danke für den Hinweis.
Krzysztof Jabłoński

Antworten:

78

Im Allgemeinen würde ich die Verwendung eines nicht vorfixierten // als schlechten Geruch in einem XPath betrachten.

Versuche dies:-

/DocText/WithQuads/Page/Word[text()='July' and Quad/P1/@X > 90]

Ihr Problem ist, dass Sie das verwenden, das //P1[@X < 90]am Anfang des Dokuments beginnt und mit der Jagd beginnt, P1daher wird es immer wahr sein. Ähnliches //P1[@X > 90]gilt immer.

AnthonyWJones
quelle
1
Ich bin überrascht, dass dies aufgrund der in Michael Kays Antwort angesprochenen Leerzeichenprobleme tatsächlich funktioniert hat. Ich habe diese Antwort in verschiedenen XPath-Evaluatoren ausprobiert und sie stimmte auch nicht mit beiden überein. Nachdem ich mit 'Normalize-Space' zum Prädikat gewechselt hatte, machte ich ein erfolgreiches Match.
Michael Sorens
Sie können die .//P1Jagd auf dem aktuellen Level starten, anstatt einen festen Pfad anzugeben
Mark Jeronimus
27

Abgesehen vom "//" Problem ist dieses XML eine sehr seltsame Verwendung von gemischtem Inhalt. Das Prädikat text()='July'stimmt mit dem Element überein, wenn ein untergeordneter Textknoten genau gleich Juli ist, was in Ihrem Beispiel aufgrund des umgebenden Leerzeichens nicht zutrifft. Abhängig von der genauen Definition des Quell-XML würde ich mich entscheiden[text()[normalize-space(.)='July'] and Quad/P1/@X > 90]

Michael Kay
quelle
Danke, Michael. Ich habe mich über das Leerzeichen gewundert ... Ich habe das Beispiel formatiert, bevor ich es in den Stapelüberlauf eingefügt habe, aber mein Quell-XML ist alles "eng". Als ich den xpath gegen die formatierte Version ausführte, funktionierte er tatsächlich nicht richtig. Ich werde versuchen, normalize-space (.)
marc esher