Wprowadzenie do korzystania z XPath w języku JavaScript

przez 5 współtwórców

UWAGA: Tłumaczenie tej strony nie zostało zakończone.
Może być ona niekompletna lub wymagać korekty.
Chcesz pomóc? | Dokończ tłumaczenie | Sprawdź ortografię | Więcej takich stron+.

W tym dokumencie przedstawiono interfejsy umożliwiające korzystanie z języka XPath w kodzie JavaScript, w rozszerzeniach oraz na stronach internetowych. W programach Mozilla zaimplementowano znaczną część specyfikacji DOM 3 XPath, co pozwala na korzystanie z wyrażeń XPath zarówno w dokumentach HTML, jak i XML.

Głównym interfejsem języka XPath jest funkcja evaluate, stanowiąca metodę obiektu document.

document.evaluate

Metoda evaluate przetwarza wyrażenie XPath w odniesieniu do dokumentu opartego na języku XML (dotyczy to także dokumentów HTML), a następnie zwraca obiekt typu XPathResult, który może być pojedynczym węzłem lub zestawem węzłów. Podstawowa dokumentacja tej metody znajduje się pod adresem DOM:document.evaluate, jednak jest niezbyt obszerna — z tego względu dokładniejsze omówienie przedstawiono poniżej w tym artykule.

var xpathResult = document.evaluate( xpathExpression, contextNode, namespaceResolver, resultType, result );

Argumenty

Metoda evaluate przyjmuje łącznie pięć argumentów:

  • xpathExpression: ciąg znaków zawierający wyrażenie XPath, które ma być przetworzone.
  • contextNode: węzeł w dokumencie, w kontekście którego ma być przetworzone wyrażenie określone w argumencie xpathExpression; uwzględnione będą także wszystkie węzły potomne tego węzła. Najczęściej używanym węzłem jest document.
  • namespaceResolver: funkcja, do której zostaną przekazane wszystkie prefiksy przestrzeni nazw zawarte w wyrażeniu xpathExpression; funkcja ta zwraca ciąg znaków reprezentujący przestrzeń nazw URI powiązaną z danym prefiksem. Pozwala to na konwersję prefiksów użytych w wyrażeniu XPath na prefiksy stosowane w dokumencie (które mogą się różnić). Funkcją tą może być:
    • Funkcja utworzona za pomocą metody createNSResolver obiektu XPathEvaluator. Ten sposób powinien być stosowany w zasadzie we wszystkich przypadkach.
    • Wartość null; dotyczy to dokumentów HTML lub sytuacji, w których nie są używane żadne prefiksy przestrzeni nazw. Jeżeli jednak wyrażenie xpathExpression zawiera prefiksy przestrzeni nazw, użycie wartości null spowoduje zgłoszenie wyjątku typu DOMException o kodzie NAMESPACE_ERR.
    • Niestandardowa funkcja zdefiniowana przez użytkownika. Szczegółowe informacje można znaleźć w sekcji Implementacja własnej funkcji określającej przestrzeń nazw.
  • resultType: stała określająca typ obiektu, jaki ma być zwrócony jako wynik przetwarzania. Najczęściej stosowana jest stała XPathResult.ANY_TYPE, w efekcie czego zwracany jest najwłaściwszy w danym przypadku typ danych. W dodatku do tego dokumentu zamieszczono pełną listę dostępnych stałych. Ich objaśnienia znajdują się w sekcji Określanie typu zwracanej wartości.
  • result: istniejący obiekt XPathResult, który ma być ponownie wykorzystany do zwrócenia wyników, lub wartość null, której użycie powoduje utworzenie nowego obiektu XPathResult.

Zwracana wartość

Metoda zwraca wartość xpathResult, którą stanowi obiekt XPathResult o typie określonym przez argument resultType. Interfejs XPathResult jest zdefiniowany w tym dokumencie.

Implementacja funkcji określającej domyślną przestrzeń nazw

Za pomocą metody createNSResolver obiektu document utworzymy funkcję określającą przestrzeń nazw.

var nsResolver = document.createNSResolver( contextNode.ownerDocument == null ? contextNode.documentElement : contextNode.ownerDocument.documentElement );

Or alternatively by using the <code>createNSResolver</code> method of a <code>XPathEvaluator</code> object. <pre> var xpEvaluator = new XPathEvaluator(); var nsResolver = xpEvaluator.createNSResolver( contextNode.ownerDocument == null ? contextNode.documentElement : contextNode.ownerDocument.documentElement ); </pre> Następnie przekażemy zmienną nsResolver do metody document.evaluate jako argument namespaceResolver.

