I'm trying to learn nim's xmlparser and xmltree modules.
To do this I'm writing a program that will (eventually) pretty-print XML.
But unfortunately, it asserts on every XML file I've tried to run it with. The code and the smallest possible XML file I could make that asserts and the assert itself are as follows:
# xml2xml.nim
{.experimental: "codeReordering".}
import os
import strformat
import strtabs
import strutils
import xmlparser
import xmltree
const INDENT = 4
main()
proc main() =
let args = commandLineParams()
if len(args) == 0 or args[0] in ["/?", "-h", "--help"]:
let appname = lastPathPart(getAppFilename())
quit(&"usage: {appname} file.xml [file2.xml ...]")
for arg in args:
pretty(arg)
proc pretty(infilename: string) =
let i = infilename.rfind('.')
let outfilename = infilename[0 ..< i] & "#.xml"
var errors = newSeq[string]()
let root = loadXml(infilename, errors)
if len(errors) > 0:
for error in errors:
echo(&"Error: {error}")
try:
var file = open(outfilename, fmWrite)
defer: file.close()
file.write(xmlHeader)
write(file, root, 0)
except IOError as err:
echo(&"Failed to write to \"{outfilename}\": {err.msg}")
proc write(file: File, node: XmlNode, indent: int) =
if indent > 0:
file.write(spaces(indent))
file.write(&"<{node.tag}")
let attrs = node.attrs()
if attrs != nil:
for (key, value) in attrs.pairs():
file.write(&" {key}=\"{value}\"")
file.write(">")
if node.text != "":
file.write(node.text)
for child in node:
write(file, child, indent + INDENT)
file.write(&"</{node.tag}>\n")
The simpletest XML file I could make (meta.xml):
<?xml version="1.0" encoding="UTF-8"?>
<meta>
</meta>
The runtime assertion error:
/home/mark/app/nim/xml2xml/xml2xml.nim(16) xml2xml
/home/mark/app/nim/xml2xml/xml2xml.nim(24) main
/home/mark/app/nim/xml2xml/xml2xml.nim(38) pretty
/home/mark/app/nim/xml2xml/xml2xml.nim(51) write
/home/mark/opt/nim/lib/pure/xmltree.nim(176) text
/home/mark/opt/nim/lib/system/assertions.nim(27) failedAssertImpl
/home/mark/opt/nim/lib/system/assertions.nim(20) raiseAssert
/home/mark/opt/nim/lib/system/fatal.nim(39) sysFatal
Error: unhandled exception: /home/mark/opt/nim/lib/pure/xmltree.nim(176, 10) `n.k in {xnText, xnComment, xnCData, xnEntity}` [AssertionError]
The problem could, of course, be with my own code: maybe I don't understand how to use xmltree and XmlNode's properly yet.
Every XMLNode has a kind and I guess the reason you get this error is that you use the text field although the node isn't a text node. In that case you must check the node kind first:
if node.kind == xnText and node.text != "":
...
Besides, the result of $node for the top-level node may already do most if not all of what you want. That said, I don't know if $node is missing any subtle information that would be necessary to reproduce the original XML semantically.
I modified the write proc as suggested but it doesn't help:
if node.kind in {xnText, xnCData, xnEntity, xnComment} and
node.text != "":
file.write(node.text)
Assert:
fatal.nim(39) sysFatal
Error: unhandled exception: xmltree.nim(213, 10) `n.k == xnElement` [AssertionError]
I also tried:
if node.kind != xnElement and node.text != "":
file.write(node.text)
And that also asserted with the same message.
And just to be sure I then commented out the if statement so that I never use node.text; and it still gives exactly the same assertion.
I found that if I don't go into child nodes then it doesn't assert: but then it doesn't go through the XML file either! Here's a modified write that still asserts:
proc write(file: File, node: XmlNode, indent: int) =
if indent > 0:
file.write(spaces(indent))
file.write(&"<{node.tag}")
let attrs = node.attrs()
if attrs != nil:
for (key, value) in attrs.pairs():
file.write(&" {key}=\"{value}\"")
file.write(">")
if node.kind != xnElement and node.text != "":
file.write(node.text)
if len(node) > 0:
for child in node:
write(file, child, indent + INDENT)
file.write(&"</{node.tag}>\n")
I tried commenting out the attrs code and the node.text code but that didn't help.
SOLVED: The problem was with the signature. Even though I don't (knowingly) modify any nodes, changing the signature's node: XmlNode to node: var XmlNode cured the assertion error.