Formats and conversion

Convert between JSON, DUST, EDN and TRON with the library's parse and serialize operations, through the C ABI or any binding. 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

people-text holds this document:

json333 B
{
  "#_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"
  }
}
clojure96 B
(with-open [people (sd/parse people-text :json)]
  (sd/parse (sd/serialize people :json) :json))
Result / 332 B
{
  "#_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 EDN mapping rules.
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.

EDN query variables

EDN symbols such as ?name parse as native strings. Serialization writes a native string such as "?name" as the EDN symbol ?name. The string must start with ?, contain a name, and form one valid EDN symbol token. Other string values remain quoted.

Query variables therefore keep their symbol form through EDN, JSON, DUST and TRON conversions. Clojure users can quote a query map to pass its symbols as data:

clojure204 B
(with-open [query (sd/from-value
                   '{:find [?name ?age]
                     :where [[?e :name ?name] [?e :age ?age]]
                     :orderBy [?name]})]
  (sd/serialize query :edn))

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:

text34 B
{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. Serialization never writes several members on one line of a multi-line object.

Operations

OperationReturnsSummary
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.

serialize

clojure17 B
[document format]
clojure23 B
[document format width]

Parameters

ParameterTypeOwnershipDefaultMeaning
documentDocumentBorrowed for the callRequiredThe document owner for this operation. Keep it alive during the call.
formatkeywordValueRequiredThe codec to use for this operation.
widthIntegerValue0 selects the formatter defaultThe requested formatter line width.

Errors

StatusWhen
:invalid-argumentthe format is unknown or the width is negative
:closedthe document is closed
:out-of-memorynative memory is exhausted

Examples

document/serialize

expression-text holds this document:

json23 B
[
  "+",
  "?x",
  1
]
clojure83 B
(with-open [source (sd/parse expression-text :json)]
  (sd/serialize source :json))
Result / 32 B
{
  "text": "[\"+\",\"?x\",1]"
}

document/serialize

x-text holds this document:

json2 B
1
clojure74 B
(with-open [source (sd/parse x-text :json)]
  (sd/serialize source :tron))
Result / 122 B
{
  "text": "TRON\u0002\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0004\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
}

document/serialize

name-text holds this document:

json6 B
"Ada"
clojure77 B
(with-open [source (sd/parse name-text :json)]
  (sd/serialize source :tron))
Result / 72 B
{
  "text": "TRON<Ada\u0004\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
}

document/serialize

expression-text holds this document:

json23 B
[
  "+",
  "?x",
  1
]
clojure83 B
(with-open [source (sd/parse expression-text :json)]
  (sd/serialize source :tron))
Result / 254 B
{
  "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

expression-text holds this document:

json23 B
[
  "+",
  "?x",
  1
]
clojure83 B
(with-open [source (sd/parse expression-text :json)]
  (sd/serialize source :json))
Result / 32 B
{
  "text": "[\"+\",\"?x\",1]"
}

C exports: stardust_serialize, stardust_text_data, stardust_text_error, stardust_text_release

convert

clojure14 B
[text from to]
clojure20 B
[text from to width]
clojure28 B
[text from to width options]

Parameters

ParameterTypeOwnershipDefaultMeaning
textTextValueRequiredEncoded input bytes in the selected format. TRON is binary.
fromkeywordValueRequiredThe input codec.
tokeywordValueRequiredThe output codec.
widthIntegerValue0 selects the formatter defaultThe requested formatter line width.
optionsmapValue{}Binding options map. The :library key selects the loaded native library. Omission uses the binding's default library.

Errors

StatusWhen
:invalid-jsonJSON text is malformed
:invalid-argumentthe text is malformed, a format is unknown or the width is negative
:out-of-memorynative memory is exhausted

Examples

convert

expression-text holds these bytes:

text278 B
// 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]
clojure40 B
(sd/convert expression-text :dust :tron)
Result / 659 B
{
  "bytes": {
    "#b64": "VFJPThwrLD94AgEAAAAAAAAADhUABwADAAAABAAAAAYAAAAJAAAAHHgCAQAAAAAAAAAPCicAAAApAAAAHHgCKQAAAAAAAAAPCjwAAAA+AAAADhEAAwACAAAAMgAAAEcAAAACAgAAAAAAAAACKgAAAAAAAAAOEQADAAIAAABiAAAAawAAAIwkY29tbWVudBTNT25lIGV4cHJlc3Npb24gYnVpbHQgb25jZSBhbmQgZXZhbHVhdGVkIGFnYWluc3Qgc2V2ZXJhbCBpbnB1dHMuIFRoZQpldmFsdWF0ZSBleGFtcGxlcyBsb2FkIHRoaXMgZmlsZSwgYnVpbGQgYGV4cHJlc3Npb25gLCBldmFsdWF0ZSBpdCB3aXRoIGVhY2gKb2YgYGlucHV0c2AgaW4gb3JkZXIgYW5kIGNvbXBhcmUgZWFjaCByZXN1bHQgd2l0aCBgZXhwZWN0ZWRgLg8KhQAAAI4AAABsaW5wdXRzDwpnAQAAUQAAAIxleHBlY3RlZA8KeAEAAHQAAAAHDgIBAABuAQAAgQEAAAcKAQAAAIsBAACsZXhwcmVzc2lvbg8KowEAABIAAAAHEkAJAABdAQAAmQEAAK4BAAC4AQAAAAAAAA=="
  }
}

convert

duplicate-text holds this document:

json14 B
"{:a 1 :a 2}"
clojure112 B
(with-open [source (sd/parse duplicate-text :json)]
  (sd/parse (:text (sd/read source (sd/root source))) :edn))
Result / 181 B
{
  "code": "invalid_edn",
  "message": "invalid EDN (Duplicate_Key) at offset 6",
  "path": null,
  "field": null,
  "entity": null,
  "offset": 6,
  "status": "invalid_argument"
}

convert

instant-text holds this document:

json37 B
{
  "#utc": "2026-10-07T12:34:56Z"
}
clojure79 B
(with-open [source (sd/parse instant-text :json)]
  (sd/serialize source :edn))
Result / 46 B
{
  "text": "#inst \"2026-10-07T12:34:56Z\""
}

convert

container-text holds this document:

json10 B
[
  1.0
]
clojure104 B
(with-open [source (sd/parse container-text :json)]
  (-> (sd/serialize source :tron) (sd/parse :tron)))
Result / 7 B
[
  1
]

convert

order-text holds this document:

json23 B
{
  "z": 2,
  "a": 1
}
clojure100 B
(with-open [source (sd/parse order-text :json)]
  (-> (sd/serialize source :tron) (sd/parse :tron)))
Result / 22 B
{
  "a": 1,
  "z": 2
}

C exports: stardust_parse, stardust_serialize, stardust_text_data, stardust_text_error, stardust_text_release

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.

The host mapping on the Language values and documents page shows how language values become documents without a text format.