An XML file starts from "the root", and branches out in complex ways to the "leaves".
Parent
Siblings
ancestor
immediate child of TEI
immediate child of TEI
5 children of teiHeader
child of text
child of text
child of body
children of div
XPath is a syntax that we write to select parts (or nodes) of an XML document
An XPath expression enables a user to describe, find, and navigate to information inside an XML document
XML languages that employ XPath to find and manipulate information in XML documents:
XSLT (eXtensible Stylesheet Language Transformations)
XQuery (XML Query language)
XML (TEI encoded)
XSLT
XPath
Navigating
Transforming
XQuery
Querying
Constraining
ODD & Schematron
A node is a piece of information in the XML tree, such as an element, an attribute, a string of text, or a comment.
text()
comment()
@
| Document Node | An implied root-level node. Contains everything, including the root node | |
| Root Node | The topmost element of a document. Contains all other parts of the document. | <TEI> <html> |
| Elements | The name for a structure to be encoded | <body> <div> <pb/> |
| Attributes | Information about an element | <div type="chapter"> <name xml:id="p002"> |
| Text Nodes | Textual data stored inside elements | <l>Lord, what fools these mortals be!</l> |
| Comments | Notes that are not processed when reading an XML document. | <!--Fix ref attributes throughout--> |
Open the 1819-01-10-MaryWebb.xml file
What happens when you enter comment()?
No luck?
Try adding two slashes before comment()
Enter //comment()
XPath syntax resembles a directory structure, use forward slashes ("/") to traverse through nodes.
What happens when you enter //text() ?
Used to target specific types of nodes in a given context
| Component | Purpose |
|---|---|
| path expression | Describe the location of some nodes in a tree. |
| axis | An axis is part of a path expression. Describe the direction in which one looks in the tree. |
| predicate | Filter the results of a path expression. |
| function | Do something with the information retrieved from the document instead of just returning it as received. |
| Expression | Description |
|---|---|
| nodename | Selects all nodes with the name "nodename" |
| / | Selects from the root node |
| // | Selects nodes in the document from the current node that match the selection no matter where they are |
| . | Selects the current node |
| .. | Selects the parent of the current node |
| @ | Selects attributes |
| Describes the location of some nodes |
|
Every XPath query walks through the full XML structure, even if we're using shorthand to express our starting point.
| Unabbreviated Syntax | Abbreviated Syntax |
|---|---|
| /child::TEI/child::text/child::body/child::div/child::p/child::persName/child::text | //p/persName/text() |
| Gives us the text included within any <name> elements that are the children of <p> (and so on, working up the tree) | // at the beginning of a query means "descendent-or-self" -- so, start with any node that is <p> or a descendant Each / following the first element name assumes you are searching for a child of the previous element |
| Write a path expression for selecting attributes |
|
| Enter //@ |
|
| What happens? |
|
| Write a path expression to select the corresp attribute |
|
| Enter //@corresp |
|
| Scroll through the Results window, what do you notice? |
|
| Look at the XPath location column in the Results window |
|
| Write a path expression to select the attribute of your choosing |
| Enter //@ |
|
| Then choose from the list of available attributes |
|
| Look at the XPath location column in the Results window for the different attributes |
|
| Write a path expression to select all of the attributes |
| Enter //@* |
|
| Expression | Description |
|---|---|
| ancestor:: | The ancestor axis sends you to parents and above, all the way up to the root node. |
| . or self:: |
The self axis designating the current context node and the current location in a path. |
| .. or parent:: | The parent axis sends you up a short distance, to the immediate parent of the context node |
| / or child:: | The child axis (the default) sends you down to the immediate child of the context node. |
| // or descendant:: | The descendant axis sends you down to the children and their children etc. |
| @ or attribute:: | The attribute (@) axis for locating attributes and attribute values |
Predicates are always embedded in [ ]
Filter the results of path expressions
Use predicates to find a specific node or a node that contains a specific value.
Remember: Predicates are always embedded in square brackets
How do we find the first paragraph of a div?
Predicates are used to find a specific node or a node that contains a specific value.
//div/p[1]
//div/p
How do we find a div element node that has a type attribute?
//div[@]
//div[@type]
//div[ ]
Exercise: Mary Webb letter
How do we find the first paragraph of a div with a type attribute?
//div[@type]
//div[@type]/p[ ]
//div[@type]/p
//div[@type]/p[1]
How do we find the first personal name mentioned in the first paragraph of a div with a type attribute?
//div[@type]/p[1]
//div[@type]/p[1]/persName
//div[@type]/p[1]/persName[ ]
//div[@type]/p[1]/persName[1]
How do we find the personal name with a reference to Chaucer?
//persName[@ref]
We need to filter for ref attributes using @ref
//persName[@ref="#Chaucer"]
We need to specify the value of @ref
//persName
Find the persName elements with a parent title element
//persName[parent::]
//persName[parent::title]
//title/persName
Find the surname elements with a persName ancestor
//surname[ ]
//surname[ancestor::]
//surname[ancestor::persName]
Find the person elements with a roleName descendant
//person[ ]
//person[descendant::]
//person[descendant::roleName]
Nodes that have the same parent element are called siblings
Following-sibling axis
Preceding-sibling axis
The following-sibling selects all sibling nodes after the current node at the same level.
following-sibling::
Find the surname elements with a following-sibling element forename
//surname[ ]
//surname[following-sibling::forename]
//surname[following-sibling::]
Find the persName elements with a preceding-sibling element placeName
//persName[ ]
//persName[preceding-sibling::placeName]
//persName[preceding-sibling::]
| Wildcard | Description |
|---|---|
| * | Matches any element node |
| @* | Matches any attribute node |
| node() | Matches any node of any kind |
| XPath Expression | Description |
|---|---|
| //* | Selects all elements in the document |
| //title[@*] | Selects all title elements which have at least one attribute of any kind |
Select all elements in the document
Enter //*
Exercise: Mary Webb Letter
Enter //title[@*]
Select all title elements in the document with an attribute
Do something with the information retrieved instead of just returning it as received.
Retrieve all of the <paragraph> elements in a <div> element (that has a type attribute value of "letter") but instead of returning the actual elements, return just a count of how many there are. This uses the count() function.
count(//div[@type="letter"]/p)
//div[@type="letter"]/p => count()
Retrieve all of the persName elements in a div element (that has a type attribute value of "letter") and return just a count of how many there are.
count(//div[@type="letter"]//persName)
//div[@type="letter"]//persName => count()
//div[@type="letter"]//persName
| Function | Description |
|---|---|
| distinct-values() | eliminate repetition in a list of results |
| last() | Returns the last |
|
lower-case() upper-case() |
Changes case of string |
| not() | Inverts the truth value of the argument. //p[not(q)] returns all <p> elements that do not have any <q> child element. |
| normalize-space() | normalize the white space (spaces, tabs, new lines, etc.) in text |
| string-length() | returns the length of text by counting characters. |
W3 School's XPath/XSLT/XQuery Functions Library
And if you need yet more specific options, XQuery lets you define your own functions.
| Function | Description |
|---|---|
| sort() | eliminate repetition in a list of results |
| first() | Returns the first instance of a particular query |
| contains() | Returns true/false result depending on whether a string contains a specified sub-string |
| matches() | Returns true/false if a string (first argument) matches a pattern (second argument) |
//div[@type]//p[1]
How do we find the last paragraph in the letter?
Remember: To find the first paragraph we used predicates [ ]
//div[@type="letter"]//p[ ]
To find the last paragraph in the letter
//div[@type="letter"]//p[last()]
Use the last() function
How do we remove repetition from the results for persName?
Use the distinct-values() function
//persName
distinct-values(//persName)
//persName=> distinct-values()
//div[@type]//persName=>distinct-values()
How do we remove repetition in the results of persName in the letter?
//div[@type]//persName
distinct-values(//div[@type]//persName)
[not(@ref="#Webb_Mary_younger")]
//div[@type]//persName
//div[@type]//persName[not(@ref="#Webb_Mary_younger")]
The not() function
| Operator | Description |
|---|---|
| ! | The simple map operator (!) means do the thing on the right once for each item on the left. |
| => | The arrow operator (=>) means apply the function on the right to the entire sequence (all at once) on the left. |
//* ! name() => distinct-values()
//* ! name()
The name() function returns a string representing the QName of the first node in a given node-set.
| Value | General | Description |
|---|---|---|
| eq | = | equal to |
| ne | != | not equal to |
| gt | > | > greater than (may also be written >) |
| ge | >= | greater than or equal to (not less than; may also be written >=) |
| lt | < | less than (may also be written <) |
| le | <= | less than or equal to (not greater than; may also be written <=) |
//p[count(persName) > 10]
Find paragraphs containing more than 10 personal names
//p/count(persName)