XML/XPath: Difference between revisions

Content added Content deleted
(Replaced the wrong solution with a solution which uses "libxml2" as external library.)
Line 2,473: Line 2,473:


=={{header|Nim}}==
=={{header|Nim}}==
{{libheader|libxml2}}
{{incorrect|Nim|Does not use XPath}}
Nim standard library provides several modules to process XML, but none to process XPath requests. There is no third party library to do that either.
<lang nim>import xmldom, xmldomparser


So we provide here a solution which uses the C "libxml2" library. Nim allows to interface with C with a great flexibility. Declaring the external function is quite easy. The major part of the work consists in declaring the data types. In "libxml2" there are a lot of structures and we have translated only what is needed to solve the task (or a bit more actually).
let doc = "test3.xml".loadXMLFile.documentElement


For the first request, we use <code>//section[1]/item[1]</code> instead of <code>//item[1]</code> (as the Python version does). The latter request works but returns the list of the first elements in each section which doesn’t seem to be what is expected.
# 1st task: retrieve the first "item" element
let i = doc.getElementsByTagName("item")[0]


<lang Nim>import sequtils, strutils
# 2nd task: perform an action on each "price" element (print it out)
for j in doc.getElementsByTagName "price":
echo j.firstChild.PText.data


const LibXml = "libxml2.so"
# 3rd task: get an array of all the "name" elements

let namesArray = doc.getElementsByTagName "name"</lang>
type

XmlDocPtr = pointer

XmlXPathContextPtr = pointer

XmlElementKind = enum
xmlElementNode = 1
xmlAttributeNode = 2
xmlTextNode = 3
xmlCdataSectionNode = 4
xmlEntityRefNode = 5
xmlEntityNode = 6
xmlPiNode = 7
xmlCommentNode = 8
xmlDocumentNode = 9
xmlDocumentTypeNode = 10
xmlDocumentFragNode = 11
xmlNotationNode = 12
xmlHtmlDocumentNode = 13
xmlDtdNode = 14
xmlElementDecl = 15
xmlAttributeDecl = 16
xmlEntityDecl = 17
xmlNamespaceDecl = 18
xmlXincludeStart = 19
xmlXincludeEnd = 20

XmlNsKind = XmlElementKind

XmlNsPtr = ptr XmlNs
XmlNs = object
next: XmlNsPtr
kind: XmlNsKind
href: cstring
prefix: cstring
private: pointer
context: XmlDocPtr

XmlAttrPtr = pointer

XmlNodePtr = ptr XmlNode
XmlNode = object
private: pointer
kind: XmlElementKind
name: cstring
children: XmlNodePtr
last: XmlNodePtr
parent: XmlNodePtr
next: XmlNodePtr
prev: XmlNodePtr
doc: XmlDocPtr
ns: XmlNsPtr
content: cstring
properties: XmlAttrPtr
nsDef: XmlNsPtr
psvi: pointer
line: cushort
extra: cushort

XmlNodeSetPtr = ptr XmlNodeSet
XmlNodeSet = object
nodeNr: cint
nodeMax: cint
nodeTab: ptr UncheckedArray[XmlNodePtr]

XmlPathObjectKind = enum
xpathUndefined
xpathNodeset
xpathBoolean
xpathNumber
xpathString
xpathPoint
xpathRange
xpathLocationset
xpathUsers
xpathXsltTree

XmlXPathObjectPtr = ptr XmlXPathObject
XmlXPathObject = object
kind: XmlPathObjectKind
nodeSetVal: XmlNodeSetPtr
boolVal: cint
floatVal: cdouble
stringVal: cstring
user: pointer
index: cint
user2: pointer
index2: cint

XmlSaveCtxtPtr = pointer

XmlBufferPtr = pointer


# Declaration of needed "libxml2" procedures.
proc xmlParseFile(docName: cstring): XmlDocPtr
{.cdecl, dynlib: LibXml, importc: "xmlParseFile".}