Należy zwrócić uwagę, że nazwy kwalifikowane (QNames) są definiowane z pominięciem prefiksów, tak aby pasowały wyłącznie do elementów w przestrzeni nazw null. W języku XPath nie ma możliwości wskazania domyślnej przestrzeni nazw. Aby dopasować elementy lub atrybuty z przestrzeni nazw innej niż null, należy skorzystać z testów nazw z prefiksami, po czym utworzyć funkcję określającą przestrzeń nazw, która będzie mapować prefiks do przestrzeni nazw. Więcej informacji o tworzeniu własnej funkcji określającej przestrzeń nazw można znaleźć poniżej. Note, XPath defines <code>QNames</code> without prefix to match only elements in the null namespace. There is no way in XPath to pick up the default namespace. To match elements or attributes in a non-null namespace, you have to use prefixed name tests, and create namespace resolver mapping the prefix to the namespace. Read more on how to create a user defined namespace resolver below.

Określanie typu zwracanej wartości

Zwracana przez metodę document.evaluate zmienna xpathResult może być zarówno pojedynczym węzłem (typy proste), jak i kolekcją węzłów (typy zestawów węzłów).

Typy proste

Jeżeli typ zwracanej wartości (określony przez argument resultType) przyjmuje jedną z poniższych wartości:

  • NUMBER_TYPE — liczba (typ double),
  • STRING_TYPE — ciąg znaków,
  • BOOLEAN_TYPE — wartość logiczna,

to zwracaną przez wyrażenie wartość można uzyskać, korzystając z własności obiektu XPathResult, odpowiednio:

  • numberValue,
  • stringValue,
  • booleanValue.
Przykład

