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:
- //item[1]: Retrieve the first "item" element
- //price/text(): Perform an action on each "price" element (print it out)
- //name: 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>
AArch64 Assembly
<lang AArch64 Assembly> /* ARM assembly AARCH64 Raspberry PI 3B */ /* program xpathXml64.s */
/*******************************************/ /* Constantes file */ /*******************************************/ /* for this file see task include a file in language AArch64 assembly*/ .include "../includeConstantesARM64.inc"
.equ NBMAXELEMENTS, 100
/*******************************************/ /* Structures */ /********************************************/ /* structure xmlNode*/
.struct 0
xmlNode_private: // application data
.struct xmlNode_private + 8
xmlNode_type: // type number, must be second !
.struct xmlNode_type + 8
xmlNode_name: // the name of the node, or the entity
.struct xmlNode_name + 8
xmlNode_children: // parent->childs link
.struct xmlNode_children + 8
xmlNode_last: // last child link
.struct xmlNode_last + 8
xmlNode_parent: // child->parent link
.struct xmlNode_parent + 8
xmlNode_next: // next sibling link
.struct xmlNode_next + 8
xmlNode_prev: // previous sibling link
.struct xmlNode_prev + 8
xmlNode_doc: // the containing document
.struct xmlNode_doc + 8
xmlNode_ns: // pointer to the associated namespace
.struct xmlNode_ns + 8
xmlNode_content: // the content
.struct xmlNode_content + 8
xmlNode_properties: // properties list
.struct xmlNode_properties + 8
xmlNode_nsDef: // namespace definitions on this node
.struct xmlNode_nsDef + 8
xmlNode_psvi: // for type/PSVI informations
.struct xmlNode_psvi + 8
xmlNode_line: // line number
.struct xmlNode_line + 4
xmlNode_extra: // extra data for XPath/XSLT
.struct xmlNode_extra + 4
xmlNode_fin: /********************************************/ /* structure xmlNodeSet*/
.struct 0
xmlNodeSet_nodeNr: // number of nodes in the set
.struct xmlNodeSet_nodeNr + 4
xmlNodeSet_nodeMax: // size of the array as allocated
.struct xmlNodeSet_nodeMax + 4
xmlNodeSet_nodeTab: // array of nodes in no particular order
.struct xmlNodeSet_nodeTab + 8
xmlNodeSet_fin: /********************************************/ /* structure xmlXPathObject*/
.struct 0
xmlPathObj_type: //
.struct xmlPathObj_type + 8
xmlPathObj_nodesetval: //
.struct xmlPathObj_nodesetval + 8
xmlPathObj_boolval: //
.struct xmlPathObj_boolval + 8
xmlPathObj_floatval: //
.struct xmlPathObj_floatval + 8
xmlPathObj_stringval: //
.struct xmlPathObj_stringval + 8
xmlPathObj_user: //
.struct xmlPathObj_user + 8
xmlPathObj_index: //
.struct xmlPathObj_index + 8
xmlPathObj_usex2: //
.struct xmlPathObj_usex2 + 8
xmlPathObj_index2: //
.struct xmlPathObj_index2 + 8
/*********************************/ /* Initialized data */ /*********************************/ .data szMessEndpgm: .asciz "\nNormal end of program.\n" szMessDisVal: .asciz "\nDisplay set values.\n" szMessDisArea: .asciz "\nDisplay area values.\n" szFileName: .asciz "testXml.xml" szMessError: .asciz "Error detected !!!!. \n"
szLibName: .asciz "name"
szLibPrice: .asciz "//price"
szLibExtName: .asciz "//name"
szCarriageReturn: .asciz "\n"
/*********************************/
/* UnInitialized data */
/*********************************/
.bss
.align 4
tbExtract: .skip 8 * NBMAXELEMENTS // result extract area
/*********************************/
/* code section */
/*********************************/
.text
.global main
main: // entry of program
ldr x0,qAdrszFileName bl xmlParseFile // create doc cbz x0,99f // error ? mov x19,x0 // doc address mov x0,x19 // doc bl xmlDocGetRootElement // get root bl xmlFirstElementChild // get first section bl xmlFirstElementChild // get first item bl xmlFirstElementChild // get first name bl xmlNodeGetContent // extract content bl affichageMess // for display ldr x0,qAdrszCarriageReturn bl affichageMess ldr x0,qAdrszMessDisVal bl affichageMess mov x0,x19 ldr x1,qAdrszLibPrice // extract prices bl extractValue mov x0,x19 ldr x1,qAdrszLibExtName // extract names bl extractValue ldr x0,qAdrszMessDisArea bl affichageMess mov x4,#0 // display string result area ldr x5,qAdrtbExtract
1:
ldr x0,[x5,x4,lsl #3] cbz x0,2f bl affichageMess ldr x0,qAdrszCarriageReturn bl affichageMess add x4,x4,1 b 1b
2:
mov x0,x19 bl xmlFreeDoc bl xmlCleanupParser ldr x0,qAdrszMessEndpgm bl affichageMess b 100f
99: // error
ldr x0,qAdrszMessError bl affichageMess
100: // standard end of the program
mov x0,0 // return code mov x8,EXIT // request to exit program svc 0 // perform the system call
qAdrszMessError: .quad szMessError qAdrszMessEndpgm: .quad szMessEndpgm qAdrszLibName: .quad szLibName qAdrszLibPrice: .quad szLibPrice qAdrszCarriageReturn: .quad szCarriageReturn qAdrszFileName: .quad szFileName qAdrszLibExtName: .quad szLibExtName qAdrtbExtract: .quad tbExtract qAdrszMessDisVal: .quad szMessDisVal qAdrszMessDisArea: .quad szMessDisArea /******************************************************************/ /* extract value of set */ /******************************************************************/ /* x0 contains the doc address /* x1 contains the address of the libel to extract */ extractValue:
stp x19,lr,[sp,-16]! // save registers stp x20,x21,[sp,-16]! // save registers stp x22,x23,[sp,-16]! // save registers mov x20,x1 // save address libel mov x19,x0 // save doc bl xmlXPathNewContext // create context mov x23,x0 // save context mov x1,x0 mov x0,x20 bl xmlXPathEvalExpression mov x21,x0 mov x0,x23 bl xmlXPathFreeContext // free context cmp x21,#0 beq 100f ldr x14,[x21,#xmlPathObj_nodesetval] // values set ldr w23,[x14,#xmlNodeSet_nodeNr] // set size mov x22,#0 // index ldr x20,[x14,#xmlNodeSet_nodeTab] // area of nodes ldr x21,qAdrtbExtract
1: // start loop
ldr x3,[x20,x22,lsl #3] // load node mov x0,x19 ldr x1,[x3,#xmlNode_children] // load string value mov x2,#1 bl xmlNodeListGetString str x0,[x21,x22,lsl #3] // store string pointer in area bl affichageMess // and display string result ldr x0,qAdrszCarriageReturn bl affichageMess add x22,x22,1 cmp x22,x23 blt 1b
100:
ldp x22,x23,[sp],16 // restaur 2 registers ldp x20,x21,[sp],16 // restaur 2 registers ldp x19,lr,[sp],16 // restaur 2 registers ret // return to address lr x30
/********************************************************/ /* File Include fonctions */ /********************************************************/ /* for this file see task include a file in language AArch64 assembly */ .include "../includeARM64.inc" </lang>
- Output:
Invisibility Cream Display set values. 14.50 23.99 4.95 3.56 Invisibility Cream Levitation Salve Blork and Freen Instameal Grob winglets Display area values. Invisibility Cream Levitation Salve Blork and Freen Instameal Grob winglets Normal end of program.
AppleScript
Using System Events
AppleScript has no-built in support for XPath, but it could be used via a 'do shell script' command. Here's a solution using Apple System Events.
<lang AppleScript>set theXMLdata to "<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>"
on getElementValuesByName(theXML, theNameToFind) set R to {} tell application "System Events" repeat with i in theXML set {theName, theElements} to {i's name, i's XML elements} if (count of theElements) > 0 then set R to R & my getElementValuesByName(theElements, theNameToFind) if theName = theNameToFind then set R to R & i's value end repeat end tell return R end getElementValuesByName
on getBlock(theXML, theItem, theInstance) set text item delimiters to "" repeat with i from 1 to theInstance set {R, blockStart, blockEnd} to {{}, "<" & theItem & space, "</" & theItem & ">"} set x to offset of blockStart in theXML if x = 0 then exit repeat set y to offset of blockEnd in (characters x thru -1 of theXML as string) if y = 0 then exit repeat set R to characters x thru (x + y + (length of blockEnd) - 2) of theXML as string set theXML to characters (y + (length of blockEnd)) thru -1 of theXML as string end repeat return R end getBlock
tell application "System Events" set xmlData to make new XML data with properties {name:"xmldata", text:theXMLdata}
return my getBlock(xmlData's text, "item", 1) -- Solution to part 1 of problem. return my getElementValuesByName(xmlData's contents, "name") -- Solution to part 2 of problem. return my getElementValuesByName(xmlData's contents, "price") -- Solution to part 3 of problem.
end tell</lang>Output for the three results (respectively):<lang AppleScript>"<item upc=\"123456789\" stock=\"12\">
<name>Invisibility Cream</name> <price>14.50</price> <description>Makes you invisible</description> </item>"
{"Invisibility Cream", "Levitation Salve", "Blork and Freen Instameal", "Grob winglets"}
{"14.50", "23.99", "4.95", "3.56"}</lang>
ARM Assembly
<lang ARM Assembly> /* ARM assembly Raspberry PI */ /* program xpathXml.s */
/* Constantes */ .equ STDOUT, 1 @ Linux output console .equ EXIT, 1 @ Linux syscall .equ WRITE, 4 @ Linux syscall
.equ NBMAXELEMENTS, 100
/*******************************************/ /* Structures */ /********************************************/ /* structure xmlNode*/
.struct 0
xmlNode_private: @ application data
.struct xmlNode_private + 4
xmlNode_type: @ type number, must be second !
.struct xmlNode_type + 4
xmlNode_name: @ the name of the node, or the entity
.struct xmlNode_name + 4
xmlNode_children: @ parent->childs link
.struct xmlNode_children + 4
xmlNode_last: @ last child link
.struct xmlNode_last + 4
xmlNode_parent: @ child->parent link
.struct xmlNode_parent + 4
xmlNode_next: @ next sibling link
.struct xmlNode_next + 4
xmlNode_prev: @ previous sibling link
.struct xmlNode_prev + 4
xmlNode_doc: @ the containing document
.struct xmlNode_doc + 4
xmlNode_ns: @ pointer to the associated namespace
.struct xmlNode_ns + 4
xmlNode_content: @ the content
.struct xmlNode_content + 4
xmlNode_properties: @ properties list
.struct xmlNode_properties + 4
xmlNode_nsDef: @ namespace definitions on this node
.struct xmlNode_nsDef + 4
xmlNode_psvi: @ for type/PSVI informations
.struct xmlNode_psvi + 4
xmlNode_line: @ line number
.struct xmlNode_line + 4
xmlNode_extra: @ extra data for XPath/XSLT
.struct xmlNode_extra + 4
xmlNode_fin: /********************************************/ /* structure xmlNodeSet*/
.struct 0
xmlNodeSet_nodeNr: @ number of nodes in the set
.struct xmlNodeSet_nodeNr + 4
xmlNodeSet_nodeMax: @ size of the array as allocated
.struct xmlNodeSet_nodeMax + 4
xmlNodeSet_nodeTab: @ array of nodes in no particular order
.struct xmlNodeSet_nodeTab + 4
xmlNodeSet_fin: /********************************************/ /* structure xmlXPathObject*/
.struct 0
xmlPathObj_type: @
.struct xmlPathObj_type + 4
xmlPathObj_nodesetval: @
.struct xmlPathObj_nodesetval + 4
xmlPathObj_boolval: @
.struct xmlPathObj_boolval + 4
xmlPathObj_floatval: @
.struct xmlPathObj_floatval + 4
xmlPathObj_stringval: @
.struct xmlPathObj_stringval + 4
xmlPathObj_user: @
.struct xmlPathObj_user + 4
xmlPathObj_index: @
.struct xmlPathObj_index + 4
xmlPathObj_user2: @
.struct xmlPathObj_user2 + 4
xmlPathObj_index2: @
.struct xmlPathObj_index2 + 4
/*********************************/ /* Initialized data */ /*********************************/ .data szMessEndpgm: .asciz "\nNormal end of program.\n" szMessDisVal: .asciz "\nDisplay set values.\n" szMessDisArea: .asciz "\nDisplay area values.\n" szFileName: .asciz "testXml.xml" szMessError: .asciz "Error detected !!!!. \n"
szLibName: .asciz "name"
szLibPrice: .asciz "//price"
szLibExtName: .asciz "//name"
szCarriageReturn: .asciz "\n"
/*********************************/
/* UnInitialized data */
/*********************************/
.bss
.align 4
tbExtract: .skip 4 * NBMAXELEMENTS @ result extract area
/*********************************/
/* code section */
/*********************************/
.text
.global main
main: @ entry of program
ldr r0,iAdrszFileName bl xmlParseFile @ create doc mov r9,r0 @ doc address mov r0,r9 @ doc bl xmlDocGetRootElement @ get root bl xmlFirstElementChild @ get first section bl xmlFirstElementChild @ get first item bl xmlFirstElementChild @ get first name bl xmlNodeGetContent @ extract content bl affichageMess @ for display ldr r0,iAdrszCarriageReturn bl affichageMess
ldr r0,iAdrszMessDisVal bl affichageMess mov r0,r9 ldr r1,iAdrszLibPrice @ extract prices bl extractValue mov r0,r9 ldr r1,iAdrszLibExtName @ extact names bl extractValue ldr r0,iAdrszMessDisArea bl affichageMess mov r4,#0 @ display string result area ldr r5,iAdrtbExtract
1:
ldr r0,[r5,r4,lsl #2] cmp r0,#0 beq 2f bl affichageMess ldr r0,iAdrszCarriageReturn bl affichageMess add r4,#1 b 1b
2:
mov r0,r9 bl xmlFreeDoc bl xmlCleanupParser ldr r0,iAdrszMessEndpgm bl affichageMess b 100f
99:
@ error ldr r0,iAdrszMessError bl affichageMess
100: @ standard end of the program
mov r0, #0 @ return code mov r7, #EXIT @ request to exit program svc #0 @ perform the system call
iAdrszMessError: .int szMessError iAdrszMessEndpgm: .int szMessEndpgm iAdrszLibName: .int szLibName iAdrszLibPrice: .int szLibPrice iAdrszCarriageReturn: .int szCarriageReturn iAdrszFileName: .int szFileName iAdrszLibExtName: .int szLibExtName iAdrtbExtract: .int tbExtract iAdrszMessDisVal: .int szMessDisVal iAdrszMessDisArea: .int szMessDisArea /******************************************************************/ /* extract value of set */ /******************************************************************/ /* r0 contains the doc address /* r1 contains the address of the libel to extract */ extractValue:
push {r1-r10,lr} @ save registres mov r4,r1 @ save address libel mov r9,r0 @ save doc ldr r8,iAdrtbExtract bl xmlXPathNewContext @ create context mov r10,r0 mov r1,r0 mov r0,r4 bl xmlXPathEvalExpression mov r5,r0 mov r0,r10 bl xmlXPathFreeContext @ free context cmp r5,#0 beq 100f ldr r4,[r5,#xmlPathObj_nodesetval] @ values set ldr r6,[r4,#xmlNodeSet_nodeNr] @ set size mov r7,#0 @ index ldr r4,[r4,#xmlNodeSet_nodeTab] @ area of nods
1: @ start loop
ldr r3,[r4,r7,lsl #2] @ load node mov r0,r9 ldr r1,[r3,#xmlNode_children] @ load string value mov r2,#1 bl xmlNodeListGetString str r0,[r8,r7,lsl #2] @ store string pointer in area bl affichageMess @ and display string result ldr r0,iAdrszCarriageReturn bl affichageMess add r7,#1 cmp r7,r6 blt 1b
100:
pop {r1-r10,lr} @ restaur registers */ bx lr @ return
/******************************************************************/ /* display text with size calculation */ /******************************************************************/ /* r0 contains the address of the message */ affichageMess:
push {r0,r1,r2,r7,lr} @ save registres mov r2,#0 @ counter length
1: @ loop length calculation
ldrb r1,[r0,r2] @ read octet start position + index cmp r1,#0 @ if 0 its over addne r2,r2,#1 @ else add 1 in the length bne 1b @ and loop @ so here r2 contains the length of the message mov r1,r0 @ address message in r1 mov r0,#STDOUT @ code to write to the standard output Linux mov r7, #WRITE @ code call system "write" svc #0 @ call systeme pop {r0,r1,r2,r7,lr} @ restaur registers */ bx lr @ return
</lang>
- Output:
Invisibility Cream Display set values. 14.50 23.99 4.95 3.56 Invisibility Cream Levitation Salve Blork and Freen Instameal Grob winglets Display area values. Invisibility Cream Levitation Salve Blork and Freen Instameal Grob winglets Normal end of program.
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>
Bracmat
<lang bracmat>{Retrieve the first "item" element} ( nestML$(get$("doc.xml",X,ML))
: ? ( inventory . ?,? (section.?,? ((item.?):?item) ?) ? ) ?
& out$(toML$!item) )
{Perform an action on each "price" element (print it out)} ( nestML$(get$("doc.xml",X,ML))
: ? ( inventory . ? , ? ( section . ? , ? ( item . ? , ? ( price . ? , ?price & out$!price & ~ ) ? ) ? ) ? ) ?
| )
{Get an array of all the "name" elements} ( :?anArray
& nestML$(get$("doc.xml",X,ML)) : ? ( inventory . ? , ? ( section . ? , ? ( item . ? , ? ( name . ? , ?name & !anArray !name:?anArray & ~ ) ? ) ? ) ? ) ?
| out$!anArray {Not truly an array, but a list.} );</lang>
C
Takes XML document and XPath expression as inputs and prints results, usage is printed if invoked incorrectly. <lang C>
- include <libxml/parser.h>
- include <libxml/xpath.h>
xmlDocPtr getdoc (char *docname) { xmlDocPtr doc; doc = xmlParseFile(docname);
return doc; }
xmlXPathObjectPtr getnodeset (xmlDocPtr doc, xmlChar *xpath){
xmlXPathContextPtr context; xmlXPathObjectPtr result;
context = xmlXPathNewContext(doc);
result = xmlXPathEvalExpression(xpath, context); xmlXPathFreeContext(context);
return result; }
int main(int argc, char **argv) {
if (argc <= 2) { printf("Usage: %s <XML Document Name> <XPath expression>\n", argv[0]); return 0; }
char *docname; xmlDocPtr doc; xmlChar *xpath = (xmlChar*) argv[2]; xmlNodeSetPtr nodeset; xmlXPathObjectPtr result; int i; xmlChar *keyword;
docname = argv[1]; doc = getdoc(docname); result = getnodeset (doc, xpath); if (result) { nodeset = result->nodesetval; for (i=0; i < nodeset->nodeNr; i++) { xmlNodePtr titleNode = nodeset->nodeTab[i]; keyword = xmlNodeListGetString(doc, titleNode->xmlChildrenNode, 1); printf("Value %d: %s\n",i+1, keyword); xmlFree(keyword); } xmlXPathFreeObject (result); } xmlFreeDoc(doc); xmlCleanupParser(); return 0; } </lang> testXML.xml contains the XML mentioned in the task description. Code must be compiled with the correct flags.
C:\rosettaCode>xPather.exe testXML.xml //price Value 1: 14.50 Value 2: 23.99 Value 3: 4.95 Value 4: 3.56 C:\rosettaCode>xPather.exe testXML.xml //name Value 1: Invisibility Cream Value 2: Levitation Salve Value 3: Blork and Freen Instameal Value 4: Grob winglets
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>
C++
The following code uses each of the three tasks given to demonstrate a different method of handling the resulting node set from an XPath query.
<lang cpp>#include <cassert>
- include <cstdlib>
- include <iostream>
- include <stdexcept>
- include <utility>
- include <vector>
- include <libxml/parser.h>
- include <libxml/tree.h>
- include <libxml/xmlerror.h>
- include <libxml/xmlstring.h>
- include <libxml/xmlversion.h>
- include <libxml/xpath.h>
- ifndef LIBXML_XPATH_ENABLED
- error libxml was not configured with XPath support
- endif
// Because libxml2 is a C library, we need a couple things to make it work // well with modern C++: // 1) a ScopeGuard-like type to handle cleanup functions; and // 2) an exception type that transforms the library's errors.
// ScopeGuard-like type to handle C library cleanup functions. template <typename F> class nodiscard scope_exit { public:
// C++20: Constructor can (and should) be nodiscard. /*nodiscard*/ constexpr explicit scope_exit(F&& f) : f_{std::move(f)} {}
~scope_exit() { f_(); }
// Non-copyable, non-movable. scope_exit(scope_exit const&) = delete; scope_exit(scope_exit&&) = delete; auto operator=(scope_exit const&) -> scope_exit& = delete; auto operator=(scope_exit&&) -> scope_exit& = delete;
private:
F f_;
};
// Exception that gets last libxml2 error. class libxml_error : public std::runtime_error { public:
libxml_error() : libxml_error(std::string{}) {}
explicit libxml_error(std::string message) : std::runtime_error{make_message_(std::move(message))} {}
private:
static auto make_message_(std::string message) -> std::string { if (auto const last_error = ::xmlGetLastError(); last_error) { if (not message.empty()) message += ": "; message += last_error->message; }
return message; }
};
auto main() -> int {
try { // Initialize libxml. ::xmlInitParser(); LIBXML_TEST_VERSION auto const libxml_cleanup = scope_exit{[] { ::xmlCleanupParser(); }};
// Load and parse XML document. auto const doc = ::xmlParseFile("test.xml"); if (not doc) throw libxml_error{"failed to load document"}; auto const doc_cleanup = scope_exit{[doc] { ::xmlFreeDoc(doc); }};
// Create XPath context for document. auto const xpath_context = ::xmlXPathNewContext(doc); if (not xpath_context) throw libxml_error{"failed to create XPath context"}; auto const xpath_context_cleanup = scope_exit{[xpath_context] { ::xmlXPathFreeContext(xpath_context); }};
// Task 1 ============================================================ { std::cout << "Task 1:\n";
auto const xpath = reinterpret_cast<::xmlChar const*>(u8"//item[1]");
// Create XPath object (same for every task). auto xpath_obj = ::xmlXPathEvalExpression(xpath, xpath_context); if (not xpath_obj) throw libxml_error{"failed to evaluate XPath"}; auto const xpath_obj_cleanup = scope_exit{[xpath_obj] { ::xmlXPathFreeObject(xpath_obj); }};
// 'result' a xmlNode* to the desired node, or nullptr if it // doesn't exist. If not nullptr, the node is owned by 'doc'. auto const result = xmlXPathNodeSetItem(xpath_obj->nodesetval, 0); if (result) std::cout << '\t' << "node found" << '\n'; else std::cout << '\t' << "node not found" << '\n'; }
// Task 2 ============================================================ { std::cout << "Task 2:\n";
auto const xpath = reinterpret_cast<::xmlChar const*>(u8"//price/text()");
// Create XPath object (same for every task). auto xpath_obj = ::xmlXPathEvalExpression(xpath, xpath_context); if (not xpath_obj) throw libxml_error{"failed to evaluate XPath"}; auto const xpath_obj_cleanup = scope_exit{[xpath_obj] { ::xmlXPathFreeObject(xpath_obj); }};
// Printing the results. auto const count = xmlXPathNodeSetGetLength(xpath_obj->nodesetval); for (auto i = decltype(count){0}; i < count; ++i) { auto const node = xmlXPathNodeSetItem(xpath_obj->nodesetval, i); assert(node);
auto const content = XML_GET_CONTENT(node); assert(content);
// Note that reinterpret_cast here is a Bad Idea, because // 'content' is UTF-8 encoded, which may or may not be the // encoding cout expects. A *PROPER* solution would translate // content to the correct encoding (or at least verify that // UTF-8 *is* the correct encoding). // // But this "works" well enough for illustration. std::cout << "\n\t" << reinterpret_cast<char const*>(content); }
std::cout << '\n'; }
// Task 3 ============================================================ { std::cout << "Task 3:\n";
auto const xpath = reinterpret_cast<::xmlChar const*>(u8"//name");
// Create XPath object (same for every task). auto xpath_obj = ::xmlXPathEvalExpression(xpath, xpath_context); if (not xpath_obj) throw libxml_error{"failed to evaluate XPath"}; auto const xpath_obj_cleanup = scope_exit{[xpath_obj] { ::xmlXPathFreeObject(xpath_obj); }};
// 'results' is a vector of pointers to the result nodes. The // nodes pointed to are owned by 'doc'. auto const results = [ns=xpath_obj->nodesetval]() { auto v = std::vector<::xmlNode*>{}; if (ns && ns->nodeTab) v.assign(ns->nodeTab, ns->nodeTab + ns->nodeNr); return v; }(); std::cout << '\t' << "set of " << results.size() << " node(s) found" << '\n'; } } catch (std::exception const& x) { std::cerr << "ERROR: " << x.what() << '\n'; return EXIT_FAILURE; }
} </lang>
Caché ObjectScript
<lang cos>Class XML.Inventory [ Abstract ] {
XData XMLData { <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> }
ClassMethod QueryXMLDoc(Output names As %List) As %Status {
// get xml stream from the 'XData' block contained in this class Set xdata=##class(%Dictionary.CompiledXData).%OpenId($this_"||XMLData",, .sc) If $$$ISERR(sc) Quit sc Set sc=##class(%XML.XPATH.Document).CreateFromStream(xdata.Data, .xdoc) If $$$ISERR(sc) Quit sc // retrieve the first 'item' element Set sc=xdoc.EvaluateExpression("//section[1]", "item[1]", .res) // perform an action on each 'price' element (print it out) Set sc=xdoc.EvaluateExpression("//price", "text()", .res) If $$$ISERR(sc) Quit sc For i=1:1:res.Count() {
If i>1 Write ", " Write res.GetAt(i).Value
} // get an array of all the 'name' elements Set sc=xdoc.EvaluateExpression("//item", "name", .res) If $$$ISERR(sc) Quit sc Set key="" Do {
Set dom=res.GetNext(.key) If '$IsObject(dom) Quit While dom.Read() { If dom.HasValue Set $List(names, key)=dom.Value }
} While key'="" // finished Quit $$$OK
}
}</lang>
- Examples:
USER>Do ##class(XML.Inventory).QueryXMLDoc(.list) 14.50, 23.99, 4.95, 3.56 USER>Write $ListToString(list, ", ") Invisibility Cream, Levitation Salve, Blork and Freen Instameal, Grob winglets
CoffeeScript
<lang coffeescript> doc = new DOMParser().parseFromString '
<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>
', 'text/xml' </lang>
# "doc" is the XML as a Document object. Click expand to see parsing code ⇒
<lang coffeescript>
- Retrieve the first "item" element
doc.evaluate('//item', doc, {}, 7, {}).snapshotItem 0
- Perform an action on each "price" element (print it out)
prices = doc.evaluate "//price", doc, {}, 7, {} for i in [0...prices.snapshotLength] by 1
console.log prices.snapshotItem(i).textContent
- Get an array of all the "name" elements
names = doc.evaluate "//name", doc, {}, 7, {} names = for i in [0...names.snapshotLength] by 1
names.snapshotItem i
</lang>
ColdFusion
<lang cfm><cfsavecontent variable="xmlString"> <inventory ... </inventory> </cfsavecontent> <cfset xml = xmlParse(xmlString)> <cfset itemSearch = xmlSearch(xml, "//item")> <cfset item = itemSearch[1]> <cfset priceSearch = xmlSearch(xml, "//price")> <cfloop from="1" to="#arrayLen(priceSearch)#" index="i">
#priceSearch[i].xmlText#
</cfloop> <cfset names = xmlSearch(xml, "//name")> <cfdump var="#variables#"></lang>
Common Lisp
<lang lisp>(dolist (system '(:xpath :cxml-stp :cxml))
(asdf:oos 'asdf:load-op system))
(defparameter *doc* (cxml:parse-file "xml" (stp:make-builder)))
(xpath:first-node (xpath:evaluate "/inventory/section[1]/item[1]" *doc*))
(xpath:do-node-set (node (xpath:evaluate "/inventory/section/item/price/text()" *doc*))
(format t "~A~%" (stp:data node)))
(defun node-array (node-set)
(coerce (xpath:all-nodes node-set) 'vector))
(node-array
(xpath:evaluate "/inventory/section/item/name" *doc*))</lang>
D
It is important to note that the KXML library currently only supports XPath minimally.
<lang d>import kxml.xml; char[]xmlinput = "<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> "; void main() {
auto root = readDocument(xmlinput); auto firstitem = root.parseXPath("inventory/section/item")[0]; foreach(price;root.parseXPath("inventory/section/item/price")) { std.stdio.writefln("%s",price.getCData); } auto namearray = root.parseXPath("inventory/section/item/name");
}</lang>
Delphi
<lang Delphi>program XMLXPath;
{$APPTYPE CONSOLE}
uses ActiveX, MSXML;
const
XML = '<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>';
var
i: Integer; s: string; lXMLDoc: IXMLDOMDocument2; lNodeList: IXMLDOMNodeList; lNode: IXMLDOMNode; lItemNames: array of string;
begin
CoInitialize(nil); lXMLDoc := CoDOMDocument.Create; lXMLDoc.setProperty('SelectionLanguage', 'XPath'); lXMLDoc.loadXML(XML);
Writeln('First item node:'); lNode := lXMLDoc.selectNodes('//item')[0]; Writeln(lNode.xml); Writeln();
lNodeList := lXMLDoc.selectNodes('//price'); for i := 0 to lNodeList.length - 1 do Writeln('Price = ' + lNodeList[i].text); Writeln();
lNodeList := lXMLDoc.selectNodes('//item/name'); SetLength(lItemNames, lNodeList.length); for i := 0 to lNodeList.length - 1 do lItemNames[i] := lNodeList[i].text; for s in lItemNames do Writeln('Item name = ' + s);
end.</lang>
Output: <lang>First item node: <item upc="123456789" stock="12"> <name>Invisibility Cream</name> <price>14.50</price> <description>Makes you invisible</description> </item>
Price = 14.50 Price = 23.99 Price = 4.95 Price = 3.56
Item name = Invisibility Cream Item name = Levitation Salve Item name = Blork and Freen Instameal Item name = Grob winglets</lang>
E
(currently a very early work in progress/draft library; design comments welcome)
<lang e>? def xml__quasiParser := <import:org.switchb.e.xml.makeXMLQuasiParser>() > def xpath__quasiParser := xml__quasiParser.xPathQuasiParser() > null
? def doc := xml`<inventory title="OmniCorp Store #45x10^3"> >
>
> </inventory>`
- value: xml`...`
? doc[xpath`inventory/section/item`][0]
- value: xml`<item stock="12" upc="123456789">
- <name>Invisibility Cream</name>
- <price>14.50</price>
- <description>Makes you invisible</description>
- </item>`
? for price in doc[xpath`inventory/section/item/price/text()`] { println(price :String) } 14.50 23.99 4.95 3.56
? doc[xpath`inventory/section/item/name`]
- value: [xml`<name>Invisibility Cream</name>`,
- xml`<name>Levitation Salve</name>`,
- xml`<name>Blork and Freen Instameal</name>`,
- xml`<name>Grob winglets</name>`]
</lang>
Erlang
<lang erlang> -module(xml_xpath). -include_lib("xmerl/include/xmerl.hrl").
-export([main/0]).
main() ->
XMLDocument = "<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>", {Document,_} = xmerl_scan:string(XMLDocument),
io:format("First item:\n~s\n", [lists:flatten( xmerl:export_simple( [hd(xmerl_xpath:string("//item[1]", Document))], xmerl_xml, [{prolog, ""}]))]), io:format("Prices:\n"), [ io:format("~s\n",[Content#xmlText.value]) || #xmlElement{content = [Content|_]} <- xmerl_xpath:string("//price", Document)],
io:format("Names:\n"), [ Content#xmlText.value || #xmlElement{content = [Content|_]} <- xmerl_xpath:string("//name", Document)].
</lang>
Output:
First item: <item upc="123456789" stock="12"> <name>Invisibility Cream</name> <price>14.50</price> <description>Makes you invisible</description> </item> Prices: 14.50 23.99 4.95 3.56 Names: ["Invisibility Cream","Levitation Salve", "Blork and Freen Instameal","Grob winglets"]
F#
<lang fsharp> open System.IO open System.Xml.XPath
let xml = new StringReader(""" <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> """)
let nav = XPathDocument(xml).CreateNavigator()
// first "item"; throws if none exists let item = nav.SelectSingleNode(@"//item[1]")
// apply a operation (print text value) to all price elements for price in nav.Select(@"//price") do
printfn "%s" (price.ToString())
// array of all name elements let names = seq { for name in nav.Select(@"//name") do yield name } |> Seq.toArray</lang>
Factor
<lang factor>
! Get first item element """<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>""" string>xml "item" deep-tag-named
! Print out prices """<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>""" string>xml "price" deep-tags-named [ children>> first ] map
! Array of all name elements """<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>""" string>xml "name" deep-tags-named </lang>
Gastona
The example uses the command XMELON which parses a XML file of any schema and stores its contents into db tables. This parser is described in the article XMeLon-Schema <lang gastona>#javaj#
<frames> oSal, XML Path sample, 300, 400
- data#
<xml> //<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> <DEEP_SQL_XML> DEEP DB, SELECT, xmelon_data ,, path pathStr ,, tag tagStr ,, patCnt ,, dataPlace ,, value
- listix#
<main> //parsing xml data ... GEN, :mem datos, xml XMELON, FILE2DB, :mem datos // //first item ... // // LOOP, SQL,, //SELECT patCnt AS patITEM1 FROM (@<DEEP_SQL_XML>) WHERE path_pathStr == '/inventory/section/item' LIMIT 1 ,HEAD, //<item ,, LOOP, SQL,, //SELECT * FROM (@<DEEP_SQL_XML>) WHERE patCnt == @<patITEM1> AND dataPlace == 'A' ,, , LINK, "" ,, ,, // @<tag_tagStr>="@<value>" ,, //> ,, // ,, LOOP, SQL,, //SELECT * FROM (@<DEEP_SQL_XML>) WHERE patCnt == @<patITEM1> AND dataPlace != 'A' ,, ,, // <@<tag_tagStr>>@<value></@<tag_tagStr>> ,TAIL, // ,TAIL, //</item> // // //report prices ... // LOOP, SQL,, //SELECT value FROM (@<DEEP_SQL_XML>) WHERE tag_tagStr == 'price' , LINK, ", " ,, @<value> // //put names into a variable // VAR=, tabnames, "name" LOOP, SQL,, //SELECT value FROM (@<DEEP_SQL_XML>) WHERE tag_tagStr == 'name' , LINK, "" ,, VAR+, tabnames, @<value> DUMP, data,, tabnames
</lang>
- Output:
parsing xml data ... first item ... <item upc="123456789" stock="12"> <name>Invisibility Cream</name> <price>14.50</price> <description>Makes you invisible</description> </item> report prices ... 14.50, 23.99, 4.95, 3.56 names into a variable #data# <tabnames> //name //health //Invisibility Cream //Levitation Salve //food //Blork and Freen Instameal //Grob winglets
Go
Using the standard encoding/xml
package:
<lang go>package main
import ( "encoding/xml" "fmt" "log" "os" )
type Inventory struct { XMLName xml.Name `xml:"inventory"` Title string `xml:"title,attr"` Sections []struct { XMLName xml.Name `xml:"section"` Name string `xml:"name,attr"` Items []struct { XMLName xml.Name `xml:"item"` Name string `xml:"name"` UPC string `xml:"upc,attr"` Stock int `xml:"stock,attr"` Price float64 `xml:"price"` Description string `xml:"description"` } `xml:"item"` } `xml:"section"` }
// To simplify main's error handling func printXML(s string, v interface{}) { fmt.Println(s) b, err := xml.MarshalIndent(v, "", "\t") if err != nil { log.Fatal(err) } fmt.Println(string(b)) fmt.Println() }
func main() { fmt.Println("Reading XML from standard input...")
var inv Inventory dec := xml.NewDecoder(os.Stdin) if err := dec.Decode(&inv); err != nil { log.Fatal(err) }
// At this point, inv is Go struct with all the fields filled // in from the XML data. Well-formed XML input that doesn't // match the specification of the fields in the Go struct are // discarded without error.
// We can reformat the parts we parsed: //printXML("Got:", inv)
// 1. Retrieve first item: item := inv.Sections[0].Items[0] fmt.Println("item variable:", item) printXML("As XML:", item)
// 2. Action on each price: fmt.Println("Prices:") var totalValue float64 for _, s := range inv.Sections { for _, i := range s.Items { fmt.Println(i.Price) totalValue += i.Price * float64(i.Stock) } } fmt.Println("Total inventory value:", totalValue) fmt.Println()
// 3. Slice of all the names: var names []string for _, s := range inv.Sections { for _, i := range s.Items { names = append(names, i.Name) } } fmt.Printf("names: %q\n", names) }</lang>
- Output:
Reading XML from standard input... item variable: {{ item} Invisibility Cream 123456789 12 14.5 Makes you invisible} As XML: <item upc="123456789" stock="12"> <name>Invisibility Cream</name> <price>14.5</price> <description>Makes you invisible</description> </item> Prices: 14.5 23.99 4.95 3.56 Total inventory value: 3994.81 names: ["Invisibility Cream" "Levitation Salve" "Blork and Freen Instameal" "Grob winglets"]
<lang go>package main
import (
"fmt" "os"
"launchpad.net/xmlpath"
)
func main() {
f, err := os.Open("test3.xml") if err != nil { fmt.Println(err) return } n, err := xmlpath.Parse(f) f.Close() if err != nil { fmt.Println(err) return } q1 := xmlpath.MustCompile("//item") if _, ok := q1.String(n); !ok { fmt.Println("no item") } q2 := xmlpath.MustCompile("//price") for it := q2.Iter(n); it.Next(); { fmt.Println(it.Node()) } q3 := xmlpath.MustCompile("//name") names := []*xmlpath.Node{} for it := q3.Iter(n); it.Next(); { names = append(names, it.Node()) } if len(names) == 0 { fmt.Println("no names") }
}</lang>
- Output:
14.50 23.99 4.95 3.56
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>
Haskell
<lang haskell>import Data.List import Control.Arrow import Control.Monad
takeWhileIncl :: (a -> Bool) -> [a] -> [a] takeWhileIncl _ [] = [] takeWhileIncl p (x:xs)
| p x = x : takeWhileIncl p xs | otherwise = [x]
getmultiLineItem n = takeWhileIncl(not.isInfixOf ("</" ++ n)). dropWhile(not.isInfixOf ('<': n)) getsingleLineItems n = map (takeWhile(/='<'). drop 1. dropWhile(/='>')). filter (isInfixOf ('<': n))
main = do
xml <- readFile "./Rosetta/xmlpath.xml" let xmlText = lines xml putStrLn "\n== First item ==\n" mapM_ putStrLn $ head $ unfoldr (Just. liftM2 (id &&&) (\\) (getmultiLineItem "item")) xmlText putStrLn "\n== Prices ==\n" mapM_ putStrLn $ getsingleLineItems "price" xmlText putStrLn "\n== Names ==\n" print $ getsingleLineItems "name" xmlText</lang>
Using the Haskell XML Toolkit (HXT): <lang haskell>{-# LANGUAGE Arrows #-} import Text.XML.HXT.Arrow {- For HXT version >= 9.0, use instead: import Text.XML.HXT.Core -}
deepElem name = deep (isElem >>> hasName name)
process = proc doc -> do
item <- single (deepElem "item") -< doc _ <- listA (arrIO print <<< deepElem "price") -< doc names <- listA (deepElem "name") -< doc returnA -< (item, names)
main = do
[(item, names)] <- runX (readDocument [] "xmlpath.xml" >>> process) print item print names</lang>
HicEst
<lang hicest>CHARACTER xml*1000, output*1000
READ(ClipBoard) xml
EDIT(Text=xml, Right='<item', Right=5, GetPosition=a, Right='</item>', Left, GetPosition=z) WRITE(Text=output) xml( a : z), $CRLF
i = 1
1 EDIT(Text=xml, SetPosition=i, SePaRators='<>', Right='<price>', Word=1, Parse=price, GetPosition=i, ERror=99)
IF(i > 0) THEN WRITE(Text=output, APPend) 'Price element = ', price, $CRLF GOTO 1 ! HicEst does not have a "WHILE" ENDIF
EDIT(Text=xml, SPR='<>', R='<name>', W=1, WordEnd=$CR, APpendTo=output, DO=999) WRITE(ClipBoard) TRIM(output) </lang>
<lang hicest> upc="123456789" stock="12">
<name>Invisibility Cream</name> <price>14.50</price> <description>Makes you invisible</description>
Price element = 14.50 Price element = 23.99 Price element = 4.95 Price element = 3.56 Invisibility Cream
Levitation Salve Blork and Freen Instameal Grob winglets
</lang>
Java
<lang java>import java.io.StringReader; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathFactory; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource;
public class XMLParser { final static String xmlStr = "<inventory title=\"OmniCorp Store #45x10^3\">" + "
" + "
" + "</inventory>";
public static void main(String[] args) { try { Document doc = DocumentBuilderFactory.newInstance() .newDocumentBuilder() .parse(new InputSource(new StringReader(xmlStr))); XPath xpath = XPathFactory.newInstance().newXPath(); // 1 System.out.println(((Node) xpath.evaluate( "/inventory/section/item[1]", doc, XPathConstants.NODE)) .getAttributes().getNamedItem("upc")); // 2, 3 NodeList nodes = (NodeList) xpath.evaluate( "/inventory/section/item/price", doc, XPathConstants.NODESET); for (int i = 0; i < nodes.getLength(); i++) System.out.println(nodes.item(i).getTextContent()); } catch (Exception e) { System.out.println("Error ocurred while parsing XML."); } } }</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>
Julia
Uses the LibExpat module for XML pathing. The exercise description is very vague about output format, so this is varied in the solution below. The first test prints the raw XML node, and the second and third are further processed. <lang julia>using LibExpat
xdoc = raw"""<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> """
debracket(s) = replace(s, r".+\>(.+)\<.+" => s"\1")
etree = xp_parse(xdoc) firstshow = LibExpat.find(etree, "//item")[1] println("The first item's node XML entry is:\n", firstshow, "\n\n")
prices = LibExpat.find(etree, "//price") println("Prices:") for p in prices
println("\t", debracket(string(p)))
end println("\n")
namearray = LibExpat.find(etree, "//name") println("Array of names of items:\n\t", map(s -> debracket(string(s)), namearray))
</lang>
- Output:
The first item's node XML entry is: <item upc="123456789" stock="12"> <name>Invisibility Cream</name> <price>14.50</price> <description>Makes you invisible</description> </item>
Prices: 14.50 23.99 4.95 3.56
Array of names of items: ["Invisibility Cream", "Levitation Salve", "Blork and Freen Instameal", "Grob winglets"]
Kotlin
<lang scala>// version 1.1.3
import javax.xml.parsers.DocumentBuilderFactory import org.xml.sax.InputSource import java.io.StringReader import javax.xml.xpath.XPathFactory import javax.xml.xpath.XPathConstants import org.w3c.dom.Node import org.w3c.dom.NodeList
val xml = """ <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> """
fun main(args: Array<String>) {
val dbFactory = DocumentBuilderFactory.newInstance() val dBuilder = dbFactory.newDocumentBuilder() val xmlInput = InputSource(StringReader(xml)) val doc = dBuilder.parse(xmlInput) val xpFactory = XPathFactory.newInstance() val xPath = xpFactory.newXPath()
val qNode = xPath.evaluate("/inventory/section/item[1]", doc, XPathConstants.NODE) as Node val upc = qNode.attributes.getNamedItem("upc") val stock = qNode.attributes.getNamedItem("stock") println("For the first item : upc = ${upc.textContent} and stock = ${stock.textContent}")
val qNodes = xPath.evaluate("/inventory/section/item/price", doc, XPathConstants.NODESET) as NodeList print("\nThe prices of each item are : ") for (i in 0 until qNodes.length) print("${qNodes.item(i).textContent} ") println()
val qNodes2 = xPath.evaluate("/inventory/section/item/name", doc, XPathConstants.NODESET) as NodeList val names = Array<String>(qNodes2.length) { qNodes2.item(it).textContent } println("\nThe names of each item are as follows :") println(" ${names.joinToString("\n ")}")
}</lang>
- Output:
For the first item : upc = 123456789 and stock = 12 The prices of each item are : 14.50 23.99 4.95 3.56 The names of each item are as follows : Invisibility Cream Levitation Salve Blork and Freen Instameal Grob winglets
Lasso
Lasso has built in support for both XML handling and Xpaths <lang Lasso>// makes extracting attribute values easier define xml_attrmap(in::xml_namedNodeMap_attr) => { local(out = map) with attr in #in do #out->insert(#attr->name = #attr->value) return #out }
local( text = '<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> ', xml = xml(#text) )
local( items = #xml -> extract('//item'), firstitem = #items -> first, itemattr = xml_attrmap(#firstitem -> attributes), newprices = array )
'First item:
UPC: '
- itemattr -> find('upc')
' (stock: '
- itemattr -> find('stock')
')
'
- firstitem -> extractone('name') -> nodevalue
' ['
- firstitem -> extractone('price') -> nodevalue
'] ('
- firstitem -> extractone('description') -> nodevalue
')
'
with item in #items
let name = #item -> extractone('name') -> nodevalue
let price = #item -> extractone('price') -> nodevalue
do {
#newprices -> insert(#name + ': ' + (decimal(#price) * 1.10) -> asstring(-precision = 2) + ' (' + #price + ')')
}
'Adjusted prices:
'
- newprices -> join('
')
'
'
'Array with all names:
'
- xml -> extract('//name') -> asstaticarray</lang>
Output:
First item: UPC: 123456789 (stock: 12) Invisibility Cream [14.50] (Makes you invisible) Adjusted prices: Invisibility Cream: 15.95 (14.50) Levitation Salve: 26.39 (23.99) Blork and Freen Instameal: 5.45 (4.95) Grob winglets: 3.92 (3.56) Array with all names: staticarray(Invisibility Cream, Levitation Salve, Blork and Freen Instameal, Grob winglets)
LiveCode
Copy the xml in this task into a text field called "FieldXML" <lang LiveCode>put revXMLCreateTree(fld "FieldXML",true,true,false) into xmltree
// task 1 put revXMLEvaluateXPath(xmltree,"//item[1]") into nodepath put revXMLText(xmltree,nodepath,true)
// task 2 put revXMLDataFromXPathQuery(xmltree,"//item/price",,comma)
// task 3 put revXMLDataFromXPathQuery(xmltree,"//name") into namenodes filter namenodes without empty split namenodes using cr put namenodes is an array</lang>
Lua
Requires LuaExpat <lang lua>require 'lxp' data = [[<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>]] local first = true local names, prices = {}, {} p = lxp.new({StartElement = function (parser, name) local a, b, c = parser:pos() --line, offset, pos if name == 'item' and first then print(data:match('.-</item>', c - b + 1)) first = false end if name == 'name' then names[#names+1] = data:match('>(.-)<', c) end if name == 'price' then prices[#prices+1] = data:match('>(.-)<', c) end end})
p:parse(data) p:close()
print('Name: ', table.concat(names, ', ')) print('Price: ', table.concat(prices, ', '))</lang>
Output:
<item upc="123456789" stock="12"> <name>Invisibility Cream</name> <price>14.50</price> <description>Makes you invisible</description> </item> Name: Invisibility Cream, Levitation Salve, Blork and Freen Instameal, Grob winglets Price: 14.50, 23.99, 4.95, 3.56
Mathematica
<lang Mathematica>example = Import["test.txt", "XML"]; Cases[example, XMLElement["item", _ , _] , Infinity] // First Cases[example, XMLElement["price", _, List[n_]] -> n, Infinity] // Column Cases[example, XMLElement["name", _, List[n_]] -> n, Infinity] // Column</lang>
Output:
XMLElement[item,{upc->123456789,stock->12}, {XMLElement[name,{},{Invisibility Cream}],XMLElement[price,{},{14.50}],XMLElement[description,{},{Makes you invisible}]}] 14.50 23.99 4.95 3.56 Invisibility Cream Levitation Salve Blork and Freen Instameal Grob winglets
NetRexx
<lang NetRexx>/* NetRexx */ options replace format comments java crossref symbols binary
import javax.xml.parsers. import javax.xml.xpath. import org.w3c.dom. import org.xml.sax.
xmlStr = -
|| '<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 priwater</description>' - || ' </item>' - || ' ' - || '</inventory>'
expr1 = '/inventory/section/item[1]' expr2 = '/inventory/section/item/price' expr3 = '/inventory/section/item/name' attr1 = 'upc'
do
doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(InputSource(StringReader(xmlStr))) xpath = XPathFactory.newInstance().newXPath()
-- Extract attribute from 1st item element say expr1 say " "(Node xpath.evaluate(expr1, doc, XPathConstants.NODE)).getAttributes().getNamedItem(attr1) say
-- Extract and display all price elments nodes = NodeList xpath.evaluate(expr2, doc, XPathConstants.NODESET) say expr2 loop i_ = 0 to nodes.getLength() - 1 say Rexx(nodes.item(i_).getTextContent()).format(10, 2) end i_ say
-- Extract elements and store in an ArrayList nameList = java.util.List nameList = ArrayList() nodes = NodeList xpath.evaluate(expr3, doc, XPathConstants.NODESET) loop i_ = 0 to nodes.getLength() - 1 nameList.add(nodes.item(i_).getTextContent()) end i_
-- display contents of ArrayList say expr3 loop n_ = 0 to nameList.size() - 1 say " "nameList.get(n_) end n_ say
catch ex = Exception
ex.printStackTrace()
end
return </lang> Output:
/inventory/section/item[1] upc="123456789" /inventory/section/item/price 14.50 23.99 4.95 3.56 /inventory/section/item/name Invisibility Cream Levitation Salve Blork and Freen Instameal Grob winglets
Nim
<lang nim>import xmldom, xmldomparser
let doc = "test3.xml".loadXMLFile.documentElement
- 1st task: retrieve the first "item" element
let i = doc.getElementsByTagName("item")[0]
- 2nd task: perform an action on each "price" element (print it out)
for j in doc.getElementsByTagName "price":
echo j.firstChild.PText.data
- 3rd task: get an array of all the "name" elements
let namesArray = doc.getElementsByTagName "name"</lang>
Objeck
XPath is used to fetch element tags. <lang objeck> use XML;
bundle Default {
class Test { function : Main(args : String[]) ~ Nil { in := String->New(); in->Append("<inventory title=\"OmniCorp Store #45x10^3\">"); in->Append(""); in->Append("<item upc=\"123456789\" stock=\"12\">"); in->Append("<name>Invisibility Cream</name>"); in->Append("<price>14.50</price>"); in->Append("<description>Makes you invisible</description>"); in->Append("</item>"); in->Append("<item upc=\"445322344\" stock=\"18\">"); in->Append("<name>Levitation Salve</name>"); in->Append("<price>23.99</price>"); in->Append("<description>Levitate yourself for up to 3 hours per application</description>"); in->Append("</item>"); in->Append(" "); in->Append(""); in->Append("<item upc=\"485672034\" stock=\"653\">"); in->Append("<name>Blork and Freen Instameal</name>"); in->Append("<price>4.95</price>"); in->Append("<description>A tasty meal in a tablet; just add water</description>"); in->Append("</item>"); in->Append("<item upc=\"132957764\" stock=\"44\">"); in->Append("<name>Grob winglets</name>"); in->Append("<price>3.56</price>"); in->Append("<description>Tender winglets of Grob. Just add water</description>"); in->Append("</item>"); in->Append(" "); in->Append("</inventory>"); parser := XmlParser->New(in); if(parser->Parse()) { # get first item results := parser->FindElements("//inventory/section[1]/item[1]"); if(results <> Nil) { IO.Console->Instance()->Print("items: ")->PrintLine(results->Size()); }; # get all prices results := parser->FindElements("//inventory/section/item/price"); if(results <> Nil) { each(i : results) { element := results->Get(i)->As(XMLElement); element->GetContent()->PrintLine(); }; }; # get names results := parser->FindElements("//inventory/section/item/name"); if(results <> Nil) { IO.Console->Instance()->Print("names: ")->PrintLine(results->Size()); }; }; } }
} </lang>
Oz
We implement a small subset of XPath for this task: <lang oz>declare
[XMLParser] = {Module.link ['x-oz://system/xml/Parser.ozf']}
proc {Main Data} Parser = {New XMLParser.parser init} [Doc] = {Parser parseVS(Data $)}
FirstItem = {XPath Doc [inventory section item]}.1
Prices = {XPath Doc [inventory section item price Text]}
Names = {XPath Doc [inventory section item name]} in {ForAll Prices System.showInfo} end
%% %% Emulation of some XPath functionality: %%
fun {XPath Doc Path} P|Pr = Path in Doc.name = P %% assert {FoldL Pr XPathStep [Doc]} end
fun {XPathStep Elements P} if {IsProcedure P} then {Map Elements P} else {FilteredChildren Elements P} end end
%% A flat list of all Type-children of all Elements. fun {FilteredChildren Elements Type} {Flatten {Map Elements fun {$ E}
{Filter E.children fun {$ X} case X of element(name:!Type ...) then true else false end end}
end}} end
%% PCDATA of an element as a ByteString fun {Text Element} Texts = for Child in Element.children collect:C do
case Child of text(data:BS ...) then {C BS} end end
in {FoldR Texts ByteString.append {ByteString.make nil}} end Data = "<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>"
in
{Main Data}</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>
PicoLisp
<lang PicoLisp>(load "@lib/xm.l")
(let Sections (body (in "file.xml" (xml)))
(pretty (car (body (car Sections)))) (prinl) (for S Sections (for L (body S) (prinl (car (body L 'price))) ) ) (make (for S Sections (for L (body S) (link (car (body L 'name))) ) ) ) )</lang>
Output:
(item ((upc . "123456789") (stock . "12")) (name NIL "Invisibility Cream") (price NIL "14.50") (description NIL "Makes you invisible") ) 14.50 23.99 4.95 3.56 -> ("Invisibility Cream" "Levitation Salve" "Blork and Freen Instameal" "Grob winglets")
PowerShell
Cast the $document
string as [xml]
and you have access to .NET methods affecting XML.
<lang PowerShell>
$document = [xml]@'
<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> '@
$query = "/inventory/section/item" $items = $document.SelectNodes($query) </lang> The first item: <lang PowerShell> $items[0] </lang>
- Output:
upc : 123456789 stock : 12 name : Invisibility Cream price : 14.50 description : Makes you invisible
Get some useful information: <lang PowerShell> $namesAndPrices = $items | Select-Object -Property name, price $namesAndPrices </lang>
- Output:
name price ---- ----- Invisibility Cream 14.50 Levitation Salve 23.99 Blork and Freen Instameal 4.95 Grob winglets 3.56
Here are the prices: <lang PowerShell> $items.price </lang>
- Output:
14.50 23.99 4.95 3.56
Here are the names: <lang PowerShell> $items.name </lang>
- Output:
Invisibility Cream Levitation Salve Blork and Freen Instameal Grob winglets
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 = minidom.parse(xmlfile).documentElement # parse from file stream or... xmldoc = minidom.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> In Python 2.5+ you can use ElementTree's limited XPath support <lang python> import xml.etree.ElementTree as ET
xml = open('inventory.xml').read() doc = ET.fromstring(xml)
doc = ET.parse('inventory.xml') # or load it directly
- Note, ElementTree's root is the top level element. So you need ".//" to really start searching from top
- Return first Item
item1 = doc.find("section/item") # or ".//item"
- Print each price
for p in doc.findall("section/item/price"): # or ".//price"
print "{0:0.2f}".format(float(p.text)) # could raise exception on missing text or invalid float() conversion
- list of names
names = doc.findall("section/item/name") # or ".//name"</lang> Or, you can install the lxml package and get full XPath support <lang python> from lxml import etree
xml = open('inventory.xml').read() doc = etree.fromstring(xml)
doc = etree.parse('inventory.xml') # or load it directly
- Return first item
item1 = doc.xpath("//section[1]/item[1]")
- Print each price
for p in doc.xpath("//price"):
print "{0:0.2f}".format(float(p.text)) # could raise exception on missing text or invalid float() conversion
names = doc.xpath("//name") # list of names</lang>
R
<lang R>## Require the XML package you can download from http://www.omegahat.org/RSXML/ library("XML") doc <- xmlInternalTreeParse("test3.xml")
- 1st Task: Retrieve the first "item" element
(firstItemElement <- getNodeSet(doc, "//item")1)
- 2nd task: Perform an action on each "price" element (print it out)
prices <- sapply(getNodeSet(doc, "//price"), xmlValue) for(i in 1:length(prices)) print(prices[i])
- 3rd Task: Get an array of all the "name" elements
(namesArray <- sapply(getNodeSet(doc, "//name"), xmlValue))</lang>
Racket
<lang racket>
- lang at-exp racket
(define input @~a{
<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>})
(require xml xml/path)
(define data (xml->xexpr
((eliminate-whitespace '(inventory section item)) (read-xml/element (open-input-string input)))))
- Retrieve the first "item" element
(displayln (xexpr->string (se-path* '(item) data)))
- => <name>Invisibility Cream</name>
- Perform an action on each "price" element (print it out)
(printf "Prices: ~a\n" (string-join (se-path*/list '(item price) data) ", "))
- => Prices
- 14.50, 23.99, 4.95, 3.56
- Get an array of all the "name" elements
(se-path*/list '(item name) data)
- => '("Invisibility Cream" "Levitation Salve" "Blork and Freen Instameal" "Grob winglets")
</lang>
Raku
(formerly Perl 6)
<lang perl6>use XML::XPath;
my $XML = XML::XPath.new(xml => q:to/END/); <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> END
put "First item:\n", $XML.find('//item[1]')[0];
put "\nPrice elements:"; .contents.put for $XML.find('//price').List;
put "\nName elements:\n", $XML.find('//name')».contents.join: ', ';</lang>
- Output:
First item: <item upc="123456789" stock="12"> <name>Invisibility Cream</name> <price>14.50</price> <description>Makes you invisible</description> </item> Price elements: 14.50 23.99 4.95 3.56 Name elements: Invisibility Cream, Levitation Salve, Blork and Freen Instameal, Grob winglets
Rascal
<lang rascal>import lang::xml::DOM; import Prelude;
public void get_first_item(loc a){ D = parseXMLDOM(readFile(a)); top-down-break visit(D){ case E:element(_,"item",_): return println(xmlPretty(E)); }; }
public void print_prices(loc a){ D = parseXMLDOM(readFile(a)); for(/element(_,"price",[charData(/str p)]) := D) println(p); }
public list[str] get_names(loc a){ D = parseXMLDOM(readFile(a)); L = []; for(/element(_,"name",[charData(/str n)]) := D) L += n; return L; }</lang> Example output: <lang rascal>rascal>get_first_item(|file:///Users/.../Desktop/xmlpath.xml|) <?xml version="1.0" encoding="UTF-8"?> <item stock="12" upc="123456789">
<name>Invisibility Cream</name> <price>14.50</price> <description>Makes you invisible</description>
</item>
ok
rascal>print_prices(|file:///Users/.../Desktop/xmlpath.xml|) 14.50 23.99 4.95 3.56 ok
rascal>get_names(|file:///Users/.../Desktop/xmlpath.xml|) list[str]: ["Invisibility Cream","Levitation Salve","Blork and Freen Instameal","Grob winglets"]</lang>
REXX
hard coded parsing
<lang rexx>/*REXX program to parse various queries on an XML document (from a file). */ iFID='XPATH.XML' /*name of the input XML file (doc). */ $= /*string will contain the file's text. */
do j=1 while lines(iFID)\==0 /*read the entire file into a string. */ $=$ linein(iFID) /*append the line to the $ string. */ end /*j*/ /* [↓] show 1st ITEM in the document*/
parse var $ '<item ' item "</item>" say center('first item:',length(space(item)),'─') /*display a nice header.*/ say space(item)
/* [↓] show all PRICES in the document*/
prices= /*nullify the list and add/append to it*/ $$=$ /*start with a fresh copy of document. */
do until $$= /* [↓] keep parsing string until done.*/ parse var $$ '<price>' price '</price>' $$ prices=prices price /*add/append the price to the list. */ end /*until*/
say say center('prices:',length(space(prices)),'─') /*display a nice header.*/ say space(prices)
/* [↓] show all NAMES in the document*/
names.= /*nullify the list and add/append to it*/ L=length(' names: ') /*maximum length of any one list name. */ $$=$ /*start with a fresh copy of document. */
do #=1 until $$= /* [↓] keep parsing string until done.*/ parse var $$ '<name>' names.# '</name>' $$ L=max(L,length(names.#)) /*L: is used to find the widest name. */ end /*#*/
names.0=#-1; say /*adjust the number of names (DO loop).*/ say center('names:',L,'─') /*display a nicely formatted header. */
do k=1 for names.0 /*display all the names in the list. */ say names.k /*display a name from the NAMES list.*/ end /*k*/ /*stick a fork in it, we're all done. */</lang>
output
──────────────────────────────────────────────────────────first item:────────────────────────────────────────────────────────── upc="123456789" stock="12"> <name>Invisibility Cream</name> <price>14.50</price> <description>Makes you invisible</description> ───────prices:─────── 14.50 23.99 4.95 3.56 ─────────names:────────── Invisibility Cream Levitation Salve Blork and Freen Instameal Grob winglets
generic parsing
<lang rexx>/*REXX program to parse various queries on an XML document (from a file). */ iFID='XPATH.XML' /*name of the input XML file (doc). */ $= /*string will contain the file's text. */
do j=1 while lines(iFID)\==0 /*read the entire file into a string. */ $=$ linein(iFID) /*append the line to the $ string. */ end /*j*/ /* [↓] display 1st ITEM in document. */
call parser 'item', 0 /*go and parse the all the ITEMs. */ say center('first item:',@L.1,'─') /*display a nicely formatted header. */ say @.1; say /*display the first ITEM found. */
call parser 'price' /*go and parse all the PRICEs. */ say center('prices:',length(@@@),'─') /*display a nicely formatted header. */ say @@@; say /*display a list of all the prices. */
call parser 'name' say center('names:',@L,'─') /*display a nicely formatted header. */
do k=1 for # /*display all the names in the list. */ say @.k /*display a name from the NAMES list.*/ end /*k*/
exit /*stick a fork in it, we're all done. */ /*────────────────────────────────────────────────────────────────────────────*/ parser: parse arg yy,tail,,@. @@. @@@; $$=$; @L=9; yb='<'yy; ye='</'yy">" tail=word(tail 1, 1) /*use a tail ">" or not?*/
do #=1 until $$= /*parse complete XML doc. */ if tail then parse var $$ (yb) '>' @@.# (ye) $$ /*find meat.*/ else parse var $$ (yb) @@.# (ye) $$ /* " " */ @.#=space(@@.#); @@@=space(@@@ @.#) /*shrink; @@@=list of YY.*/ @L.#=length(@.#); @L=max(@L,@L.#) /*length; maximum length. */ end /*#*/
- =#-1 /*adjust # of thing found.*/
return</lang> output is the same as the 1st version.
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>
Scala
The code is entered in to Scala's REPL, to better show the results.
<lang scala> scala> val xml: scala.xml.Elem =
| <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>
scala> val firstItem = xml \\ "item" take 1 firstItem: scala.xml.NodeSeq = NodeSeq(<item upc="123456789" stock="12">
<name>Invisibility Cream</name> <price>14.50</price> <description>Makes you invisible</description> </item>)
scala> xml \\ "price" map (_.text) foreach println 14.50 23.99 4.95 3.56
scala> val names = (xml \\ "name").toArray names: Array[scala.xml.Node] = Array(<name>Invisibility Cream</name>, <name>Levitation Salve</name>, <name>Blork and Freen Instameal</name>, <name>Grob winglets</name>)</lang>
SenseTalk
<lang sensetalk>Set XMLSource to {{ <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> }}
put node "//item[1]" of XMLSource put node "//price/text()" of XMLSource put all nodes "//name" of XMLSource</lang>
- Output:
<item upc="123456789" stock="12"> <name>Invisibility Cream</name> <price>14.50</price> <description>Makes you invisible</description> </item> 14.50 (<name>Invisibility Cream</name>,<name>Levitation Salve</name>,<name>Blork and Freen Instameal</name>,<name>Grob winglets</name>)
Sidef
<lang ruby>require('XML::XPath');
var x = %s'XML::XPath'.new(ARGF.slurp);
[x.findnodes('//item[1]')][0]; say [x.findnodes('//price')].map{x.getNodeText(_)}; [x.findnodes('//name')];</lang>
- Output:
[14.5, 23.99, 4.95, 3.56]
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>
TUSCRIPT
<lang tuscript> $$ MODE TUSCRIPT,{} MODE DATA $$ XML=* <inventory title="OmniCorp Store #45x10³">
<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> $$ MODE TUSCRIPT
FILE = "test.xml" ERROR/STOP CREATE (file,fdf-o,-std-) FILE/ERASE/UTF8 $FILE = xml
BUILD S_TABLE beg=":<item*>:<name>:<price>:" BUILD S_TABLE end=":</item>:</name>:</price>:" BUILD S_TABLE modifiedbeg=":<name>:<price>:" BUILD S_TABLE modifiedend=":</name>:</price>:" firstitem=names="",countitem=0 ACCESS q: READ/STREAM/UTF8 $FILE s,a/beg+t+e/end LOOP READ/EXIT q IF (a=="<name>") names=APPEND(names,t) IF (a=="<price>") PRINT t IF (a.sw."<item") countitem=1 IF (countitem==1) THEN firstitem=CONCAT(firstitem,a) firstitem=CONCAT(firstitem,t) firstitem=CONCAT(firstitem,e)
IF (e=="</item>") THEN COUNTITEM=0 MODIFY ACCESS q s_TABLE modifiedbeg,-,modifiedend ENDIF
ENDIF ENDLOOP ENDACCESS q ERROR/STOP CLOSE (file) firstitem=EXCHANGE (firstitem,":{2-00} ::") firstitem=INDENT_TAGS (firstitem,-," ") names=SPLIT(names) TRACE *firstitem,names </lang> Output:
14.50 23.99 4.95 3.56 TRACE * 63 -*TUSTEP.EDT firstitem = * 1 = <item upc="123456789" stock="12"> 2 = <name>Invisibility Cream</name> 3 = <price>14.50</price> 4 = <description>Makes you invisible</description> 5 = </item> names = * 1 = Invisibility Cream 2 = Levitation Salve 3 = Blork and Freen Instameal 4 = Grob winglets
VBScript
<lang vb> Set objXMLDoc = CreateObject("msxml2.domdocument")
objXMLDoc.load("In.xml")
Set item_nodes = objXMLDoc.selectNodes("//item") i = 1 For Each item In item_nodes If i = 1 Then WScript.StdOut.Write item.xml WScript.StdOut.WriteBlankLines(2) Exit For End If Next
Set price_nodes = objXMLDoc.selectNodes("//price") list_price = "" For Each price In price_nodes list_price = list_price & price.text & ", " Next WScript.StdOut.Write list_price WScript.StdOut.WriteBlankLines(2)
Set name_nodes = objXMLDoc.selectNodes("//name") list_name = "" For Each name In name_nodes list_name = list_name & name.text & ", " Next WScript.StdOut.Write list_name WScript.StdOut.WriteBlankLines(2) </lang>
- Output:
<item upc="123456789" stock="12"> <name>Invisibility Cream</name> <price>14.50</price> <description>Makes you invisible</description> </item> 14.50, 23.99, 4.95, 3.56, Invisibility Cream, Levitation Salve, Blork and Freen Instameal, Grob winglets,
Visual Basic .NET
<lang vbnet>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</lang>
XProc
<lang xml><p:pipeline xmlns:p="http://www.w3.org/ns/xproc"
name="one-two-three" version="1.0"> <p:identity> <p:input port="source"> <p:inline> <root> <first/> <prices/> <names/> </root> </p:inline> </p:input> </p:identity> <p:insert match="/root/first" position="first-child"> <p:input port="insertion" select="(//item)[1]"> <p:pipe port="source" step="one-two-three"/> </p:input> </p:insert> <p:insert match="/root/prices" position="first-child"> <p:input port="insertion" select="//price"> <p:pipe port="source" step="one-two-three"/> </p:input> </p:insert> <p:insert match="/root/names" position="first-child"> <p:input port="insertion" select="//name"> <p:pipe port="source" step="one-two-three"/> </p:input> </p:insert>
</p:pipeline></lang>
XQuery
<lang xquery>(:
1. Retrieve the first "item" element Notice the braces around //item. This evaluates first all item elements and then retrieving the first one. Whithout the braces you get the first item for every section.
- )
let $firstItem := (//item)[1]
(: 2. Perform an action on each "price" element (print it out) :) let $price := //price/data(.)
(: 3. Get an array of all the "name" elements :) let $names := //name
return
<result> <firstItem>{$firstItem}</firstItem> <prices>{$price}</prices> <names>{$names}</names> </result></lang>
Performing this XQuery on the given input document results in <lang xquery><?xml version="1.0" encoding="UTF-8"?> <result>
<firstItem> <item upc="123456789" stock="12"> <name>Invisibility Cream</name> <price>14.50</price> <description>Makes you invisible</description> </item> </firstItem> <prices>14.50 23.99 4.95 3.56</prices> <names> <name>Invisibility Cream</name> <name>Levitation Salve</name> <name>Blork and Freen Instameal</name> <name>Grob winglets</name> </names>
</result></lang>
XSLT
<lang xml><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" /> <xsl:template match="/"> <xsl:text>
The first item element is</xsl:text>
<xsl:value-of select="//item[1]" /> <xsl:text>
The prices are: </xsl:text>
<xsl:for-each select="//price"> <xsl:text> </xsl:text> <xsl:copy-of select="." /> </xsl:for-each> <xsl:text>
The names are: </xsl:text>
<xsl:copy-of select="//name" /> </xsl:template>
</xsl:stylesheet></lang>
- Programming Tasks
- XML
- AArch64 Assembly
- AppleScript
- ARM Assembly
- AutoHotkey
- AHK XPath
- Bracmat
- C
- LibXML
- C sharp
- C++
- Caché ObjectScript
- CoffeeScript
- ColdFusion
- Common Lisp
- Plexippus-xpath
- Cxml
- Cxml-stp
- D
- KXML
- Delphi
- E
- E-XML
- Erlang
- Xmerl
- F Sharp
- Factor
- Xml
- Xml.data
- Xml.traversal
- Gastona
- Go
- Xmlpath
- Groovy
- Haskell
- HicEst
- Java
- JavaScript
- Julia
- Kotlin
- Lasso
- LiveCode
- Lua
- Mathematica
- NetRexx
- Nim
- Nim examples needing attention
- Examples needing attention
- Objeck
- Oz
- Perl
- PHP
- PicoLisp
- PowerShell
- Python
- R
- XML (R)
- Racket
- Raku
- Rascal
- REXX
- Ruby
- REXML
- Scala
- SenseTalk
- Sidef
- Tcl
- TDOM
- TUSCRIPT
- VBScript
- Visual Basic .NET
- XProc
- XQuery
- XSLT
- Batch File/Omit
- PARI/GP/Omit