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"
  }
}
c479 B
stardust_document *people = NULL;
stardust_text *output = NULL;
stardust_document *result = NULL;
uint32_t status = stardust_parse(people_text, STARDUST_JSON, &people);
if (status == STARDUST_OK)
    status = stardust_serialize(people, STARDUST_JSON, 0, &output);
if (status == STARDUST_OK)
    status = stardust_parse(stardust_text_data(output), STARDUST_JSON, &result);
stardust_text_release(output);
stardust_document_release(people);
if (status != STARDUST_OK) return status;
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
stardust_serializeStatus. text through outputdocument.serialize(format, width) writes text, or binary bytes for TRON.
convertUnavailableconvert(text, from, to, width) calls parse and serialize, then releases the intermediate document.

stardust_serialize

c118 B
uint32_t stardust_serialize(const stardust_document *doc, uint32_t format, int64_t max_width, stardust_text **output);
c63 B
stardust_bytes stardust_text_data(const stardust_text *result);
c92 B
uint32_t stardust_text_error(const stardust_text *result, stardust_diagnostic_view *output);
c50 B
void stardust_text_release(stardust_text *result);

Parameters

stardust_serialize

ParameterTypeOwnershipDefaultMeaning
docconst stardust_document *Borrowed handleRequiredKeep the handle alive until the call returns.
formatuint32_tValueRequiredThe codec to use for this operation.
max_widthint64_tValueRequiredValue passed to the call.
outputstardust_text **Owned outputRequiredReceives a library-owned handle. Release it after use.

stardust_text_data

ParameterTypeOwnershipDefaultMeaning
resultconst stardust_text *Borrowed handleNULLKeep the handle alive until the call returns.

stardust_text_error

ParameterTypeOwnershipDefaultMeaning
resultconst stardust_text *Borrowed handleRequiredKeep the handle alive until the call returns.
outputstardust_diagnostic_view *Caller-owned outputRequiredReceives a record. Its pointer fields follow the record's ownership contract.

stardust_text_release

ParameterTypeOwnershipDefaultMeaning
resultstardust_text *Consumed handleNULLEnds ownership. Do not use the handle after this call.

Errors

StatusWhen
STARDUST_INVALID_ARGUMENTthe format is unknown or the width is negative
STARDUST_OUT_OF_MEMORYnative memory is exhausted

Examples

document/serialize

expression_text holds this document:

json23 B
[
  "+",
  "?x",
  1
]
c339 B
stardust_document *source = NULL;
stardust_text *result = NULL;
uint32_t status = stardust_parse(expression_text, STARDUST_JSON, &source);
if (status == STARDUST_OK) status = stardust_serialize(source, STARDUST_JSON, 0, &result);
stardust_document_release(source);
if (status != STARDUST_OK) {stardust_text_release(result); return status;}
Result / 32 B
{
  "text": "[\"+\",\"?x\",1]"
}

document/serialize

number_text holds this document:

json2 B
1
c335 B
stardust_document *source = NULL;
stardust_text *result = NULL;
uint32_t status = stardust_parse(number_text, STARDUST_JSON, &source);
if (status == STARDUST_OK) status = stardust_serialize(source, STARDUST_TRON, 0, &result);
stardust_document_release(source);
if (status != STARDUST_OK) {stardust_text_release(result); return status;}
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"
c333 B
stardust_document *source = NULL;
stardust_text *result = NULL;
uint32_t status = stardust_parse(name_text, STARDUST_JSON, &source);
if (status == STARDUST_OK) status = stardust_serialize(source, STARDUST_TRON, 0, &result);
stardust_document_release(source);
if (status != STARDUST_OK) {stardust_text_release(result); return status;}
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
]
c339 B
stardust_document *source = NULL;
stardust_text *result = NULL;
uint32_t status = stardust_parse(expression_text, STARDUST_JSON, &source);
if (status == STARDUST_OK) status = stardust_serialize(source, STARDUST_TRON, 0, &result);
stardust_document_release(source);
if (status != STARDUST_OK) {stardust_text_release(result); return status;}
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
]
c339 B
stardust_document *source = NULL;
stardust_text *result = NULL;
uint32_t status = stardust_parse(expression_text, STARDUST_JSON, &source);
if (status == STARDUST_OK) status = stardust_serialize(source, STARDUST_JSON, 0, &result);
stardust_document_release(source);
if (status != STARDUST_OK) {stardust_text_release(result); return status;}
Result / 32 B
{
  "text": "[\"+\",\"?x\",1]"
}

Also calls: stardust_text_data, stardust_text_error, stardust_text_release

convert

Compose stardust_parse and stardust_serialize.

Examples

convert

duplicate_text holds this document:

json14 B
"{:a 1 :a 2}"
c640 B
stardust_document *source = NULL;
uint32_t root = 0;
stardust_node_view view = {0};
int64_t written = 0;
uint32_t status = stardust_parse(duplicate_text, STARDUST_JSON, &source);
if (status == STARDUST_OK) status = stardust_document_root(source, &root);
if (status == STARDUST_OK) status = stardust_document_read_many(source, root, 1, &view, &written);
if (status != STARDUST_OK) {stardust_document_release(source); return status;}
stardust_document *failed = NULL;
status = stardust_parse(view.text, STARDUST_EDN, &failed);
stardust_document_release(source);
stardust_diagnostic_view result = {0};
stardust_document_error(failed, &result);
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"
}
c335 B
stardust_document *source = NULL;
stardust_text *result = NULL;
uint32_t status = stardust_parse(instant_text, STARDUST_JSON, &source);
if (status == STARDUST_OK) status = stardust_serialize(source, STARDUST_EDN, 0, &result);
stardust_document_release(source);
if (status != STARDUST_OK) {stardust_text_release(result); return status;}
Result / 46 B
{
  "text": "#inst \"2026-10-07T12:34:56Z\""
}

convert

container_text holds this document:

json10 B
[
  1.0
]
c511 B
stardust_document *source = NULL;
stardust_text *output = NULL;
stardust_document *result = NULL;
uint32_t status = stardust_parse(container_text, STARDUST_JSON, &source);
if (status == STARDUST_OK) status = stardust_serialize(source, STARDUST_TRON, 0, &output);
if (status == STARDUST_OK) status = stardust_parse(stardust_text_data(output), STARDUST_TRON, &result);
stardust_text_release(output);
stardust_document_release(source);
if (status != STARDUST_OK) {stardust_document_release(result); return status;}
Result / 7 B
[
  1
]

convert

order_text holds this document:

json23 B
{
  "z": 2,
  "a": 1
}
c507 B
stardust_document *source = NULL;
stardust_text *output = NULL;
stardust_document *result = NULL;
uint32_t status = stardust_parse(order_text, STARDUST_JSON, &source);
if (status == STARDUST_OK) status = stardust_serialize(source, STARDUST_TRON, 0, &output);
if (status == STARDUST_OK) status = stardust_parse(stardust_text_data(output), STARDUST_TRON, &result);
stardust_text_release(output);
stardust_document_release(source);
if (status != STARDUST_OK) {stardust_document_release(result); return status;}
Result / 22 B
{
  "a": 1,
  "z": 2
}

Also calls: 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.