Curl Global Community
XML3:XPATH Expressions - Printable Version

+- Curl Global Community (https://communities.curl.com)
+-- Forum: Tutorials (https://communities.curl.com/forumdisplay.php?fid=3)
+--- Forum: Public Training (https://communities.curl.com/forumdisplay.php?fid=4)
+---- Forum: Curl Clues (https://communities.curl.com/forumdisplay.php?fid=5)
+---- Thread: XML3:XPATH Expressions (/showthread.php?tid=34)



XML3:XPATH Expressions - kino - 06-16-2011

Path expressions allows you to specify objects and object values from the Document Object Model using expressions. The expressions are defined using the W3C XPath's functions and the results are returned within the framework of DOM APIs in a standard, interoperable way. Path expressions conform to the XPath 1.0 standard, and are represented as XDMPath objects.


It is often more concise to use reference paths to locate data items in XML structures. This approach allows for a clearer distinction between the document structure, and the processing. The result of a path expression may be a nodeset, or an atomic value (text, number or boolean). In this Curl Cue, we will describe general XPath expressons that will get you started.


First, we need to describe general XPath expressions.

Now we can apply the expressions above to the example XML data where

•inventory is the root node

•item is a child node of inventory

•title, part, and price are children of item

•loc is an attribute


Code:
ff123
9.99



np213
6.95


Let's take a look at the following XPath expressions...


1. /inventory/item


Reading from left to right:

•/: is the root node of the document

•inventory/: specifies go to the inventory tag

•item: find all elements with an item tag



In this case, the result will be all item nodes in the samples root element.


2. /inventory/item/title/@loc


Reading from left to right:

•/: is the root node of the document

•inventory/: specifies to go to the inventory tag

•item/: go to all elements with an item tag

•title/: go to all elements with a title tag

•@loc: select loc attributes



In this case, the result will be all loc attributes from title nodes under item.


3. //title\[@loc\]


Reading from left to right:

•//: search the entire document

•title\[@loc\]: find all title elements that have a loc attribute

In this case, the result will be all the item elements that have an attribute named loc. Note that example 2 will return the loc attributes, while this example will return item elements that have a loc attribute.


4. //title\[@loc ="us"\]


Reading from left to right:

•//: search the entire document

•title\[@loc\]: find all title elements that have a loc attribute with the value of 'us'



In this case, the result will be the item elements that have a loc attribute equal to 'us'.


5. /inventory/item\[1\]


Reading from left to right:

•/: is the root node of the document

•inventory/: specifies to go to the inventory tag

•item\[1\]: select the first item element



In this case, the result will be the first item element that is the child of the inventory element. Note that expressions enclosed in \[ \] are predicates. A predicate is used to find a specific node or node that contains a specific value.


XDMPath Display


Now that we understand some XPath syntax, the following applet allows us to experiment with the expressions. The XDMPath Display applet has three panes:

•The top pane allows us to select an XML file from a dropdown list.

•The left pane displays the entire XML structure includijkng nodes, elements, attributes, and associated values.

•The right pane allows us to input an XPath query and display the results.





Once you have entered the desired XPath expression, click on the 'Select' button. The results of the query will be displayed in the right pane. Selecting the 'Highlight' button will highlight the corresponding nodes withing the XML display on the left pane.

Code:
||| Copyright (C) 1998-2009, Sumisho Computer Systems Corp. All Rights Reserved.
|||
||| An OpenCurl project.

||| Licensed under the Apache License, Version 2.0 (the "License");
||| you may not use this file except in compliance with the License.
||| You may obtain a copy of the License at
|||
||| http://www.apache.org/licenses/LICENSE-2.0
|||
||| Unless required by applicable law or agreed to in writing, software
||| distributed under the License is distributed on an "AS IS" BASIS,
||| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
||| See the License for the specific language governing permissions and
||| limitations under the License.

{curl 6.0, 7.0 applet}
{applet manifest = "../manifest.mcurl"}
{import * from COM.CURL.WSDK.XML-DOCUMENT-MODEL}
{import * from COM.CURL.WSDK.XML-DISPLAY}

|| prefix declarations
{define-proc {dwim-prefix-declarations
subject:XDMElement,
context:XDMNamespaceContext = xml-namespace-context
}:void
{subject.walk-children
|| walk descendants and self
include-self? = true,
include-descendants? = true,
{proc {n:XDMNode}:bool
{type-switch n
case e:XDMElement do
|| check declared prefixes
{if-non-null ps = e.namespace-declarations then
{for p in {ps.get-prefixes} do
{if-non-null ns = {ps.get-uri p} then
{if {context.get-uri p} == null
then
|| extend global declarations accordingly
{context.declare-prefix? p, ns}}}}}}
|| continue walking
{return true}}}
}

{document-style PlainDocument}
{set-document-properties
hstretch? = true,
vstretch? = true
}

{persistent-data "Settings"}
{value
|| defaults
let base-url:Url = {{get-the-applet}.url.parent.canonicalize}
let restore-settings?:bool = true
let factory-settings:XDMElement =
{XDMElement "settings",
{XDMElement "filename", "inventory.xml"},
{XDMElement "path", "*"}
}
let settings:XDMElement = {factory-settings.clone}
|| TODO: list model
|| - recent files
|| - recent paths
{if-non-null settings-xml =
{if restore-settings? then
{get-persistent-data "settings", error-if-missing? = false}
else null}
then
{try
set settings =
{build-xml preserve-whitespace? = false,
settings-xml}.root
catch e:Exception do
{popup-message {message Ignoring faulty settings}}}}
let settings-context:XDMDataBindingContext =
{XDMDataBindingContext model = settings}
{on-applet-suspend do
{set-persistent-data "settings",
{settings.to-XML}}
{commit-persistent-data}}

|| model
let xmldoc:#XDMDocument = null
let xml:#XDMElement = null
|| model tree
let tm:XDMTreeModel =
{XDMTreeModel null}
let tc:XDMTreeControl =
{XDMTreeControl tm,
tree-connector-color = "silver",
selection-policy = "multiple"}

|| subset
let xs:XDMNodeSet = {XDMNodeSet}
|| subset tree
let stm:XDMTreeModel =
{XDMTreeModel null}
let stc:XDMTreeControl =
{XDMTreeControl stm,
tree-connector-color = "silver",
xdm-text-breakable? = true,
selection-policy = "multiple"}

|| actions
let search-status:TextDisplay = {TextDisplay}
let search-command:ComboBox =
{ComboBox
{bind value to "path"},
width = 4in,
vorigin = 70%,
{on ValueFinished at c:ComboBox do
{with-busy-cursor
|| clear highlighted nodes
{tc.clear-categories category = "highlight"}
{tc.select-nothing}
{if c.value.empty? then
set stm.root = null
else
{try
|| apply specified xpath
let w:StopWatch = {StopWatch}
{w.start}
set xs = {xml.search c.value}
{w.stop}
set search-status.value =
{message Found {value xs.size} nodes in {w.elapsed}}
set stm.root = xs asa XDMTreeNode
{stc.expand-node {non-null stm.root}}
|| note new setting
{c.update-data-binding "value"}
|| remember new query
{if {c.find c.value} < 0 then
{c.append c.value}}
catch e:Exception do
set search-status.value = e.message
{popup-message e}}
}}}}
let load:{proc-type {Url}:void} =
{proc {loc:Url}:void
{try
|| load document
set xmldoc =
{build-xml loc, preserve-whitespace? = false}
set xml = xmldoc.root
|| populate displays
set tm.root = xml asa XDMTreeNode
{tc.expand-node {non-null tm.root}}
|| augment global prefix declarations
{dwim-prefix-declarations {non-null xml}}
|| reset path
||-- {search-command.clear-items}
{search-command.set-value-with-events "*"}
catch e:Exception do
{popup-message e}}}
let load-command:DropdownList =
{DropdownList
"inventory.xml",
"samples.xml",
{bind value to "filename"},
vorigin = 70%,
{on ValueFinished at c:DropdownList do
{with-busy-cursor
{load {base-url.concat c.value}}
|| note new setting
{c.update-data-binding "value"}
|| remember new query
{if {c.find c.value} < 0 then
{c.append c.value}}}}}

|| display
let pane-style:Arguments =
{Arguments
border-width = 3px,
border-style = "ridge"}
let display-full:Graphic =
{VBox
margin = 3px,
width = 2in,
tc,
{Fill}}
let display-subset:Graphic =
{VBox
margin = 3px,
{HBox
spacing = 3pt,
margin = 3pt,
{text XPath:},
search-command,
{CommandButton
label = {message Select},
{on Action do
|| select corresponding tree nodes
{with-busy-cursor do
{tc.collapse-node {non-null tm.root},
collapse-descendants? = true}
{for x in xs do
let n:XDMTreeNode = tm[x]
{tc.select-nodes additive? = true, n}}}}},
{CommandButton
label = {message Highlight},
{on Action do
|| highlight corresponding tree nodes
{with-busy-cursor do
|| clear this category
{tc.clear-categories category = "highlight"}
{for x in xs do
let n:XDMTreeNode = tm[x]
|| assure node is visible
{if-non-null p = n.parent then
{tc.expand-node p,
expand-ancestors? = true}}
{tc.set-category n, "highlight"}
}}}},
{Fill}},
{ScrollBox
hstretch? = true,
hscroll? = false,
stc},
{Fill},
search-status}

|| behavior
let expand-action:EventHandler =
{on Action at c:TreeControl do
{if-non-null node = c.current-node then
{with-busy-cursor
{if {c.expanded-nodes.member? node} then
{c.collapse-node node,
collapse-descendants? = true}
else
{c.expand-node node,
expand-descendants? = true}}}}}
{tc.add-event-handler expand-action}
{stc.add-event-handler expand-action}

|| arrangement
let main:PanedWindow =
{PanedWindow orientation = "horizontal"}
{main.add-pane
{ScrollBox
margin = 1px,
hstretch? = true,
vstretch? = true,
{splice pane-style},
display-full}}
{main.add-pane
{Frame
margin = 1px,
hstretch? = true,
vstretch? = true,
{splice pane-style},
display-subset}}
{main.set-pane-sizes {{Array-of double} 30%, 70%}}

|| initialize
{after 0s do
{load-command.handle-event {ValueFinished}}}

|| layout
{VBox
data-binding-context = settings-context,
margin = 3pt,
spacing = 3pt,
{splice pane-style},
{HBox
spacing = 3pt,
margin = 3pt,
{title XDMPath Display},
{Fill width = .5in},
{text File:},
load-command,
{Fill},
{CommandButton
label = {message Restore Settings},
{on Action do
set settings = {factory-settings.clone}
set settings-context.model = settings
{load-command.handle-event {ValueFinished}}
}}},
{hrule},
main
}

}

Since the above applet allows us to quickly view XPath query results, XDMPath Display is definitely an asset for developers. We have made this available for you to download on your machine to use with other XML files. Please select the following link to download a zip file of the XPath Explorer: XPath Explorer. Once you unzip the files, select xpath-explorer.curl to run the applet. Note that this Curl applet is also included within the WSDK documentation.


XPath in a Nutshell
Just to summarize using XPath expressions, we have put together a list of points worth noting:


•Path expressions navigate nodes in an XML document (these resemble filesystem paths).

•Paths return collections of nodes (nodesets)

•Absolute paths, starting with '/' begin at the start of the document. Other paths are relative.

•Paths consist of steps separated by slashes '/'.

•Each step builds on the nodeset from the previous step.

•Steps can follow different "axes": child elements (by name), attributes (name prefixed with '@'), descendants (starting wtih '//').

•Steps can be filtered using a predicate inside square brackets \[ \].





Of course, this is just a short list. There is a lot more on XPath such as datatypes, functions, axes, shorthand, context-node, and operators. For a complete listing of XPath expressions, please see W3C XML Path Language (XPath).