Skip to content

jsonunmarshal

Reads JSON text into a declared user-defined type or a one-dimensional typed array.

The output declaration supplies the expected names and types. A JSON object maps to a user-defined type (UDT); a JSON array maps to a typed array. Nested structs and arrays of structs can describe presets, scores and other structured data.

Syntax

value:Type = jsonunmarshal(Sjson [, iflags [, imaxdepth]])
values:T[] = jsonunmarshal(Sjson [, iflags [, imaxdepth]])
value:Type jsonunmarshal Sjson [, iflags [, imaxdepth]]
values:T[] jsonunmarshal Sjson [, iflags [, imaxdepth]]

Type stands for a type declared with struct. T stands for i, k, b, B, S, or a supported UDT. These are placeholders, not literal type names.

Initialization

Sjson is the JSON text. It must contain one complete object or array. Use {{ ... }} for an inline Csound string containing JSON double quotes. This opcode does not treat its input as a filename; use jsonunmarshalfile to read a file.

value or values receives the decoded data. Declare the destination type explicitly for a UDT or when the variable name does not imply the type. The opcode allocates arrays to match the input length, including zero for []. A successful call replaces the whole value; it does not merge fields into an existing preset.

iflags is an optional integer. Its default is 0.

Value Accepted input
0 Strict JSON
1 JSON plus // line comments and /* ... */ block comments
2 JSON plus trailing commas in objects and arrays
3 Both comments and trailing commas

The flags do not allow single quotes, unquoted keys, NaN, infinity, or missing fields. Comments inside quoted strings remain text. A .jsonc filename does not set flags for you.

imaxdepth is an optional integer from 0 to 256. The default 0 selects a limit of 256 nested objects and arrays. Values 1 through 256 set the limit directly. The root counts as one container; scalar members add no level. For example, {"notes":[{"pitch":60}]} needs a limit of at least 3. To set only the depth, supply 0 for iflags first. Negative, fractional, non-finite or out-of-range options cause an initialization error.

Types and errors

Csound member or array element Required JSON value
i, k A finite number in the build's numeric range
b, B true or false
S A valid UTF-8 string without NUL characters
A supported UDT An object with exactly its declared members
A one-dimensional typed array member An array of values of its declared element type

Every field must occur exactly once. Names are case-sensitive and may appear in any order. Missing, unknown or duplicate fields, null, and wrong value types cause an initialization error. The opcode does not convert numeric strings to numbers or numbers to booleans. All array elements must match the declared type. Audio and spectral values, live handles, and multidimensional arrays are unsupported. Root numbers, booleans, strings and null are also unsupported; place a scalar in a struct or array.

A failed read leaves the destination unchanged instead of storing part of the input. It still raises an initialization error: there is no success flag, default-value option, or mode that skips bad entries. Syntax errors give a byte position; mapping errors give a field or element path where possible, such as $.notes[2].pitch. The depth limit applies to the complete input, including unknown fields.

See JSON data: type mappings and fault handling for numeric precision, strings, nested arrays, diagnostics and application-level checks.

Performance

The opcode runs only at initialization. It initializes k and B data once; later changes to the input string do not trigger another read. Parsing and allocation are not safe for a real-time audio callback. Load data before live playback or while rendering offline. Starting a new instrument during live playback still runs its initialization in the audio processing path.

Examples

jsonunmarshal.csd covers every supported scalar type, all supported root array element types, empty arrays, nested UDTs, arrays of UDTs and a write/read round trip.

Read typed JSON and write it back
<CsoundSynthesizer>
<CsOptions>
-n -d -m0
</CsOptions>
<CsInstruments>
sr = 48000
ksmps = 32
nchnls = 1
0dbfs = 1

struct Envelope attack:i, release:i
struct Note pitch:i, label:S
struct Preset name:S, gain:i, cutoff:k, enabled:b, bypass:B, envelope:Envelope, notes:Note[], levels:i[], controls:k[], labels:S[], switches:b[], gates:B[]
struct Row values:i[]

instr 1
  ; Member names, including their case, are the JSON keys.
  source:S = {{
    {
      "name": "Étude // this is text",
      "gain": 0.2,
      "cutoff": 1200,
      "enabled": true,
      "bypass": false,
      "envelope": {"attack": 0.01, "release": 0.2},
      "notes": [{"pitch": 60, "label": "C4"}, {"pitch": 67, "label": "G4"}],
      "levels": [0.25, 0.5, 1],
      "controls": [800, 1200],
      "labels": ["soft", "bright"],
      "switches": [true, false],
      "gates": [false, true]
    }
  }}
  preset:Preset = jsonunmarshal(source)
  prints("%s: %d notes, attack %.2f seconds\n", \
         preset.name, lenarray(preset.notes), preset.envelope.attack)

  ; Writing reads k and B members once, during initialization.
  encoded:S = jsonmarshal(preset, 1)
  prints("%s\n", encoded)
  restored:Preset = jsonunmarshal(encoded)
  prints("Restored last note: %s\n", restored.notes[1].label)

  ; Each supported scalar type can also be the element type of a root array.
  numbers:i[] = jsonunmarshal("[1, 2.5, -3]")
  controls:k[] = jsonunmarshal("[440, 660]")
  names:S[] = jsonunmarshal({{ ["one", "two"] }})
  switches:b[] = jsonunmarshal("[true, false]")
  gates:B[] = jsonunmarshal("[false, true]")
  notes:Note[] = jsonunmarshal({{ [{"pitch":72,"label":"C5"}] }})
  empty:i[] = jsonunmarshal("[]")
  prints("Root arrays: %s | %s | %s | %s | %s | %s | %s\n", \
         jsonmarshal(numbers), jsonmarshal(controls), jsonmarshal(names), \
         jsonmarshal(switches), jsonmarshal(gates), jsonmarshal(notes), \
         jsonmarshal(empty))

  ; Use structs containing arrays for rows of different lengths.
  rows:Row[] = jsonunmarshal({{ [{"values":[1,2]},{"values":[3]}] }})
  prints("Rows: %s\n", jsonmarshal(rows))
endin
</CsInstruments>
<CsScore>
i 1 0 0.1
e
</CsScore>
</CsoundSynthesizer>

The output includes Restored last note: G4. The Rows value shows how to represent rows of different lengths with an array of structs containing arrays.

jsonunmarshal-options.csd reads comments and trailing commas, then writes strict JSON and reads it with default flags.

Read JSON with comments and trailing commas
<CsoundSynthesizer>
<CsOptions>
-n -d -m0
</CsOptions>
<CsInstruments>
sr = 48000
ksmps = 32
nchnls = 1
0dbfs = 1

struct Settings gain:i, name:S

instr 1
  source:S = {{
    {
      // Comments help when editing a preset by hand.
      "name": "soft // literal text",
      "gain": /* linear amplitude */ 0.2,
    }
  }}
  ; 1 allows comments, 2 allows trailing commas; 3 allows both.
  ; The root object is the only container, so a depth of 1 is enough.
  settings:Settings = jsonunmarshal(source, 3, 1)
  strict:S = jsonmarshal(settings)
  prints("Strict JSON: %s\n", strict)
  again:Settings = jsonunmarshal(strict)
  prints("Read back: gain = %.1f, name = %s\n", again.gain, again.name)
endin
</CsInstruments>
<CsScore>
i 1 0 0.1
e
</CsScore>
</CsoundSynthesizer>

Its compact output is:

{"gain":0.2,"name":"soft // literal text"}

See also

jsonunmarshalfile, jsonmarshal, JSON data, User-defined types, init, String Conversion Opcodes

Availability

New in Csound 7.