2010-10-13 10 views
15

Ich möchte eine XPath-Abfrage erstellen, die ein "div" oder "Tabelle" -Element zurückgibt, solange es einen Nachkommen mit dem Text "abc" hat. Der einzige Nachteil ist, dass es keine div- oder table-Nachkommen haben kann.XPath Abfrage mit Nachkommen und Nachkommen Text() Prädikate

<div> 
    <table> 
    <form> 
     <div> 
     <span> 
      <p>abcdefg</p> 
     </span> 
     </div> 
     <table> 
     <span> 
      <p>123456</p> 
     </span> 
     </table> 
    </form> 
    </table> 
</div> 

So ist das einzige richtige Ergebnis dieser Abfrage wäre:

//div[contains(//text(), "abc") and not(descendant::div or descendant::table)] | //table[contains(//text(), "abc") and not(descendant::div or descendant::table)] 

aber nicht zurück das richtige Ergebnis:

/div/table/form/div 

Mein bester Versuch, etwas wie folgt aussieht.

Danke für Ihre Hilfe.

+0

Gute Frage, +1. Sehen Sie meine Antwort für die wahrscheinlich kürzeste Lösung. :) –

Antwort

32

Etwas anderes: :)

//text()[contains(.,'abc')]/ancestor::*[self::div or self::table][1] 

scheint viel kürzer als die anderen Lösungen, nicht wahr? :)

übersetzte in einfachen englischen: für jeden Text-Knoten in dem Dokument, die die Zeichenfolge enthalten "abc" seinen ersten Vorfahren auswählen, die entweder ein oder ein divtable.

Dies ist effizienter, da nur ein Scan des Dokumentbaums (und nicht irgendein anderen) erforderlich ist, und die ancestor::* Traversal ist sehr billig im Vergleich zu einem descendent:: (Baum) Scan.

Um sicherzustellen, dass diese Lösung "wirklich funktioniert":

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 
<xsl:strip-space elements="*"/> 

<xsl:template match="/"> 
    <xsl:copy-of select= 
    "//text()[contains(.,'abc')]/ancestor::*[self::div or self::table][1] "/> 
</xsl:template> 
</xsl:stylesheet> 

wenn diese Transformation auf dem mitgelieferten XML-Dokument ausgeführt wird:

<div> 
    <table> 
    <form> 
     <div> 
     <span> 
      <p>abcdefg</p> 
     </span> 
     </div> 
     <table> 
     <span> 
      <p>123456</p> 
     </span> 
     </table> 
    </form> 
    </table> 
</div> 

das gewünschte, richtige Ergebnis hergestellt:

<div> 
    <span> 
     <p>abcdefg</p> 
    </span> 
</div> 

Hinweis: Es ist nicht notwendig, XSLT zu verwenden - jeder XPath 1.0-Host - wie DOM - muss das gleiche Ergebnis erhalten.

+1

danke für deine Antwort und danke für die +1. Ich bevorzuge die Kompaktheit dieser Antwort, aber ich kann sie in meinen Tests nicht anwenden. Die anderen beiden Antworten auf diese Frage funktionieren für mich. Ist es möglich, dass es einen Tippfehler in Ihrer Antwort gibt? Ich kann nicht behaupten, alles zu verstehen.Was macht der [1]? Noch einmal, wenn Sie irgendeine Einsicht haben, warum diese Antwort nicht für mich und die anderen funktioniert, würde ich es begrüßen. Ich würde für Ihre Zeit +1 geben, aber ich bin neu auf dieser Seite und habe noch nicht die Fähigkeit. Vielen Dank. – juan234

+0

@ juan234: Ich habe zu meiner Antwort einen Bestätigungscode hinzugefügt, den jeder ausführen und die Korrektheit des Ergebnisses überprüfen kann. Diese Überprüfung zeigt die Korrektheit des Ausdrucks - es gibt * nein * Tippfehler. Sie haben möglicherweise Probleme aus verschiedenen Gründen: von der Verwendung der inkompatiblen XPath 1.0-Engine zu Problemen in Ihrem Code - um den Grund zu ermitteln, warum Sie Ihren Code sehen müssen. "[1]" bedeutet den ersten Knoten des Knotensatzes, der durch den Teil des Ausdrucks ausgewählt wird, der unmittelbar rechts von "[1]" ist - in umgekehrten Achsen (wie etwa "Vorgänger" bedeutet er tatsächlich den letzten Knoten) in Dokumentenreihenfolge). –

+0

Ich bin überzeugt :) – juan234

1

könnten Sie versuchen:

//div[ 
    descendant::text()[contains(., "abc")] 
    and not(descendant::div or descendant::table) 
] | 
//table[ 
    descendant::text()[contains(., "abc")] 
    and not(descendant::div or descendant::table) 
] 

tut diese Hilfe?

1
//*[self::div|self::table] 
    [descendant::text()[contains(.,"abc")]] 
    [not(descendant::div|descendant::table)] 

Das Problem ist, dass contains(//text(), "abc") Funktionen Knotenmengen gegossen den ersten Knoten nehmen.