Anonymous user
XML/XPath: Difference between revisions
Replaced the wrong solution with a solution which uses "libxml2" as external library.
(Replaced the wrong solution with a solution which uses "libxml2" as external library.) |
|||
Line 2,473:
=={{header|Nim}}==
{{libheader|libxml2}}
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.
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).
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.
<lang Nim>import sequtils, strutils
const LibXml = "libxml2.so"
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}}==
|