xml.md 8.8 KB

core.base.xml

This module provides a lightweight DOM-style XML toolkit that works inside Xmake's sandbox environment. It focuses on predictable data structures, JSON-like usability, and optional streaming so you can parse large XML documents without building the entire tree.

::: tip TIP To use this module, you need to import it first: import("core.base.xml") :::

The XML module features:

  • DOM-style node structure using plain Lua tables
  • Streaming parser for large files (xml.scan)
  • XPath-like queries (xml.find)
  • Convenient file I/O helpers (xml.loadfile, xml.savefile)
  • Support for comments, CDATA, DOCTYPE, and unquoted attributes
  • Pretty printing with customizable indentation

XML nodes are plain Lua tables with the following structure:

{
    name     = "element-name" | nil,  -- only for element nodes
    kind     = "element" | "text" | "comment" | "cdata" | "doctype" | "document",
    attrs    = { key = value, ... } or nil,
    text     = string or nil,
    children = { child1, child2, ... } or nil,
    prolog   = { comment/doctype nodes before root } or nil
}

xml.decode

  • Parse XML string into a document node

Function Prototype

::: tip API

xml.decode(xml_string: <string>, options?: <table>)

:::

Parameter Description

Parameter Description
xml_string XML string to parse
options Optional table with parsing options

Options

Option Description
trim_text = true Trim leading/trailing whitespace from text nodes
keep_whitespace_nodes = true Keep text nodes that contain only whitespace

Return Value

Type Description
table Document node (root of the XML tree)

Usage

import("core.base.xml")

local doc = assert(xml.decode([[
<?xml version="1.0"?>
<root id="1">
  <item id="foo">hello</item>
</root>
]]))

xml.encode

  • Encode document node to XML string

Function Prototype

::: tip API

xml.encode(doc: <table>, options?: <table>)

:::

Parameter Description

Parameter Description
doc Document node to encode
options Optional table with encoding options

Options

Option Description
pretty = true Enable pretty printing
indent Number of spaces for indentation (default: 2)
indentchar Character to use for indentation (default: " ")

Return Value

Type Description
string XML string representation

Usage

import("core.base.xml")

local doc = xml.decode("<root><item>hello</item></root>")
local pretty = assert(xml.encode(doc, {pretty = true, indent = 2}))

xml.loadfile

  • Load XML document from file

Function Prototype

::: tip API

xml.loadfile(filepath: <string>, options?: <table>)

:::

Parameter Description

Parameter Description
filepath Path to XML file
options Optional table with parsing options (same as xml.decode)

Return Value

Type Description
table Document node

Usage

import("core.base.xml")

local plist = assert(xml.loadfile("Info.plist"))

xml.savefile

  • Save XML document to file

Function Prototype

::: tip API

xml.savefile(filepath: <string>, doc: <table>, options?: <table>)

:::

Parameter Description

Parameter Description
filepath Path to save XML file
doc Document node to save
options Optional table with encoding options (same as xml.encode)

Return Value

Type Description
boolean Returns true on success

Usage

import("core.base.xml")

local doc = xml.loadfile("Info.plist")
-- ... modify nodes ...
assert(xml.savefile("Info.plist", doc, {pretty = true, indent = 2}))

xml.find

  • Find node using XPath-like query

Function Prototype

::: tip API

xml.find(doc: <table>, xpath: <string>)

:::

Parameter Description

Parameter Description
doc Document node or any node to search from
xpath XPath-like query string

Return Value

Type Description
table|nil Found node or nil if not found

Usage

import("core.base.xml")

local doc = assert(xml.loadfile("config.xml"))

-- Find by path
local element = xml.find(doc, "/root/item")

-- Find by attribute
local item = xml.find(doc, "//item[@id='foo']")

-- Find by text content
local node = xml.find(doc, "//string[text()='value']")

xml.scan

  • Stream parse XML and call callback for each node

Function Prototype

::: tip API

xml.scan(xml_string: <string>, callback: <function>, options?: <table>)

:::

Parameter Description

Parameter Description
xml_string XML string to parse
callback Function called for each node: function(node) -> boolean
options Optional table with parsing options (same as xml.decode)

Callback Return Value

Return false from the callback to stop scanning immediately.

Usage

import("core.base.xml")

local found
xml.scan(plist_text, function(node)
    if node.name == "key" and xml.text_of(node) == "NSPrincipalClass" then
        found = node
        return false -- early terminate
    end
end)

xml.scan walks nodes as they are completed; returning false stops the scan immediately. This is ideal for large files when you only need a few entries.

xml.text_of

  • Get text content of a node

Function Prototype

::: tip API

xml.text_of(node: <table>)

:::

Parameter Description

Parameter Description
node Node to extract text from

Return Value

Type Description
string Text content of the node

Usage

import("core.base.xml")

local node = xml.find(doc, "//item[@id='foo']")
local text = xml.text_of(node)  -- Returns "hello"

xml.text

  • Create a text node

Function Prototype

::: tip API

xml.text(text: <string>)

:::

Parameter Description

Parameter Description
text Text content

Return Value

Type Description
table Text node

Usage

import("core.base.xml")

local textnode = xml.text("hello")

xml.empty

  • Create an empty element node

Function Prototype

::: tip API

xml.empty(name: <string>, attrs?: <table>)

:::

Parameter Description

Parameter Description
name Element name
attrs Optional attributes table

Return Value

Type Description
table Element node

Usage

import("core.base.xml")

local empty = xml.empty("br", {class = "line"})

xml.comment

  • Create a comment node

Function Prototype

::: tip API

xml.comment(text: <string>)

:::

Parameter Description

Parameter Description
text Comment text

Return Value

Type Description
table Comment node

Usage

import("core.base.xml")

local comment = xml.comment("generated by xmake")

xml.cdata

  • Create a CDATA node

Function Prototype

::: tip API

xml.cdata(text: <string>)

:::

Parameter Description

Parameter Description
text CDATA text

Return Value

Type Description
table CDATA node

Usage

import("core.base.xml")

local cdata_node = xml.cdata("if (value < 1) {...}")

xml.doctype

  • Create a DOCTYPE node

Function Prototype

::: tip API

xml.doctype(declaration: <string>)

:::

Parameter Description

Parameter Description
declaration DOCTYPE declaration string

Return Value

Type Description
table DOCTYPE node

Usage

import("core.base.xml")

local doctype = xml.doctype('plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"')

Complete Example

import("core.base.xml")

-- Parse XML string
local doc = assert(xml.decode([[
<?xml version="1.0"?>
<root id="1">
  <item id="foo">hello</item>
</root>
]]))

-- Find and modify nodes
local item = assert(xml.find(doc, "//item[@id='foo']"))
item.attrs.lang = "en"             -- mutate attrs directly
item.children = {xml.text("world")} -- replace existing text node

-- Add comment
table.insert(doc.children, xml.comment("generated by xmake"))

-- Encode with pretty printing
local pretty = assert(xml.encode(doc, {pretty = true}))
assert(xml.savefile("out.xml", doc, {pretty = true}))