proc xmlXPathNewContext(doc: XmlDocPtr): XmlXPathContextPtr
{.cdecl, dynlib: LibXml, importc: "xmlXPathNewContext".}

proc xmlXPathEvalExpression(str: cstring; ctxt: XmlXPathContextPtr): XmlXPathObjectPtr
{.cdecl, dynlib: LibXml, importc: "xmlXPathEvalExpression".}

proc xmlXPathFreeContext(ctxt: XmlXPathContextPtr)
{.cdecl, dynlib: LibXml, importc: "xmlXPathFreeContext".}

proc xmlXPathFreeObject(obj: XmlXPathObjectPtr)
{.cdecl, dynlib: LibXml, importc: "xmlXPathFreeObject".}

proc xmlSaveToBuffer(vuffer: XmlBufferPtr; encoding: cstring; options: cint): XmlSaveCtxtPtr
{.cdecl, dynlib: LibXml, importc: "xmlSaveToBuffer".}

proc xmlBufferCreate(): XmlBufferPtr
{.cdecl, dynlib: LibXml, importc: "xmlBufferCreate".}

proc xmlBufferFree(buf: XmlBufferPtr)
{.cdecl, dynlib: LibXml, importc: "xmlBufferCreate".}

proc xmlBufferContent(buf: XmlBufferPtr): cstring
{.cdecl, dynlib: LibXml, importc: "xmlBufferContent".}

proc xmlSaveTree(ctxt: XmlSaveCtxtPtr; cur: XmlNodePtr): clong
{.cdecl, dynlib: LibXml, importc: "xmlSaveTree".}

proc xmlSaveClose(ctxt: XmlSaveCtxtPtr)
{.cdecl, dynlib: LibXml, importc: "xmlSaveClose".}


proc `$`(node: XmlNodePtr): string =
## Return the representation of a node.
let buffer = xmlBufferCreate()
let saveContext = xmlSaveToBuffer(buffer, nil, 0)
discard saveContext.xmlSaveTree(node)
saveContext.xmlSaveClose()
result = $buffer.xmlBufferContent()
xmlBufferFree(buffer)


iterator nodes(xpath: string; context: XmlXPathContextPtr): XmlNodePtr =
## Yield the nodes which fit the XPath request.
let xpathObj = xmlXPathEvalExpression(xpath, context)
if xpathObj.isNil:
quit "Failed to evaluate XPath: " & xpath, QuitFailure
assert xpathObj.kind == xpathNodeset
let nodeSet = xpathObj.nodeSetVal
if not nodeSet.isNil:
for i in 0..<nodeSet.nodeNr:
yield nodeSet.nodeTab[i]
xmlXPathFreeObject(xpathObj)


# Load and parse XML file.
let doc = xmlParseFile("xpath_test.xml")
if doc.isNil:
quit "Unable to load and parse document", QuitFailure

# Create an XPath context.
let context = xmlXPathNewContext(doc)
if context.isNil:
quit "Failed to create XPath context", QuitFailure

var xpath = "//section[1]/item[1]"
echo "Request $#:".format(xpath)
for node in nodes(xpath, context):
echo node
echo()

xpath = "//price/text()"
echo "Request $#:".format(xpath)
for node in nodes(xpath, context):
echo node.content
echo()

xpath = "//name"
echo "Request $#:".format(xpath)
let names = toSeq(nodes(xpath, context)).mapIt(it.children.content)
echo names

xmlXPathFreeContext(context)</lang>

{{out}}
<pre>Request //section[1]/item[1]:
<item upc="123456789" stock="12">
<name>Invisibility Cream</name>
<price>14.50</price>
<description>Makes you invisible</description>
</item>

Request //price/text():
14.50
23.99
4.95
3.56

Request //name:
@["Invisibility Cream", "Levitation Salve", "Blork and Freen Instameal", "Grob winglets"]</pre>


=={{header|Objeck}}==
=={{header|Objeck}}==