JSON parsing and generation for PTC-Lisp.
Implements (json/parse-string s), (json/parse-lines s), and
(json/generate-string v) — Cheshire-shaped builtins.
The two directions signal differently, following spec rule 4 (properties of input data may signal; properties of the program raise):
- Parse helpers take external text and return
nilon failure rather than raising (DIV-23) — bad input is a recoverable condition. generate-stringis handed a value the program itself built, so a value with no JSON encoding is a program fault. It returns a string or raises a namedtype_errorlocating the offending position (DIV-24). It never returnsnil; a silentnilis indistinguishable from an empty encoding oncestrconcatenates it (#1165).
Generated JSON objects use string keys: keyword keys encode as their name and integer keys as their decimal text. Keyword values, sets, tuples, and non-finite floats have no JSON encoding and are refused.
Summary
Functions
Encode an Elixir value as a JSON string.
Parse line-delimited JSON into a list.
Parse a JSON string into an Elixir value.
Functions
Encode an Elixir value as a JSON string.
Returns the encoded string, or raises a type_error naming the position
that has no JSON encoding. It never returns nil — see the module doc.
A conversion walk runs before Jason.encode/1, because Jason would
otherwise coerce non-boolean atoms (e.g. the PTC-Lisp keyword :fs) into
JSON strings on its own, eroding the wire-boundary type signal.
Keys become JSON strings: a string key is used as-is, an integer key
becomes its decimal text, and a keyword key becomes its name verbatim
— no hyphen-to-underscore normalization, so :max-turns encodes as
"max-turns" and never silently renames a key. Two keys that would encode
to the same JSON key are refused rather than emitted as a duplicate.
Examples
iex> PtcRunner.Lisp.Runtime.Json.generate_string(nil)
"null"
iex> PtcRunner.Lisp.Runtime.Json.generate_string([1, 2, 3])
"[1,2,3]"
iex> PtcRunner.Lisp.Runtime.Json.generate_string("hello")
"\"hello\""
iex> PtcRunner.Lisp.Runtime.Json.generate_string(%{:server => "fs"})
"{\"server\":\"fs\"}"
iex> PtcRunner.Lisp.Runtime.Json.generate_string(%{1 => "a"})
"{\"1\":\"a\"}"
iex> PtcRunner.Lisp.Runtime.Json.generate_string(%{"server" => :fs})
** (RuntimeError) json/generate-string: cannot encode a keyword as a JSON value at ["server"]. JSON has no keyword type — convert it first with (name x) or (str x).
Parse line-delimited JSON into a list.
Blank or whitespace-only lines are skipped. Each remaining line is
parsed with parse_string/1, so malformed lines and valid JSON
literal null lines both produce nil.
Examples
iex> PtcRunner.Lisp.Runtime.Json.parse_lines("{\"a\":1}\n[2,3]\n")
[%{"a" => 1}, [2, 3]]
iex> PtcRunner.Lisp.Runtime.Json.parse_lines("null\nnot json\n")
[nil, nil]
iex> PtcRunner.Lisp.Runtime.Json.parse_lines(" \n{\"ok\":true}\n")
[%{"ok" => true}]
iex> PtcRunner.Lisp.Runtime.Json.parse_lines(42)
nil
Parse a JSON string into an Elixir value.
Returns the parsed value on success; nil on any failure
(invalid JSON, non-binary input, nil input). Map keys are
decoded as strings (no atom keys) to avoid atom memory
leaks on untrusted input.
Examples
iex> PtcRunner.Lisp.Runtime.Json.parse_string(~S|{"a": 1, "b": [2, 3]}|)
%{"a" => 1, "b" => [2, 3]}
iex> PtcRunner.Lisp.Runtime.Json.parse_string("[1, 2, 3]")
[1, 2, 3]
iex> PtcRunner.Lisp.Runtime.Json.parse_string("null")
nil
iex> PtcRunner.Lisp.Runtime.Json.parse_string("not json")
nil
iex> PtcRunner.Lisp.Runtime.Json.parse_string(nil)
nil
iex> PtcRunner.Lisp.Runtime.Json.parse_string(42)
nil