Formats and conversion
Serialization writes a document as text, or as binary bytes for TRON. The example parses the people document and serializes it in the same format.
Example
Serialize the people document
peopleText holds this document:
#_ada {name Ada
role engineer
age 36}
#_bob {name Bob
role engineer
age 41
mentor {#link #_ada}}
#_cy {name Cy
role designer
age 29}
#_tx {author smoke
reason seed}
people, err := stardust.Parse(peopleText, stardust.DUST)
if err != nil {
return err
}
defer people.Close()
output, err := people.Serialize(stardust.DUST, 0)
if err != nil {
return err
}
result, err := stardust.Parse(output, stardust.DUST)
if err != nil {
return err
}
defer result.Close(){#_ada {name Ada
role engineer
age 36}
#_bob {name Bob
role engineer
age 41
mentor {#link #_ada}}
#_cy {name Cy
role designer
age 29}
#_tx {author smoke
reason seed}}Choosing a format
| Format | Use it to |
|---|---|
| DUST | Write readable document examples and query expressions. |
| JSON | Exchange documents with JSON readers. |
| EDN | Exchange values with Clojure readers, under the mapping rules of the codec. |
| TRON | Encode a document as binary bytes. |
Warning
A format can change number spelling, member order, or tagged values. Check the fidelity table before you depend on them.
DUST layout
An object on one line, {op a args {t 1} e 1}, separates its members with whitespace. A top-level document, and any object that spans lines, holds exactly one member per line, and the rest of the line is the value of that member:
{name Ada Lovelace
role engineer}This object is {"name":"Ada Lovelace","role":"engineer"}. In a multi-line object, {op a args 1 is the member op with the string a args 1. A line that cannot be one bare value, such as {op a args {t 1}, fails with Members_On_One_Line and the offset of its second key. Start each member on its own line, or keep the whole object on one line. The formatter never writes several members on one line of a multi-line object.
Operations
| Operation | Returns | Summary |
|---|---|---|
| Document.Serialize | text | document.serialize(format, width) writes text, or binary bytes for TRON. |
| Convert | text | convert(text, from, to, width) calls parse and serialize, then releases the intermediate document. |
| Document.ToValue | value | document.to_value() copies a document into host values. |
Document.Serialize
document.serialize(format, width) writes text, or binary bytes for TRON.
- Width controls DUST and EDN line wrapping.
- Zero selects the default.
func (d *Document) Serialize(format Format, width int64) ([]byte, error)- Concurrency: Other calls can overlap this call, except the release of a handle that this call uses.
Parameters
| Parameter | Type | Ownership | Default | Meaning |
|---|---|---|---|---|
format | Format | Value | Required | The codec to use for this operation. |
width | int64 | Value | 0 selects the formatter default | The requested formatter line width. |
Errors
| Status | When |
|---|---|
StatusInvalidArgument | the format is unknown or the width is negative |
StatusClosed | the document is closed |
StatusOutOfMemory | native memory is exhausted |
Examples
document/serialize
expressionText holds this document:
[+ ?x 1]
source, err := stardust.Parse(expressionText, stardust.DUST)
if err != nil {
return err
}
defer source.Close()
result, err := source.Serialize(stardust.DUST, 0)
if err != nil {
return err
}{
text '''
[+ ?x 1]
'''
}document/serialize
numberText holds this document:
1
source, err := stardust.Parse(numberText, stardust.DUST)
if err != nil {
return err
}
defer source.Close()
result, err := source.Serialize(stardust.TRON, 0)
if err != nil {
return err
}{
text "TRON\u0002\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0004\u0000\u0000\u0000\u0000\u0000\u0000\u0000"}document/serialize
nameText holds this document:
Ada
source, err := stardust.Parse(nameText, stardust.DUST)
if err != nil {
return err
}
defer source.Close()
result, err := source.Serialize(stardust.TRON, 0)
if err != nil {
return err
}{
text "TRON<Ada\u0004\u0000\u0000\u0000\u0000\u0000\u0000\u0000"}document/serialize
expressionText holds this document:
[+ ?x 1]
source, err := stardust.Parse(expressionText, stardust.DUST)
if err != nil {
return err
}
defer source.Close()
result, err := source.Serialize(stardust.TRON, 0)
if err != nil {
return err
}{
text "TRON\u001c+,?x\u0002\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u000e\u0015\u0000\u0007\u0000\u0003\u0000\u0000\u0000\u0004\u0000\u0000\u0000\u0006\u0000\u0000\u0000\t\u0000\u0000\u0000\u0012\u0000\u0000\u0000\u0000\u0000\u0000\u0000"}document/serialize
expressionText holds this document:
[+ ?x 1]
source, err := stardust.Parse(expressionText, stardust.DUST)
if err != nil {
return err
}
defer source.Close()
result, err := source.Serialize(stardust.DUST, 0)
if err != nil {
return err
}{
text '''
[+ ?x 1]
'''
}C exports: stardust_serialize, stardust_text_data, stardust_text_error, stardust_text_release
Convert
convert(text, from, to, width) calls parse and serialize, then releases the intermediate document.
func Convert(text []byte, from, to Format, width int64) ([]byte, error)- Concurrency: Other calls can overlap this call, except the release of a handle that this call uses.
Parameters
| Parameter | Type | Ownership | Default | Meaning |
|---|---|---|---|---|
text | []byte | Value | Required | Encoded input bytes in the selected format. TRON is binary. |
from | Format | Value | Required | The input codec. |
to | Format | Value | Required | The output codec. |
width | int64 | Value | 0 selects the formatter default | The requested formatter line width. |
Errors
| Status | When |
|---|---|
StatusInvalidJSON | JSON text is malformed |
StatusInvalidArgument | the text is malformed, a format is unknown or the width is negative |
StatusOutOfMemory | native memory is exhausted |
Examples
convert
expressionText holds these bytes:
// One expression built once and evaluated against several inputs. The
// evaluate examples load this file, build `expression`, evaluate it with each
// of `inputs` in order and compare each result with `expected`.
expression [+ ?x 1]
inputs [{x 1} {x 41}]
expected [2 42]result, err := stardust.Convert(expressionText, stardust.DUST, stardust.TRON, 0)
if err != nil {
return err
}{bytes {
#b64 VFJPThwrLD94AgEAAAAAAAAADhUABwADAAAABAAAAAYAAAAJAAAAHHgCAQAAAAAAAAAPCicAAAApAAAAHHgCKQAAAAAAAAAPCjwAAAA+AAAADhEAAwACAAAAMgAAAEcAAAACAgAAAAAAAAACKgAAAAAAAAAOEQADAAIAAABiAAAAawAAAIwkY29tbWVudBTNT25lIGV4cHJlc3Npb24gYnVpbHQgb25jZSBhbmQgZXZhbHVhdGVkIGFnYWluc3Qgc2V2ZXJhbCBpbnB1dHMuIFRoZQpldmFsdWF0ZSBleGFtcGxlcyBsb2FkIHRoaXMgZmlsZSwgYnVpbGQgYGV4cHJlc3Npb25gLCBldmFsdWF0ZSBpdCB3aXRoIGVhY2gKb2YgYGlucHV0c2AgaW4gb3JkZXIgYW5kIGNvbXBhcmUgZWFjaCByZXN1bHQgd2l0aCBgZXhwZWN0ZWRgLg8KhQAAAI4AAABsaW5wdXRzDwpnAQAAUQAAAIxleHBlY3RlZA8KeAEAAHQAAAAHDgIBAABuAQAAgQEAAAcKAQAAAIsBAACsZXhwcmVzc2lvbg8KowEAABIAAAAHEkAJAABdAQAAmQEAAK4BAAC4AQAAAAAAAA==}}convert
duplicateText holds this document:
'{:a 1 :a 2}'
source, err := stardust.Parse(duplicateText, stardust.DUST)
if err != nil {
return err
}
defer source.Close()
root, err := source.Root()
if err != nil {
return err
}
view, err := source.Read(root)
if err != nil {
return err
}
failed, result := stardust.Parse([]byte(view.Text), stardust.EDN)
if failed != nil {
defer failed.Close()
}{code invalid_edn
message invalid EDN (Duplicate_Key) at offset 6
path null
field null
entity null
offset 6
status invalid_argument}convert
instantText holds this document:
#utc 2026-10-07T12:34:56Z
source, err := stardust.Parse(instantText, stardust.DUST)
if err != nil {
return err
}
defer source.Close()
result, err := source.Serialize(stardust.EDN, 0)
if err != nil {
return err
}{text '#inst "2026-10-07T12:34:56Z"'}convert
containerText holds this document:
[1.0]
source, err := stardust.Parse(containerText, stardust.DUST)
if err != nil {
return err
}
defer source.Close()
output, err := source.Serialize(stardust.TRON, 0)
if err != nil {
return err
}
result, err := stardust.Parse(output, stardust.TRON)
if err != nil {
return err
}
defer result.Close()[1]
convert
orderText holds this document:
z 2
a 1
source, err := stardust.Parse(orderText, stardust.DUST)
if err != nil {
return err
}
defer source.Close()
output, err := source.Serialize(stardust.TRON, 0)
if err != nil {
return err
}
result, err := stardust.Parse(output, stardust.TRON)
if err != nil {
return err
}
defer result.Close(){a 1
z 2}C exports: stardust_parse, stardust_serialize, stardust_text_data, stardust_text_error, stardust_text_release
Document.ToValue
document.to_value() copies a document into host values.
- Host map semantics determine how duplicate keys are represented.
- The copied value survives document closure.
func (d *Document) ToValue() (any, error)- Concurrency: Other calls can overlap this call, except the release of a handle that this call uses.
Errors
| Status | When |
|---|---|
StatusClosed | the document is closed |
Examples
Copy a native input into a host value
inputText holds this document:
x 41
input, err := stardust.Parse(inputText, stardust.DUST)
if err != nil {
return err
}
defer input.Close()
value, err := input.ToValue()
if err != nil {
return err
}
result, err := stardust.FromValue(value)
if err != nil {
return err
}
defer result.Close(){x 41}C exports: stardust_document_count, stardust_document_read_many, stardust_document_i64, stardust_document_f64
Fidelity
Each cell shows the result after parsing and serialization in that format. The notes below the table explain each result.
| Value | JSON | DUST | EDN | TRON |
|---|---|---|---|---|
null_boolean | Round trip | Round trip | Round trip | Round trip |
integer | Round trip | Changed | Changed | Changed |
float | Round trip | Round trip | Round trip | Changed |
non_finite | Refused | Changed | Refused | Refused |
decimal_ratio | Round trip | Round trip | Changed | Changed |
string | Round trip | Round trip | Changed | Round trip |
member_order | Round trip | Round trip | Round trip | Changed |
duplicate_keys | Round trip | Round trip | Refused | Changed |
empty_object | Round trip | Round trip | Round trip | Round trip |
utc | Round trip | Round trip | Changed | Round trip |
link | Round trip | Round trip | Round trip | Round trip |
binary | Round trip | Round trip | Round trip | Changed |
edn_forms | Not applicable | Not applicable | Changed | Not applicable |
Fidelity notes
null_boolean- JSON: Round trip. Null and booleans keep their values.
- DUST: Round trip. Null and booleans keep their values.
- EDN: Round trip. EDN nil maps to null.
- TRON: Round trip. Binary tags preserve null and booleans.
integer- JSON: Round trip. The codec preserves integer spelling.
- DUST: Changed. Leading-zero tokens are strings.
- EDN: Changed. Integer spelling is canonical.
- TRON: Changed. Integers keep their value and use canonical spelling.
float- JSON: Round trip. The codec preserves float spelling.
- DUST: Round trip. The codec preserves float spelling.
- EDN: Round trip. The codec preserves this float spelling.
- TRON: Changed. Integral floats in containers become integers.
non_finite- JSON: Refused. JSON has no non-finite numbers.
- DUST: Changed. NaN is a string.
- EDN: Refused. The parser rejects non-finite numeric values.
- TRON: Refused. The parser rejects non-finite binary values.
decimal_ratio- JSON: Round trip. The codec preserves finite decimal spelling.
- DUST: Round trip. The codec preserves finite decimal spelling.
- EDN: Changed. Ratios reduce to native numbers.
- TRON: Changed. Decimals use
f64precision.
string- JSON: Round trip. Strings require UTF-8.
- DUST: Round trip. Number-like strings require quotes.
- EDN: Changed. A lone surrogate becomes a question mark.
- TRON: Round trip. Decoded strings require UTF-8.
member_order- JSON: Round trip. The codec preserves object order.
- DUST: Round trip. The codec preserves object order.
- EDN: Round trip. The codec preserves map order.
- TRON: Changed. Members use trie order.
duplicate_keys- JSON: Round trip. The codec preserves all duplicate members.
- DUST: Round trip. The codec preserves all duplicate members.
- EDN: Refused. The parser rejects equal map keys.
- TRON: Changed. The codec keeps the last duplicate value.
empty_object- JSON: Round trip. The empty object is braces.
- DUST: Round trip. The empty root object serializes to zero bytes.
- EDN: Round trip. The empty map is braces.
- TRON: Round trip. The empty object has a binary footer.
utc- JSON: Round trip. UTC remains a tagged object.
- DUST: Round trip. UTC uses a tagged object.
- EDN: Changed. Millisecond instants use the EDN inst tag.
- TRON: Round trip. UTC remains a plain map.
link- JSON: Round trip. Links remain tagged objects.
- DUST: Round trip. Links remain tagged objects.
- EDN: Round trip. Links remain maps.
- TRON: Round trip. Links remain maps.
binary- JSON: Round trip. Base64 remains a tagged object.
- DUST: Round trip. Base64 remains a tagged object.
- EDN: Round trip. Base64 remains a map.
- TRON: Changed. Valid padded base64 in a sole member becomes a binary node.
edn_forms- JSON: Not applicable. EDN forms have no JSON syntax.
- DUST: Not applicable. EDN forms have no DUST syntax.
- EDN: Changed. The parser converts keywords, symbols, characters, sets and lists to native values. It omits metadata and discarded forms. It reads only the first form.
- TRON: Not applicable. EDN forms have no TRON syntax.
Host mapping
from_value copies a host value into a document, and to_value copies it back.
| Value | Host | Document | Back | Meaning |
|---|---|---|---|---|
null | nil | null | nil | Nil slices and maps also become null. |
integer | signed integers | i64 | int64 | Signed host integers become i64. |
float | float32 or float64 | f64 | float64 | The conversion refuses non-finite numbers. |
text | string | UTF-8 string | string | The conversion refuses incorrect UTF-8. |
array | []any | array | []any | The conversion refuses other slice types. |
object | map[string]any | object | map[string]any | Input keys use byte-sorted order. |
time | time.Time or time.Duration | refused | not offered | Time values require explicit document construction. |
to_value_numbers | document number | i64 or f64 | int64 or float64 | The conversion tries typed i64 reads before f64. |
duplicate_keys | document object | duplicate members | first value | Host maps retain the first value for a duplicate key. |