Skip to content

Latest commit

 

History

History
103 lines (75 loc) · 2.88 KB

xml.md

File metadata and controls

103 lines (75 loc) · 2.88 KB

🧬 xml

A standalone XML module that converts between XML text and a Lua node model.

  • xml.encode(node [, options]) → XML text from a node table. Alias: xml.stringify.
    • Setting options.pretty = true indents with two spaces. Setting options.indent = N indents with N spaces.
  • xml.decode(text) → The root node table. Alias: xml.parse. Raises on invalid input.

The node model round-trips losslessly:

{
  name = "tag",                 -- Element name
  attributes = { k = "v" },     -- Present only when the element has attributes
  children = { <node>, ... },   -- Child elements, in document order
  text = "...",                 -- Concatenated direct text, when present
}

Element names are sanitized and text is escaped on encode. The parser does not load external entities or expand DTD entities, and node conversion is depth-capped.

Examples

Aliases

-- Parse and stringify are aliases of decode and encode.
local xml = require("xml")

local node = xml.parse('<user id="1"><name>Lua</name></user>')
print(node.name, node.attributes.id, node.children[1].text)

print(xml.stringify({ name = "ok", text = "done" }))

Encode and decode

-- Build a document from the node model and parse it back.
local xml = require("xml")

local doc = xml.encode({
    name = "catalog",
    children = {
        {
            name = "book",
            attributes = { id = "1" },
            children = { { name = "title", text = "Varn" } },
        },
    },
}, { pretty = true })
print(doc)

local node = xml.decode(doc)
print(node.name, node.children[1].attributes.id, node.children[1].children[1].text)

Escaping

-- Text and attribute specials are escaped and invalid names are sanitized.
local xml = require("xml")

-- Special characters in text and attributes are escaped on encode.
print(xml.encode({ name = "msg", attributes = { note = 'a & b "c"' }, text = "x < y > z" }))

-- An invalid element name is sanitized so it cannot break the document.
print(xml.encode({ name = "bad name!", text = "ok" }))

-- Escaped values round-trip back to their original form.
local back = xml.decode(xml.encode({ name = "n", text = "a < b & c" }))
print(back.text)

Indent

-- Indent sets the indentation width in spaces.
local xml = require("xml")

local node = { name = "root", children = { { name = "item", text = "value" } } }
print(xml.encode(node, { indent = 4 }))

Roundtrip

-- Parse a document, walk its nodes, and re-encode it.
local xml = require("xml")

local original = '<note priority="high"><to>Lua</to><from>C++</from><body>hello &amp; bye</body></note>'

local node = xml.decode(original)
print("root:", node.name, "priority:", node.attributes.priority)
for _, child in ipairs(node.children) do
    print(child.name, "=", child.text)
end

print(xml.encode(node))

Under the hood

Parsing and serialization use the pugixml C++ library.