XML¶
To use this module, include the following line in your project file:
require engine.format.xml_core // or require engine.core
Serialize typed Daslang values to XML strings and back.
Use from_xml to parse an XML string into a typed Daslang value, and to_xml to print one
back out - the mirror of from_json / to_json.
Usage example:
struct Player {
name : string
score : int
}
// <player name="Alice" score="42"/>
var loaded : Player
from_xml("<player name=\"Alice\" score=\"42\"/>", loaded)
// loaded.name == "Alice", loaded.score == 42
let xml = to_xml(loaded) // "<root name=\"Alice\" score=\"42\"/>" (pretty-printed)
from_xml walks from the document’s single root element, so the destination struct maps to that
root (the way a JSON object root maps to a struct for from_json). to_xml wraps the value in
a <root> element; the root’s own name is irrelevant to from_xml, so the two round-trip.
Mapping convention
One convention decides where each field maps - no per-field configuration is needed beyond the two
annotations below - and it is symmetric: to_xml writes what from_xml reads. Ordinary
struct/array data round-trips (from_xml(to_xml(x)) == x); the exceptions are listed under
Round-trip limitations below.
a flat field (
bool,int,uint,int64,uint64,float,double,string, enum, bitfield) maps to the same-named attribute of the struct’s element;a struct field maps to a single same-named child element;
an array<T> field maps to the repeated same-named child elements, in document order;
a field marked
@xml_inner_textmaps to the element’s text content.
On read, unknown attributes and child elements are ignored, exactly as extra JSON keys are ignored
by from_json. Fields whose attribute/element is absent keep the value the destination already
has (its field initializer default on a freshly constructed value).
Field annotations
@rename = "key"Use
keyas the attribute/element name instead of the field name. The same annotationfrom_jsonuses, so one struct reads from both formats. Also needed when the XML name is a Daslang reserved word (e.g.type,template):struct Object { @rename = "type" type_ : string } // reads the "type" attribute: <object type="hero"/>
@xml_inner_textRead the field from the element’s text content (PCDATA and CDATA) rather than an attribute or child. Element text has no name, so it cannot follow the same-named rule and needs this marker:
struct Data { @rename = "encoding" encoding : string // <data encoding="csv"> attribute @xml_inner_text text : string // the text between <data> and </data> }
CDATA
Text read via @xml_inner_text includes CDATA sections verbatim, so characters that would
otherwise need escaping (<, &) pass through unchanged:
struct Note {
@xml_inner_text
text : string
}
var note : Note
from_xml("<note><![CDATA[a<b & c]]></note>", note)
// note.text == "a<b & c"
Supported types
Primitives:
bool,int,uint,int64,uint64,float,double,stringEnums and bitfields (enums matched by member name; bitfields parsed from a numeric string)
Structs (non-class, non-lambda)
array<T>(element type must be serializable)
On read, numeric/bool/string values are parsed from their attribute or text string; a value that fails to parse leaves the field at its current value (its default), so malformed input never corrupts a field. On write, floats and doubles are printed at full precision, so they survive a round trip bit-for-bit. Tables and tuples are not supported.
Round-trip limitations
Ordinary flat/struct/array data round-trips exactly. These shapes do not, because XML has no lossless place for them under the single convention:
Vector types (
int2..``float4``) have no serializer: a vector field writes nothing and reads back as its default; a vector root writes an empty<root/>(and cannot be read back).Variant arm selection on read is a best-effort guess from the text (there is no type tag), so an arm can change on the round trip when two arms share a text shape: a
floatarm holding an integral value (2.0prints as2) reads back as anintarm; astringarm holding a numeric string ("42") reads as a number arm. A struct arm is picked when the element has a child element, or as the fallback when no flat/string arm matches - so an all-attribute struct arm round-trips as long as no flat/string arm wins classification first.String normalization: leading/trailing whitespace in an attribute value or inner text is kept, but a whitespace-only inner text reads back as
""(the XML parser drops it); a carriage return (\r) is folded to\n; anullstring reads back as"".
File loading
The engine imports .xml files as text assets (alongside .txt and .json); read a file’s
contents with request_text and pass the string to from_xml.
Functions¶
- from_xml(xml_str: string; value: any)¶
Deserializes an XML string into a typed Daslang value.
Supports structs, arrays and primitive types, parsing from the document’s root element. The
inverse is to_xml, which writes exactly what from_xml reads (from_xml(to_xml(x)) == x).
One mapping convention decides where each field’s value comes from (no per-field DSL):
a flat field (int/float/bool/string/enum/bitfield) reads the same-named attribute of the struct’s element;
a struct field reads a single same-named child element;
an
array<T>field reads the repeated same-named child elements, in document order;a field annotated
@xml_inner_textreads the element’s text content (PCDATA and CDATA).
@rename = "key" overrides the attribute/element name (the same annotation from_json
honors), so a struct annotated for JSON reads from XML unchanged. Unknown attributes and child
elements are ignored, exactly as from_json ignores extra JSON keys.
Fields missing in the XML keep the value the destination variable already has. Array elements are created by the deserializer and initialized with the struct’s field initializers (by running its generated zero-argument constructor), so attributes absent on an element read as the declared defaults.
Note
The generated constructor of a struct that is never constructed explicitly in script code can
be removed by dead code elimination - array elements of such a struct then stay
zero-initialized and the field initializers are lost. Add options always_export_initializer
to the module declaring the struct to keep its constructor alive.
- Arguments:
xml_str : string implicit - XML string to parse
value : any - variable to deserialize into (must be
var, non-const)
Usage example:
options always_export_initializer // keep struct ctors for from_xml defaults
struct Tile {
id : int
@rename = "type"
type_ : string
visible : bool = true // absent attribute -> keeps this default
}
struct Layer {
name : string
@rename = "tile"
tile : array<Tile> // repeated <tile> children
}
var layer : Layer
from_xml("<layer name=\"ground\"><tile id=\"1\" type=\"grass\"/><tile id=\"2\"/></layer>", layer)
// layer.name == "ground"
// layer.tile[0].id == 1, layer.tile[0].type_ == "grass", layer.tile[0].visible == true
// layer.tile[1].id == 2, layer.tile[1].visible == true (default kept)
// @xml_inner_text reads the element body (CDATA passes '<' and '&' through verbatim)
struct Note {
@xml_inner_text
text : string
}
var note : Note
from_xml("<note><![CDATA[a<b & c]]></note>", note)
// note.text == "a<b & c"
- to_xml(value: any): string¶
Serializes a typed Daslang value to an XML string. The inverse of from_xml.
The value is wrapped in a <root> element and pretty-printed. The field mapping is the same one
from_xml reads: ordinary flat/struct/array data round-trips (from_xml(to_xml(x)) == x); see
Round-trip limitations on the XML page for the exceptions (vectors,
variant arm selection, string whitespace/newline normalization). The mapping:
a flat field (int/float/bool/string/enum/bitfield) is written as the same-named attribute;
a struct field is written as a single same-named child element;
an
array<T>field is written as the repeated same-named child elements;a field annotated
@xml_inner_textis written as the element’s text content.
@rename = "key" overrides the attribute/element name (the same annotation from_json and
to_json honor). Floats and doubles are printed at full precision, so a value survives a round
trip bit-for-bit.
- Arguments:
value : any - any serializable value - a struct/array root, or a flat root (int/float/string) which
is written as the root element’s text. Reading back requires a struct/array destination, so a bare flat root does not round-trip.
- Returns:
string - pretty-printed XML string with a
<root>element
Usage example:
struct Player {
name : string
@rename = "type"
type_ : string
score : int
}
let p = Player(name = "Alice", type_ = "hero", score = 42)
let xml = to_xml(p)
// <root name="Alice" type="hero" score="42" />
var loaded : Player
from_xml(xml, loaded) // round-trips: loaded == p