W poniższym kodzie wyrażenie XPath count(//p) zostało użyte w celu znalezienia liczby elementów <p> w dokumencie HTML:

var paragraphCount = document.evaluate( 'count(//p)', document, null, XPathResult.ANY_TYPE, null );

alert( 'Ten dokument zawiera następującą liczbę akapitów: ' + paragraphCount.numberValue );

O ile w języku JavaScript liczby zostaną automatycznie skonwertowane na ciągi znaków, o tyle interfejs XPath nie wykona automatycznej konwersji wyniku liczbowego, jeżeli pobrana zostanie własność stringValue — z tego względu poniższy kod <big>nie</big> będzie działać:

var paragraphCount = document.evaluate('count(//p)', document, null, XPathResult.ANY_TYPE, null );

alert( 'Ten dokument zawiera następującą liczbę akapitów: ' + paragraphCount.stringValue );

W takiej sytuacji zgłoszony zostanie wyjątek o kodzie NS_DOM_TYPE_ERROR.

Typy zestawów węzłów

Istnieją 3 różne typy zestawów węzłów zwracanych jako obiekt XPathResult:

Iteratory

Jeżeli w parametrze resultType określony jest jeden z poniższych typów:

  • UNORDERED_NODE_ITERATOR_TYPE,
  • ORDERED_NODE_ITERATOR_TYPE,

to zwracanym obiektem XPathResult jest zestaw pasujących węzłów, działający jak iterator — dostęp do poszczególnych węzłów można uzyskać za pomocą metody iterateNext() obiektu XPathResult.

Po przejściu przez wszystkie pasujące węzły metoda iterateNext() zwraca wartość null.

Jeżeli jednak w trakcie iteracji dokument zostanie zmieniony (tj. zmodyfikowane zostanie drzewo dokumentu), iteracja zostanie unieważniona. Własność invalidIteratorState obiektu XPathResult przyjmuje wówczas wartość true oraz zgłaszany jest wyjątek NS_ERROR_DOM_INVALID_STATE_ERR.

Przykład iteratora
var iterator = document.evaluate('//phoneNumber', documentNode, null, XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null );

try {
  var thisNode = iterator.iterateNext();
  
  while (thisNode) {
    alert( thisNode.textContent );
    thisNode = iterator.iterateNext();
  }	
}
catch (e) {
  dump( 'Błąd: drzewo dokumentu zostało zmodyfikowane podczas iteracji ' + e );
}
Migawka

Jeżeli w parametrze resultType określony jest jeden z poniższych typów:

  • UNORDERED_NODE_SNAPSHOT_TYPE,
  • ORDERED_NODE_SNAPSHOT_TYPE,

to zwracanym obiektem XPathResult jest statyczny zestaw pasujących węzłów; dostęp do poszczególnych węzłów można uzyskać za pomocą metody snapshotItem(itemNumber) obiektu XPathResult, gdzie itemNumber to indeks pobieranego węzła. Liczbę wszystkich węzłów zawartych w obiekcie XPathResult określa własność snapshotLength.

Migawki nie ulegają zmianom wraz z modyfikacjami dokumentu, więc — w przeciwieństwie do iteratorów — nie podlegają unieważnieniu, mogą jednak nie odpowiadać bieżącej postaci dokumentu. Jeżeli na przykład niektóre węzły zostaną przesunięte, migawka może zawierać węzły nieistniejące, nie będzie natomiast zawierać węzłów dodanych w nowym miejscu.

Przykład migawki
var nodesSnapshot = document.evaluate('//phoneNumber', documentNode, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null );

for ( var i=0 ; i < nodesSnapshot.snapshotLength; i++ )
{
  dump( nodesSnapshot.snapshotItem(i).textContent );
}
Pierwsze węzły

Jeżeli w parametrze resultType określony jest jeden z poniższych typów:

  • ANY_UNORDERED_NODE_TYPE
  • FIRST_ORDERED_NODE_TYPE

to zwracanym obiektem XPathResult jest wyłącznie pierwszy znaleziony węzeł pasujący do wyrażenia XPath. Dostęp do tego węzła można uzyskać za pomocą własności singleNodeValue obiektu XPathResult. Jeżeli zestaw węzłów jest pusty, zwracana jest wartość null.

Należy zwrócić uwagę, że o ile w przypadku podtypu nieuporządkowanego zwracany pojedynczy węzeł może nie być pierwszym w kolejności węzłem dokumentu, o tyle w razie użycia podtypu uporządkowanego gwarantowane jest zwrócenie pierwszego pasującego węzła dokumentu.

Przykład pierwszego węzła
var firstPhoneNumber = document.evaluate('//phoneNumber', documentNode, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null );

dump( 'Pierwszy znaleziony numer telefonu to ' + firstPhoneNumber.singleNodeValue.textContent );

Stała ANY_TYPE

Jeżeli w parametrze resultType określony jest typ ANY_TYPE, zwracany obiekt XPathResult będzie takiego typu, jaki jest najbardziej odpowiedni dla danego wyrażenia.

Może to być jeden z typów prostych (NUMBER_TYPE, STRING_TYPE, BOOLEAN_TYPE), <big>jednak</big>, jeżeli zwracany jest zestaw węzłów, <big>jedynym</big> możliwym typem jest typ UNORDERED_NODE_ITERATOR_TYPE.

Aby określić typ zwracany po przetworzeniu wyrażenia, należy skorzystać z własności resultType obiektu XPathResult. Stałe wartości tej własności są zdefiniowane w dodatku do tego artykułu. None Yet =====Przykład zastosowania stałej <code>ANY_TYPE</code>===== <pre> </pre>

Przykłady

Dokumenty HTML

Poniższy kod można umieścić w dowolnym miejscu kodu JavaScript, który znajduje się w tym dokumencie HTML lub jest powiązany z tym dokumentem HTML, w odniesieniu do którego przetwarzane będzie wyrażenie XPath.

To extract all the <h2> heading elements in a HTML document using XPath, the xpathExpression is simply '//h2'. Where, // is the Recursive Descent Operator that matches elements with the nodeName h2 anywhere in the document tree. The full code for this is: link to introductory xpath doc

var headings = document.evaluate('//h2', document, null, XPathResult.ANY_TYPE, null );

Notice that, since HTML does not have namespaces, we have passed null for the namespaceResolver parameter.

Since we wish to search over the entire document for the headings, we have used the document object itself as the contextNode.

The result of this expression is an XPathResult object. If we wish to know the type of result returned, we may evaluate the resultType property of the returned object. In this case that will evaluate to 4, a UNORDERED_NODE_ITERATOR_TYPE. This is the default return type when the result of the XPath expression is a node-set. It provides access to a single node at a time and may not return nodes in a particular order. To access the returned nodes, we use the iterateNext() method of the returned object:

var thisHeading = headings.iterateNext();

var alertText = 'Level 2 headings in this document are:\n'

while (thisHeading) {
  alertText += thisHeading.textContent + '\n';
  thisHeading = headings.iterateNext();
}

Once we iterate to a node, we have access to all the standard DOM interfaces on that node. After iterating through all the h2 elements returned from our expression, any further calls to iterateNext() will return null.

Przetwarzanie wyrażenia w kontekście dokumentu XML w kodzie rozszerzenia

The following uses an XML document located at chrome://yourextension/content/peopleDB.xml as an example.

<?xml version="1.0"?>
<people xmlns:xul = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" >
  <person>
	<name first="george" last="bush" />
	<address street="1600 pennsylvania avenue" city="washington" country="usa"/>
	<phoneNumber>202-456-1111</phoneNumber>
  </person>
  <person>
	<name first="tony" last="blair" />
	<address street="10 downing street" city="london" country="uk"/>
	<phoneNumber>020 7925 0918</phoneNumber>
  </person>
</people>

To make the contents of the XML document available within the extension, we create a XMLHttpRequest object to load the document synchronously, the variable xmlDoc will contain the document as an XMLDocument object against which we can use the evaluate method

JavaScript used in the extensions xul/js documents.

var req = new XMLHttpRequest();

req.open("GET", "chrome://yourextension/content/peopleDB.xml", false); 
req.send(null);

var xmlDoc = req.responseXML;		

var nsResolver = xmlDoc.createNSResolver( xmlDoc.ownerDocument == null ? xmlDoc.documentElement : xmlDoc.ownerDocument.documentElement);

var personIterator = xmlDoc.evaluate('//person', xmlDoc, nsResolver, XPathResult.ANY_TYPE, null );

Dodatek

Implementacja własnej funkcji określającej przestrzeń nazw

This is an example for illustration only. This function will need to take namespace prefixes from the xpathExpression and return the URI that corresponds to that prefix. For example, the expression:

'//xhtml:td/mathml:math'

will select all MathML expressions that are the children of (X)HTML table data cell elements.

In order to associate the 'mathml:' prefix with the namespace URI 'http://www.w3.org/1998/Math/MathML' and 'xhtml:' with the URI 'http://www.w3.org/1999/xhtml' we provide a function:

function nsResolver(prefix) {
  var ns = {
    'xhtml' : 'http://www.w3.org/1999/xhtml',
    'mathml': 'http://www.w3.org/1998/Math/MathML'
  };
  return ns[prefix] || null;
}

Our call to document.evaluate would then looks like:

document.evaluate( '//xhtml:td/mathml:math', document, nsResolver, XPathResult.ANY_TYPE, null );

Implementacja domyślnej przestrzeni nazw dla dokumentów XML

As noted in the #Implementing a Default Namespace Resolver previously, the default resolver does not handle the default namespace for XML documents. For example with this document:

<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <entry />
    <entry />
    <entry />
</feed>

doc.evaluate('//entry', doc, nsResolver, XPathResult.ANY_TYPE, null) will return an empty set, where nsResolver is the resolver returned by createNSResolver. Passing a null resolver doesn't work any better, either.

One possible workaround is to create a custom resolver that returns the correct default namespace (the Atom namespace in this case). Note that you still have to use some namespace prefix in your XPath expression, so that the resolver function will be able to change it to your required namespace. E.g.:

function resolver() {
    return 'http://www.w3.org/2005/Atom';
}
doc.evaluate('//myns:entry', doc, resolver, XPathResult.ANY_TYPE, null)

Note that a more complex resolver will be required if the document uses multiple namespaces.

Zdefiniowane stałe określające typy obiektu XPathResult

Result Type Defined Constant Value Description
ANY_TYPE 0 A result set containing whatever type naturally results from evaluation of the expression. Note that if the result is a node-set then UNORDERED_NODE_ITERATOR_TYPE is always the resulting type.
NUMBER_TYPE 1 A result containing a single number. This is useful for example, in an XPath expression using the count() function.
STRING_TYPE 2 A result containing a single string.
BOOLEAN_TYPE 3 A result containing a single boolean value. This is useful for example, in an XPath expression using the not() function.
UNORDERED_NODE_ITERATOR_TYPE 4 A result node-set containing all the nodes matching the expression. The nodes may not necessarily be in the same order that they appear in the document.
ORDERED_NODE_ITERATOR_TYPE 5 A result node-set containing all the nodes matching the expression. The nodes in the result set are in the same order that they appear in the document.
UNORDERED_NODE_SNAPSHOT_TYPE 6 A result node-set containing snapshots of all the nodes matching the expression. The nodes may not necessarily be in the same order that they appear in the document.
ORDERED_NODE_SNAPSHOT_TYPE 7 A result node-set containing snapshots of all the nodes matching the expression. The nodes in the result set are in the same order that they appear in the document.
ANY_UNORDERED_NODE_TYPE 8 A result node-set containing any single node that matches the expression. The node is not necessarily the first node in the document that matches the expression.
FIRST_ORDERED_NODE_TYPE 9 A result node-set containing the first node in the document that matches the expression.

Informacje o oryginalnym dokumencie

  • Treść została oparta na dokumencie Kurs Mozilla XPath.
  • Autor oryginału: James Graham.
  • Współautor: James Thompson.
  • Data ostatniej aktualizacji: 25 marca 2006 r.

 

Autorzy i etykiety dokumentu

Contributors to this page: teoli, fscholz, Mgjbot, Flaneur, Ptak82
Ostatnia aktualizacja: teoli,