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:

dust232B
#_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}
go293B
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()
Result / 244B
{#_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

FormatUse it to
DUSTWrite readable document examples and query expressions.
JSONExchange documents with JSON readers.
EDNExchange values with Clojure readers, under the mapping rules of the codec.
TRONEncode 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:

text34B
{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

OperationReturnsSummary
Document.Serializetextdocument.serialize(format, width) writes text, or binary bytes for TRON.
Converttextconvert(text, from, to, width) calls parse and serialize, then releases the intermediate document.
Document.ToValuevaluedocument.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.
go72B
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

ParameterTypeOwnershipDefaultMeaning
formatFormatValueRequiredThe codec to use for this operation.
widthint64Value0 selects the formatter defaultThe requested formatter line width.

Errors

StatusWhen
StatusInvalidArgumentthe format is unknown or the width is negative
StatusClosedthe document is closed
StatusOutOfMemorynative memory is exhausted

Examples

document/serialize

expressionText holds this document:

dust9B
[+ ?x 1]
go192B
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
}
Result / 33B
{
  text '''
    [+ ?x 1]
  '''
}

document/serialize

numberText holds this document:

dust2B
1
go188B
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
}
Result / 118B
{
  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:

dust4B
Ada
go186B
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
}
Result / 68B
{
  text "TRON<Ada\u0004\u0000\u0000\u0000\u0000\u0000\u0000\u0000"}

document/serialize

expressionText holds this document:

dust9B
[+ ?x 1]
go192B
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
}
Result / 250B
{
  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:

dust9B
[+ ?x 1]
go192B
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
}
Result / 33B
{
  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.

go71B
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

ParameterTypeOwnershipDefaultMeaning
text[]byteValueRequiredEncoded input bytes in the selected format. TRON is binary.
fromFormatValueRequiredThe input codec.
toFormatValueRequiredThe output codec.
widthint64Value0 selects the formatter defaultThe requested formatter line width.

Errors

StatusWhen
StatusInvalidJSONJSON text is malformed
StatusInvalidArgumentthe text is malformed, a format is unknown or the width is negative
StatusOutOfMemorynative memory is exhausted

Examples

convert

expressionText holds these bytes:

text278B
// 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]
go110B
result, err := stardust.Convert(expressionText, stardust.DUST, stardust.TRON, 0)
if err != nil {
	return err
}
Result / 643B
{bytes {
   #b64 VFJPThwrLD94AgEAAAAAAAAADhUABwADAAAABAAAAAYAAAAJAAAAHHgCAQAAAAAAAAAPCicAAAApAAAAHHgCKQAAAAAAAAAPCjwAAAA+AAAADhEAAwACAAAAMgAAAEcAAAACAgAAAAAAAAACKgAAAAAAAAAOEQADAAIAAABiAAAAawAAAIwkY29tbWVudBTNT25lIGV4cHJlc3Npb24gYnVpbHQgb25jZSBhbmQgZXZhbHVhdGVkIGFnYWluc3Qgc2V2ZXJhbCBpbnB1dHMuIFRoZQpldmFsdWF0ZSBleGFtcGxlcyBsb2FkIHRoaXMgZmlsZSwgYnVpbGQgYGV4cHJlc3Npb25gLCBldmFsdWF0ZSBpdCB3aXRoIGVhY2gKb2YgYGlucHV0c2AgaW4gb3JkZXIgYW5kIGNvbXBhcmUgZWFjaCByZXN1bHQgd2l0aCBgZXhwZWN0ZWRgLg8KhQAAAI4AAABsaW5wdXRzDwpnAQAAUQAAAIxleHBlY3RlZA8KeAEAAHQAAAAHDgIBAABuAQAAgQEAAAcKAQAAAIsBAACsZXhwcmVzc2lvbg8KowEAABIAAAAHEkAJAABdAQAAmQEAAK4BAAC4AQAAAAAAAA==}}

convert

duplicateText holds this document:

dust14B
'{:a 1 :a 2}'
go339B
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()
}
Result / 149B
{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:

dust26B
#utc 2026-10-07T12:34:56Z
go188B
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
}
Result / 37B
{text '#inst "2026-10-07T12:34:56Z"'}

convert

containerText holds this document:

dust6B
[1.0]
go296B
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()
Result / 3B
[1]

convert

orderText holds this document:

dust8B
z 2
a 1
go292B
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()
Result / 10B
{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.
go41B
func (d *Document) ToValue() (any, error)
  • Concurrency: Other calls can overlap this call, except the release of a handle that this call uses.

Errors

StatusWhen
StatusClosedthe document is closed

Examples

Copy a native input into a host value

inputText holds this document:

dust5B
x 41
go258B
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()
Result / 6B
{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.

ValueJSONDUSTEDNTRON
null_booleanRound tripRound tripRound tripRound trip
integerRound tripChangedChangedChanged
floatRound tripRound tripRound tripChanged
non_finiteRefusedChangedRefusedRefused
decimal_ratioRound tripRound tripChangedChanged
stringRound tripRound tripChangedRound trip
member_orderRound tripRound tripRound tripChanged
duplicate_keysRound tripRound tripRefusedChanged
empty_objectRound tripRound tripRound tripRound trip
utcRound tripRound tripChangedRound trip
linkRound tripRound tripRound tripRound trip
binaryRound tripRound tripRound tripChanged
edn_formsNot applicableNot applicableChangedNot 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 f64 precision.
  • 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.

ValueHostDocumentBackMeaning
nullnilnullnilNil slices and maps also become null.
integersigned integersi64int64Signed host integers become i64.
floatfloat32 or float64f64float64The conversion refuses non-finite numbers.
textstringUTF-8 stringstringThe conversion refuses incorrect UTF-8.
array[]anyarray[]anyThe conversion refuses other slice types.
objectmap[string]anyobjectmap[string]anyInput keys use byte-sorted order.
timetime.Time or time.Durationrefusednot offeredTime values require explicit document construction.
to_value_numbersdocument numberi64 or f64int64 or float64The conversion tries typed i64 reads before f64.
duplicate_keysdocument objectduplicate membersfirst valueHost maps retain the first value for a duplicate key.