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 vector or range field (
int2..``float4``, ranges) maps to the same-named attribute holding the space-separated components, e.g.float3(1, 2, 3)<->"1 2 3";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 fixed-array field (
int[3],float3[2], nestedint[3][3]) maps to a same-named child element holding one<item>per element; the size is fixed, so a source with too few items leaves the trailing cells at their declared defaults (subject to the struct-ctor caveat underfrom_xml) and extra items are ignored;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,stringVector and range types (
int2..``float4``, ranges): space-separated components in one attribute, e.g."1 2 3"Enums 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, a float or double is printed in a form that reads back to the same value. Tables and tuples are not supported.
Round-trip limitations
Ordinary flat/vector/struct/array data round-trips exactly. These shapes do not, because XML has no lossless place for them under the single convention:
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, primitive types and vector/range types (int2..``float4``, ranges),
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);
see Round-trip limitations on the XML page for the exceptions
(variant arm selection, string whitespace/newline normalization).
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 and fixed-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 - and the trailing cells of a fixed array the XML does not fill - 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 and fixed-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/vector/struct/array data round-trips (from_xml(to_xml(x)) == x); see
Round-trip limitations on the XML page for the exceptions (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). A float or double is printed in a form that reads back to the same value.
- 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 and reads back via
from_xmlinto a flat var.- 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