Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion daslib/json_boost.das
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ def public parse_json_annotation(name : string; annotation : array<tuple<name :
var fieldState : JsonFieldState
fieldState.argName = name
for (ann in annotation) {
if (ann.name == "rename") {
if (ann.name == "rename" && ann.data is tString) {
fieldState.argName = ann.data as tString
} elif (ann.name == "enum_as_int" && ann.data is bool) {
fieldState.enumAsInt = ann.data as tBool
Expand Down
124 changes: 124 additions & 0 deletions doc/source/reference/tutorials/30_json.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ JSON
single: Tutorial; JSON Parsing
single: Tutorial; JSON Serialization
single: Tutorial; json_boost
single: Tutorial; sprint_json

This tutorial covers ``daslib/json`` and ``daslib/json_boost`` — parsing,
building, writing, and querying JSON data in daslang.
Expand Down Expand Up @@ -195,6 +196,129 @@ Broken JSON repair
let fixed = try_fixing_broken_json(bad)
var js = read_json(fixed, error)

sprint_json
===========

``sprint_json`` is a builtin function that serializes any daslang value
directly to a JSON string — no ``JsonValue?`` intermediate. It handles
structs, classes, variants, tuples, tables, arrays, enums, pointers,
and all basic types::

struct Record {
id : int
tag : string
data : Payload
values : array<int>
meta : table<string; int>
coords : tuple<int; float>
ptr : void?
}

var r <- Record(uninitialized
id = 1, tag = "test",
data = Payload(uninitialized code = 42),
values = [1, 2, 3],
meta <- { "x" => 10 },
coords = (7, 3.14),
ptr = null
)
let compact = sprint_json(r, false)
// {"id":1,"tag":"test","data":{"code":42},"values":[1,2,3],...}

let pretty = sprint_json(r, true)
// human-readable with indentation

The second argument controls human-readable formatting. Works with
simple values too::

sprint_json(42, false) // 42
sprint_json("hello", false) // "hello"
sprint_json([10, 20, 30], false) // [10,20,30]

Field annotations
=================

Struct field annotations control how ``sprint_json`` serializes fields.
These require ``options rtti`` to be enabled.

- ``@optional`` — skip the field if it has a default or empty value
- ``@embed`` — embed a string field as raw JSON (no extra quotes)
- ``@unescape`` — don't escape special characters in the string
- ``@enum_as_int`` — serialize an enum as its integer value, not a string
- ``@rename="key"`` — use ``key`` as the JSON field name instead of the daslang field name

::

struct AnnotatedConfig {
name : string
@optional debug : bool // omitted when false
@optional tags : array<string> // omitted when empty
@embed raw_data : string // embedded as raw JSON
@unescape raw_path : string // no escaping of special chars
pri : Priority // serialized as string
@enum_as_int level : Priority // serialized as integer
@rename="type" _type : string // appears as "type" in JSON
}

var c <- AnnotatedConfig(uninitialized
name = "app", debug = false,
raw_data = "[1,2,3]",
raw_path = "C:\\Users\\test",
pri = Priority.high,
level = Priority.medium,
_type = "widget"
)
let json_str = sprint_json(c, false)
// {"name":"app","raw_data":[1,2,3],"raw_path":"C:\Users\test","pri":"high","level":1,"type":"widget"}

In this example: ``debug`` and ``tags`` are omitted (``@optional``),
``raw_data`` is embedded as ``[1,2,3]`` not ``"[1,2,3]"`` (``@embed``),
``raw_path`` keeps backslashes unescaped (``@unescape``), ``level``
is ``1`` instead of ``"medium"`` (``@enum_as_int``), and ``_type``
appears as ``"type"`` in the output (``@rename``).

@rename annotation
==================

Use ``@rename="json_key"`` when the JSON key is a daslang reserved word
or doesn't follow daslang naming conventions. The field keeps a safe name
in code (e.g. ``_type``) but serializes as the desired key. ``@rename``
works with ``sprint_json``, ``JV``, and ``from_JV``::

struct ApiResponse {
@rename="type" _type : string
@rename="class" _class : int
value : float
}

var resp = ApiResponse(_type = "widget", _class = 3, value = 1.5)
sprint_json(resp, false)
// {"type":"widget","class":3,"value":1.5}

// from_JV maps renamed keys back to struct fields
var js = read_json("{\"type\":\"button\",\"class\":5,\"value\":2.0}", error)
var result = from_JV(js, type<ApiResponse>)
// result._type == "button", result._class == 5

Class serialization
===================

Both ``JV``/``from_JV`` and ``sprint_json`` work with classes.
Classes serialize their fields just like structs::

class Animal {
species : string
legs : int
}

var a = new Animal(species = "cat", legs = 4)
let json_str = sprint_json(*a, false)
// {"species":"cat","legs":4}

var js = JV(*a)
print(write_json(js))
// {"legs":4,"species":"cat"}

.. seealso::

Full source: :download:`tutorials/language/30_json.das <../../../../tutorials/language/30_json.das>`
Expand Down
56 changes: 56 additions & 0 deletions doc/source/stdlib/handmade/module-json_boost.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,59 @@ Example:
// output:
// name = Alice
// age = 30
Field annotations
-----------------

Struct fields can carry annotations that control how ``JV`` / ``from_JV`` and
the builtin ``sprint_json`` serialize and deserialize them. Annotations are
parsed by :ref:`parse_json_annotation <function-json_boost_parse_json_annotation_string_array_ls_tuple_ls_name_c_string;data_c_variant_ls_tBool_c_bool;tInt_c_int;tUInt_c_uint;tInt64_c_int64;tUInt64_c_uint64;tFloat_c_float;tDouble_c_double;tString_c_string;nothing_c_any_gr__gr__gr_>`
into a :ref:`JsonFieldState <struct-json_boost-JsonFieldState>` and stored in a
``static_let`` cache so each field is parsed only once.

``sprint_json`` requires ``options rtti`` for annotations to take effect at
runtime.

.. list-table::
:header-rows: 1
:widths: 20 80

* - Annotation
- Effect
* - ``@optional``
- Skip the field when its value is default / empty (``0``, ``false``,
empty string, empty array, empty table, null pointer).
* - ``@rename="json_key"``
- Use *json_key* instead of the daslang field name in JSON output and
when looking up keys during ``from_JV`` deserialization. The annotation
value must be a string (``@rename="name"``). A bare ``@rename`` with
no string value is silently ignored.
* - ``@embed``
- Treat a ``string`` field as raw JSON — embed it without extra quoting.
During ``JV`` conversion the string is parsed with ``read_json`` and
the resulting sub-tree is inserted directly.
* - ``@unescape``
- Write the string field without escaping special characters
(backslashes, quotes, etc.).
* - ``@enum_as_int``
- Serialize an enum field as its integer value instead of the
enumeration name string.

Example with ``sprint_json``:

.. code-block:: das
options rtti
struct Config {
name : string
@optional debug : bool // omitted when false
@rename="type" _type : string // JSON key is "type"
@embed raw : string // embedded as raw JSON
@unescape path : string // no escaping of backslashes
@enum_as_int level : Priority // integer, not string
}
let json_str = sprint_json(cfg, false)
See :ref:`tutorial_json` for runnable examples of every annotation.
56 changes: 56 additions & 0 deletions doc/source/stdlib/json_boost.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,62 @@ Example:
// name = Alice
// age = 30
Field annotations
-----------------

Struct fields can carry annotations that control how ``JV`` / ``from_JV`` and
the builtin ``sprint_json`` serialize and deserialize them. Annotations are
parsed by :ref:`parse_json_annotation <function-json_boost_parse_json_annotation_string_array_ls_tuple_ls_name_c_string;data_c_variant_ls_tBool_c_bool;tInt_c_int;tUInt_c_uint;tInt64_c_int64;tUInt64_c_uint64;tFloat_c_float;tDouble_c_double;tString_c_string;nothing_c_any_gr__gr__gr_>`
into a :ref:`JsonFieldState <struct-json_boost-JsonFieldState>` and stored in a
``static_let`` cache so each field is parsed only once.

``sprint_json`` requires ``options rtti`` for annotations to take effect at
runtime.

.. list-table::
:header-rows: 1
:widths: 20 80

* - Annotation
- Effect
* - ``@optional``
- Skip the field when its value is default / empty (``0``, ``false``,
empty string, empty array, empty table, null pointer).
* - ``@rename="json_key"``
- Use *json_key* instead of the daslang field name in JSON output and
when looking up keys during ``from_JV`` deserialization. The annotation
value must be a string (``@rename="name"``). A bare ``@rename`` with
no string value is silently ignored.
* - ``@embed``
- Treat a ``string`` field as raw JSON — embed it without extra quoting.
During ``JV`` conversion the string is parsed with ``read_json`` and
the resulting sub-tree is inserted directly.
* - ``@unescape``
- Write the string field without escaping special characters
(backslashes, quotes, etc.).
* - ``@enum_as_int``
- Serialize an enum field as its integer value instead of the
enumeration name string.

Example with ``sprint_json``:

.. code-block:: das
options rtti
struct Config {
name : string
@optional debug : bool // omitted when false
@rename="type" _type : string // JSON key is "type"
@embed raw : string // embedded as raw JSON
@unescape path : string // no escaping of backslashes
@enum_as_int level : Priority // integer, not string
}
let json_str = sprint_json(cfg, false)
See :ref:`tutorial_json` for runnable examples of every annotation.



++++++++++
Expand Down
8 changes: 1 addition & 7 deletions examples/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,6 @@ file(GLOB RUNTIME_ERRORS_SRC
list(SORT RUNTIME_ERRORS_SRC)
SOURCE_GROUP_FILES("runtime_errors" RUNTIME_ERRORS_SRC)

file(GLOB MIX_TEST_SRC
"${CMAKE_CURRENT_SOURCE_DIR}/examples/test/misc/*.das"
)
list(SORT MIX_TEST_SRC)
SOURCE_GROUP_FILES("mix" MIX_TEST_SRC)

file(GLOB_RECURSE MODULE_TEST_SRC
"${CMAKE_CURRENT_SOURCE_DIR}/examples/test/module/*.*"
)
Expand All @@ -68,7 +62,7 @@ UNITIZE_BUILD("examples/test/unit_tests" TEST_GENERATED_SRC)
#DAS_AOT("hello_world.das" TEST_GENERATED_SRC daScriptTestAot daslang)
SOURCE_GROUP_FILES("generated" TEST_GENERATED_SRC)

add_executable(daScriptTest ${TEST_GENERATED_SRC} ${UNIT_TEST_SRC} ${COMPILATION_FAIL_TEST_SRC} ${MIX_TEST_SRC} ${MODULE_TEST_SRC}
add_executable(daScriptTest ${TEST_GENERATED_SRC} ${UNIT_TEST_SRC} ${COMPILATION_FAIL_TEST_SRC} ${MODULE_TEST_SRC}
${TEST_MAIN_SRC} ${OPTIMIZATION_SRC} ${RUNTIME_ERRORS_SRC})
TARGET_LINK_LIBRARIES(daScriptTest libDaScriptTest libDaScriptAot Threads::Threads ${DAS_MODULES_LIBS})
TARGET_INCLUDE_DIRECTORIES(daScriptTest PRIVATE ${PROJECT_SOURCE_DIR}/examples/test)
Expand Down
77 changes: 0 additions & 77 deletions examples/test/misc/sprint_json.das

This file was deleted.

Loading