XML/XPath
You are encouraged to solve this task according to the task description, using any language you may know.
Perform the following three XPath queries on the XML Document below:
- Retrieve the first "item" element
- Perform an action on each "price" element (print it out)
- Get an array of all the "name" elements
XML Document:
<inventory title="OmniCorp Store #45x10^3"><item upc="123456789" stock="12"> <name>Invisibility Cream</name> <price>14.50</price> <description>Makes you invisible</description> </item> <item upc="445322344" stock="18"> <name>Levitation Salve</name> <price>23.99</price> <description>Levitate yourself for up to 3 hours per application</description> </item> <item upc="485672034" stock="653"> <name>Blork and Freen Instameal</name> <price>4.95</price> <description>A tasty meal in a tablet; just add water</description> </item> <item upc="132957764" stock="44"> <name>Grob winglets</name> <price>3.56</price> <description>Tender winglets of Grob. Just add water</description> </item> </inventory>
AutoHotkey
With regular expressions <lang AutoHotkey> FileRead, inventory, xmlfile.xml
RegExMatch(inventory, "<item.*?</item>", item1) MsgBox % item1
pos = 1 While, pos := RegExMatch(inventory, "<price>(.*?)</price>", price, pos + 1)
MsgBox % price1
While, pos := RegExMatch(inventory, "<name>.*?</name>", name, pos + 1)
names .= name . "`n"
MsgBox % names </lang>
<lang AutoHotkey>
- Include xpath.ahk
xpath_load(doc, "xmlfile.xml")
- Retrieve the first "item" element
MsgBox % xpath(doc, "/inventory/section[1]/item[1]/text()")
- Perform an action on each "price" element (print it out)
prices := xpath(doc, "/inventory/section/item/price/text()") Loop, Parse, prices,`,
reordered .= A_LoopField "`n"
MsgBox % reordered
- Get an array of all the "name" elements
MsgBox % xpath(doc, "/inventory/section/item/name") </lang>
C#
<lang csharp> XmlReader XReader;
// Either read the xml from a string ... XReader = XmlReader.Create(new StringReader("<inventory title=... </inventory>")); // ... or read it from the file system. XReader = XmlReader.Create("xmlfile.xml"); // Create a XPathDocument object (which implements the IXPathNavigable interface) // which is optimized for XPath operation. (very fast). IXPathNavigable XDocument = new XPathDocument(XReader); // Create a Navigator to navigate through the document. XPathNavigator Nav = XDocument.CreateNavigator(); Nav = Nav.SelectSingleNode("//item"); // Move to the first element of the selection. (if available). if(Nav.MoveToFirst()) { Console.WriteLine(Nav.OuterXml); // The outer xml of the first item element. } // Get an iterator to loop over multiple selected nodes. XPathNodeIterator Iterator = XDocument.CreateNavigator().Select("//price"); while (Iterator.MoveNext()) { Console.WriteLine(Iterator.Current.Value); } Iterator = XDocument.CreateNavigator().Select("//name"); // Use a generic list. List<string> NodesValues = new List<string>(); while (Iterator.MoveNext()) { NodesValues.Add(Iterator.Current.Value); } // Convert the generic list to an array and output the count of items. Console.WriteLine(NodesValues.ToArray().Length);</lang>
ColdFusion
<cfsavecontent variable="xmlString"> <inventory ... </inventory> </cfsavecontent> <cfset xml = xmlParse(xmlString)> <!--- First Task ---> <cfset itemSearch = xmlSearch(xml, "//item")> <!--- item = the first Item (xml element object) ---> <cfset item = itemSearch[1]> <!--- Second Task ---> <cfset priceSearch = xmlSearch(xml, "//price")> <!--- loop and print each price ---> <cfloop from="1" to="#arrayLen(priceSearch)#" index="i"> #priceSearch[i].xmlText#<br/> </cfloop> <!--- Third Task ---> <!--- array of all the name elements ---> <cfset names = xmlSearch(xml, "//name")> <!--- visualize the results ---> <cfdump var="#variables#">
Groovy
<lang groovy>def inventory = new XmlSlurper().parseText("<inventory...") //optionally parseText(new File("inv.xml").text) def firstItem = inventory.section.item[0] //1. first item inventory.section.item.price.each { println it } //2. print each price def allNamesArray = inventory.section.item.name.collect {it} //3. collect item names into an array</lang>
JavaScript
<lang javascript> //create XMLDocument object from file
var xhr = new XMLHttpRequest(); xhr.open('GET', 'file.xml', false); xhr.send(null); var doc = xhr.responseXML; //get first <item> element var firstItem = doc.evaluate( '//item[1]', doc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null ).singleNodeValue; alert( firstItem.textContent ); //output contents of <price> elements var prices = doc.evaluate( '//price', doc, null, XPathResult.ANY_TYPE, null ); for( var price = prices.iterateNext(); price != null; price = prices.iterateNext() ) { alert( price.textContent ); } //add <name> elements to array var names = doc.evaluate( '//name', doc, null, XPathResult.ANY_TYPE, null); var namesArray = []; for( var name = names.iterateNext(); name != null; name = names.iterateNext() ) { namesArray.push( name ); } alert( namesArray );</lang>
Although some browsers support XPath, working with XML is much easier with E4X.
<lang javascript> //create XML object from file
var xhr = new XMLHttpRequest(); xhr.open('GET', 'file.xml', false); xhr.send(null); var doc = new XML(xhr.responseText); //get first <item> element var firstItem = doc..item[0]; alert( firstItem ); //output contents of <price> elements for each( var price in doc..price ) { alert( price ); } //add <name> elements to array var names = []; for each( var name in doc..name ) { names.push( name ); } alert( names );</lang>
Perl
<lang perl> use XML::XPath qw();
my $x = XML::XPath->new('<inventory ... </inventory>'); [$x->findnodes('//item[1]')->get_nodelist]->[0]; print $x->findnodes_as_string('//price'); $x->findnodes('//name')->get_nodelist;</lang>
PHP
<lang php>
<?php //PHP5 only example due to changes in XML extensions between version 4 and 5 (Tested on PHP5.2.0) $doc = DOMDocument::loadXML('<inventory title="OmniCorp Store #45x10^3">...</inventory>'); //Load from file instead with $doc = DOMDocument::load('filename'); $xpath = new DOMXPath($doc); /* 1st Task: Retrieve the first "item" element */ $nodelist = $xpath->query('//item'); $result = $nodelist->item(0); /* 2nd task: Perform an action on each "price" element (print it out) */ $nodelist = $xpath->query('//price'); for($i = 0; $i < $nodelist->length; $i++) { //print each price element in the DOMNodeList instance, $nodelist, as text/xml followed by a newline print $doc->saveXML($nodelist->item($i))."\n"; } /* 3rd Task: Get an array of all the "name" elements */ $nodelist = $xpath->query('//name'); //our array to hold all the name elements, though in practice you'd probably not need to do this and simply use the DOMNodeList $result = array(); //a different way of iterating through the DOMNodeList foreach($nodelist as $node) { $result[] = $node; }
</lang>
Python
<lang python>
- Python has basic xml parsing built in
from xml.dom import minidom
xmlfile = file("test3.xml") # load xml document from file xmldoc = xmldom.parse(xmlfile).documentElement # parse from file stream or... xmldoc = xmldom.parseString("<inventory title="OmniCorp Store #45x10^3">...</inventory>").documentElement # alternatively, parse a string
- 1st Task: Retrieve the first "item" element
i = xmldoc.getElementsByTagName("item") # get a list of all "item" tags firstItemElement = i[0] # get the first element
- 2nd task: Perform an action on each "price" element (print it out)
for j in xmldoc.getElementsByTagName("price"): # get a list of all "price" tags print j.childNodes[0].data # XML Element . TextNode . data of textnode
- 3rd Task: Get an array of all the "name" elements
namesArray = xmldoc.getElementsByTagName("name") </lang>
Ruby
<lang ruby> #Example taken from the REXML tutorial (http://www.germane-software.com/software/rexml/docs/tutorial.html)
require "rexml/document" include REXML #create the REXML Document from the string (%q is Ruby's multiline string, everything between the two @-characters is the string) doc = Document.new( %q@<inventory title="OmniCorp Store #45x10^3"> ... </inventory> @ ) # The invisibility cream is the first <item> invisibility = XPath.first( doc, "//item" ) # Prints out all of the prices XPath.each( doc, "//price") { |element| puts element.text } # Gets an array of all of the "name" elements in the document. names = XPath.match( doc, "//name" )</lang>
Tcl
<lang tcl># assume $xml holds the XML data package require tdom set doc [dom parse $xml] set root [$doc documentElement]
set allNames [$root selectNodes //name] puts [llength $allNames] ;# ==> 4
set firstItem [lindex [$root selectNodes //item] 0] puts [$firstItem @upc] ;# ==> 123456789
foreach node [$root selectNodes //price] {
puts [$node text]
}</lang>
Visual Basic .NET
Dim first_item = xml.XPathSelectElement("//item") Console.WriteLine(first_item) For Each price In xml.XPathSelectElements("//price") Console.WriteLine(price.Value) Next Dim names = (From item In xml.XPathSelectElements("//name") Select item.Value).ToArray
XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text" /> <xsl:template match="/"> <!-- 1. first item element --> <xsl:text> The first item element is</xsl:text> <xsl:value-of select="//item[1]" /> <!-- 2. Print each price element --> <xsl:text> The prices are: </xsl:text> <xsl:for-each select="//price"> <xsl:text> </xsl:text> <xsl:copy-of select="." /> </xsl:for-each> <!-- 3. Collect all the name elements --> <xsl:text> The names are: </xsl:text> <xsl:copy-of select="//name" /> </xsl:template> </xsl:stylesheet>