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:

edn287 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"}}
rust156 B
let people = stardust::parse(people_text, Format::EDN)?;
let output = people.serialize(Format::EDN, 0)?;
let result = stardust::parse(output, Format::EDN)?;
Result / 384 B
{:value {:#_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
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::serialize

rust70 B
pub fn serialize(&self, format: Format, width: i64) -> Result<Vec<u8>>

Parameters

ParameterTypeOwnershipDefaultMeaning
self&SelfBorrowed for the callRequiredThe document owner for this operation. Keep it alive during the call.
formatFormatValueRequiredThe codec to use for this operation.
widthi64Value0 selects the formatter defaultThe requested formatter line width.

Errors

Close consumes this owner. Rust ownership prevents later access. Thus, the safe API does not report closed for this receiver.

StatusWhen
Status::INVALID_ARGUMENTthe format is unknown or the width is negative
Status::OUT_OF_MEMORYnative memory is exhausted

Examples

document/serialize

source_text holds this document:

edn10 B
["+" ?x 1]
rust104 B
let source = stardust::parse(source_text, Format::EDN)?;
let result = source.serialize(Format::EDN, 0)?;
Result / 31 B
{:value {:text "[\"+\" ?x 1]"}}

document/serialize

source_text holds this document:

edn1 B
1
rust105 B
let source = stardust::parse(source_text, Format::EDN)?;
let result = source.serialize(Format::TRON, 0)?;
Result / 125 B
{:value {:text "TRON\u0002\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0004\u0000\u0000\u0000\u0000\u0000\u0000\u0000"}}

document/serialize

source_text holds this document:

edn5 B
"Ada"
rust105 B
let source = stardust::parse(source_text, Format::EDN)?;
let result = source.serialize(Format::TRON, 0)?;
Result / 75 B
{:value {:text "TRON<Ada\u0004\u0000\u0000\u0000\u0000\u0000\u0000\u0000"}}

document/serialize

source_text holds this document:

edn10 B
["+" ?x 1]
rust105 B
let source = stardust::parse(source_text, Format::EDN)?;
let result = source.serialize(Format::TRON, 0)?;
Result / 257 B
{:value {: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

source_text holds this document:

edn10 B
["+" ?x 1]
rust104 B
let source = stardust::parse(source_text, Format::EDN)?;
let result = source.serialize(Format::EDN, 0)?;
Result / 31 B
{:value {:text "[\"+\" ?x 1]"}}

C exports: stardust_serialize, stardust_text_data, stardust_text_error, stardust_text_release

convert

rust95 B
pub fn convert(text: impl AsRef<[u8]>, from: Format, to: Format, width: i64) -> Result<Vec<u8>>

Parameters

ParameterTypeOwnershipDefaultMeaning
textimpl AsRef<[u8]>ValueRequiredEncoded input bytes in the selected format. TRON is binary.
fromFormatValueRequiredThe input codec.
toFormatValueRequiredThe output codec.
widthi64Value0 selects the formatter defaultThe requested formatter line width.

Errors

StatusWhen
Status::INVALID_JSONJSON text is malformed
Status::INVALID_ARGUMENTthe text is malformed, a format is unknown or the width is negative
Status::OUT_OF_MEMORYnative memory is exhausted

Examples

convert

expression 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]
rust75 B
let result = stardust::convert(expression, Format::DUST, Format::TRON, 0)?;
Result / 652 B
{:value {:bytes {:#b64 "VFJPThwrLD94AgEAAAAAAAAADhUABwADAAAABAAAAAYAAAAJAAAAHHgCAQAAAAAAAAAPCicAAAApAAAAHHgCKQAAAAAAAAAPCjwAAAA+AAAADhEAAwACAAAAMgAAAEcAAAACAgAAAAAAAAACKgAAAAAAAAAOEQADAAIAAABiAAAAawAAAIwkY29tbWVudBTNT25lIGV4cHJlc3Npb24gYnVpbHQgb25jZSBhbmQgZXZhbHVhdGVkIGFnYWluc3Qgc2V2ZXJhbCBpbnB1dHMuIFRoZQpldmFsdWF0ZSBleGFtcGxlcyBsb2FkIHRoaXMgZmlsZSwgYnVpbGQgYGV4cHJlc3Npb25gLCBldmFsdWF0ZSBpdCB3aXRoIGVhY2gKb2YgYGlucHV0c2AgaW4gb3JkZXIgYW5kIGNvbXBhcmUgZWFjaCByZXN1bHQgd2l0aCBgZXhwZWN0ZWRgLg8KhQAAAI4AAABsaW5wdXRzDwpnAQAAUQAAAIxleHBlY3RlZA8KeAEAAHQAAAAHDgIBAABuAQAAgQEAAAcKAQAAAIsBAACsZXhwcmVzc2lvbg8KowEAABIAAAAHEkAJAABdAQAAmQEAAK4BAAC4AQAAAAAAAA=="}}}

convert

edn_text holds this document:

edn13 B
"{:a 1 :a 2}"
rust149 B
let edn = stardust::parse(edn_text, Format::EDN)?;
let output = edn.read(edn.root()?)?.text;
let result = stardust::parse(output, Format::EDN).err();
Result / 216 B
{:value {:code    "invalid_edn"
         :message "invalid EDN (Duplicate_Key) at offset 6"
         :path    nil
         :field   nil
         :entity  nil
         :offset  6
         :status  "invalid_argument"}}

convert

instant_text holds this document:

edn28 B
#inst "2026-10-07T12:34:56Z"
rust107 B
let instant = stardust::parse(instant_text, Format::EDN)?;
let result = instant.serialize(Format::EDN, 0)?;
Result / 49 B
{:value {:text "#inst \"2026-10-07T12:34:56Z\""}}

convert

source_text holds this document:

edn5 B
[1.0]
rust158 B
let source = stardust::parse(source_text, Format::EDN)?;
let output = source.serialize(Format::TRON, 0)?;
let result = stardust::parse(output, Format::TRON)?;
Result / 12 B
{:value [1]}

convert

source_text holds this document:

edn12 B
{:z 2
 :a 1}
rust158 B
let source = stardust::parse(source_text, Format::EDN)?;
let output = source.serialize(Format::TRON, 0)?;
let result = stardust::parse(output, Format::TRON)?;
Result / 29 B
{:value {: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.