Skip to content

Commit 9f85346

Browse files
authored
Merge pull request GaijinEntertainment#2127 from GaijinEntertainment/language-testing
sprint_json documentation, bugfixes, tests
2 parents a0f5f5d + bc1278d commit 9f85346

9 files changed

Lines changed: 965 additions & 86 deletions

File tree

daslib/json_boost.das

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ def public parse_json_annotation(name : string; annotation : array<tuple<name :
436436
var fieldState : JsonFieldState
437437
fieldState.argName = name
438438
for (ann in annotation) {
439-
if (ann.name == "rename") {
439+
if (ann.name == "rename" && ann.data is tString) {
440440
fieldState.argName = ann.data as tString
441441
} elif (ann.name == "enum_as_int" && ann.data is bool) {
442442
fieldState.enumAsInt = ann.data as tBool

doc/source/reference/tutorials/30_json.rst

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ JSON
99
single: Tutorial; JSON Parsing
1010
single: Tutorial; JSON Serialization
1111
single: Tutorial; json_boost
12+
single: Tutorial; sprint_json
1213

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

199+
sprint_json
200+
===========
201+
202+
``sprint_json`` is a builtin function that serializes any daslang value
203+
directly to a JSON string — no ``JsonValue?`` intermediate. It handles
204+
structs, classes, variants, tuples, tables, arrays, enums, pointers,
205+
and all basic types::
206+
207+
struct Record {
208+
id : int
209+
tag : string
210+
data : Payload
211+
values : array<int>
212+
meta : table<string; int>
213+
coords : tuple<int; float>
214+
ptr : void?
215+
}
216+
217+
var r <- Record(uninitialized
218+
id = 1, tag = "test",
219+
data = Payload(uninitialized code = 42),
220+
values = [1, 2, 3],
221+
meta <- { "x" => 10 },
222+
coords = (7, 3.14),
223+
ptr = null
224+
)
225+
let compact = sprint_json(r, false)
226+
// {"id":1,"tag":"test","data":{"code":42},"values":[1,2,3],...}
227+
228+
let pretty = sprint_json(r, true)
229+
// human-readable with indentation
230+
231+
The second argument controls human-readable formatting. Works with
232+
simple values too::
233+
234+
sprint_json(42, false) // 42
235+
sprint_json("hello", false) // "hello"
236+
sprint_json([10, 20, 30], false) // [10,20,30]
237+
238+
Field annotations
239+
=================
240+
241+
Struct field annotations control how ``sprint_json`` serializes fields.
242+
These require ``options rtti`` to be enabled.
243+
244+
- ``@optional`` — skip the field if it has a default or empty value
245+
- ``@embed`` — embed a string field as raw JSON (no extra quotes)
246+
- ``@unescape`` — don't escape special characters in the string
247+
- ``@enum_as_int`` — serialize an enum as its integer value, not a string
248+
- ``@rename="key"`` — use ``key`` as the JSON field name instead of the daslang field name
249+
250+
::
251+
252+
struct AnnotatedConfig {
253+
name : string
254+
@optional debug : bool // omitted when false
255+
@optional tags : array<string> // omitted when empty
256+
@embed raw_data : string // embedded as raw JSON
257+
@unescape raw_path : string // no escaping of special chars
258+
pri : Priority // serialized as string
259+
@enum_as_int level : Priority // serialized as integer
260+
@rename="type" _type : string // appears as "type" in JSON
261+
}
262+
263+
var c <- AnnotatedConfig(uninitialized
264+
name = "app", debug = false,
265+
raw_data = "[1,2,3]",
266+
raw_path = "C:\\Users\\test",
267+
pri = Priority.high,
268+
level = Priority.medium,
269+
_type = "widget"
270+
)
271+
let json_str = sprint_json(c, false)
272+
// {"name":"app","raw_data":[1,2,3],"raw_path":"C:\Users\test","pri":"high","level":1,"type":"widget"}
273+
274+
In this example: ``debug`` and ``tags`` are omitted (``@optional``),
275+
``raw_data`` is embedded as ``[1,2,3]`` not ``"[1,2,3]"`` (``@embed``),
276+
``raw_path`` keeps backslashes unescaped (``@unescape``), ``level``
277+
is ``1`` instead of ``"medium"`` (``@enum_as_int``), and ``_type``
278+
appears as ``"type"`` in the output (``@rename``).
279+
280+
@rename annotation
281+
==================
282+
283+
Use ``@rename="json_key"`` when the JSON key is a daslang reserved word
284+
or doesn't follow daslang naming conventions. The field keeps a safe name
285+
in code (e.g. ``_type``) but serializes as the desired key. ``@rename``
286+
works with ``sprint_json``, ``JV``, and ``from_JV``::
287+
288+
struct ApiResponse {
289+
@rename="type" _type : string
290+
@rename="class" _class : int
291+
value : float
292+
}
293+
294+
var resp = ApiResponse(_type = "widget", _class = 3, value = 1.5)
295+
sprint_json(resp, false)
296+
// {"type":"widget","class":3,"value":1.5}
297+
298+
// from_JV maps renamed keys back to struct fields
299+
var js = read_json("{\"type\":\"button\",\"class\":5,\"value\":2.0}", error)
300+
var result = from_JV(js, type<ApiResponse>)
301+
// result._type == "button", result._class == 5
302+
303+
Class serialization
304+
===================
305+
306+
Both ``JV``/``from_JV`` and ``sprint_json`` work with classes.
307+
Classes serialize their fields just like structs::
308+
309+
class Animal {
310+
species : string
311+
legs : int
312+
}
313+
314+
var a = new Animal(species = "cat", legs = 4)
315+
let json_str = sprint_json(*a, false)
316+
// {"species":"cat","legs":4}
317+
318+
var js = JV(*a)
319+
print(write_json(js))
320+
// {"legs":4,"species":"cat"}
321+
198322
.. seealso::
199323

200324
Full source: :download:`tutorials/language/30_json.das <../../../../tutorials/language/30_json.das>`

doc/source/stdlib/handmade/module-json_boost.rst

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,59 @@ Example:
3535
// output:
3636
// name = Alice
3737
// age = 30
38+
39+
Field annotations
40+
-----------------
41+
42+
Struct fields can carry annotations that control how ``JV`` / ``from_JV`` and
43+
the builtin ``sprint_json`` serialize and deserialize them. Annotations are
44+
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_>`
45+
into a :ref:`JsonFieldState <struct-json_boost-JsonFieldState>` and stored in a
46+
``static_let`` cache so each field is parsed only once.
47+
48+
``sprint_json`` requires ``options rtti`` for annotations to take effect at
49+
runtime.
50+
51+
.. list-table::
52+
:header-rows: 1
53+
:widths: 20 80
54+
55+
* - Annotation
56+
- Effect
57+
* - ``@optional``
58+
- Skip the field when its value is default / empty (``0``, ``false``,
59+
empty string, empty array, empty table, null pointer).
60+
* - ``@rename="json_key"``
61+
- Use *json_key* instead of the daslang field name in JSON output and
62+
when looking up keys during ``from_JV`` deserialization. The annotation
63+
value must be a string (``@rename="name"``). A bare ``@rename`` with
64+
no string value is silently ignored.
65+
* - ``@embed``
66+
- Treat a ``string`` field as raw JSON — embed it without extra quoting.
67+
During ``JV`` conversion the string is parsed with ``read_json`` and
68+
the resulting sub-tree is inserted directly.
69+
* - ``@unescape``
70+
- Write the string field without escaping special characters
71+
(backslashes, quotes, etc.).
72+
* - ``@enum_as_int``
73+
- Serialize an enum field as its integer value instead of the
74+
enumeration name string.
75+
76+
Example with ``sprint_json``:
77+
78+
.. code-block:: das
79+
80+
options rtti
81+
82+
struct Config {
83+
name : string
84+
@optional debug : bool // omitted when false
85+
@rename="type" _type : string // JSON key is "type"
86+
@embed raw : string // embedded as raw JSON
87+
@unescape path : string // no escaping of backslashes
88+
@enum_as_int level : Priority // integer, not string
89+
}
90+
91+
let json_str = sprint_json(cfg, false)
92+
93+
See :ref:`tutorial_json` for runnable examples of every annotation.

doc/source/stdlib/json_boost.rst

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,62 @@ Example:
4545
// name = Alice
4646
// age = 30
4747
48+
Field annotations
49+
-----------------
50+
51+
Struct fields can carry annotations that control how ``JV`` / ``from_JV`` and
52+
the builtin ``sprint_json`` serialize and deserialize them. Annotations are
53+
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_>`
54+
into a :ref:`JsonFieldState <struct-json_boost-JsonFieldState>` and stored in a
55+
``static_let`` cache so each field is parsed only once.
56+
57+
``sprint_json`` requires ``options rtti`` for annotations to take effect at
58+
runtime.
59+
60+
.. list-table::
61+
:header-rows: 1
62+
:widths: 20 80
63+
64+
* - Annotation
65+
- Effect
66+
* - ``@optional``
67+
- Skip the field when its value is default / empty (``0``, ``false``,
68+
empty string, empty array, empty table, null pointer).
69+
* - ``@rename="json_key"``
70+
- Use *json_key* instead of the daslang field name in JSON output and
71+
when looking up keys during ``from_JV`` deserialization. The annotation
72+
value must be a string (``@rename="name"``). A bare ``@rename`` with
73+
no string value is silently ignored.
74+
* - ``@embed``
75+
- Treat a ``string`` field as raw JSON — embed it without extra quoting.
76+
During ``JV`` conversion the string is parsed with ``read_json`` and
77+
the resulting sub-tree is inserted directly.
78+
* - ``@unescape``
79+
- Write the string field without escaping special characters
80+
(backslashes, quotes, etc.).
81+
* - ``@enum_as_int``
82+
- Serialize an enum field as its integer value instead of the
83+
enumeration name string.
84+
85+
Example with ``sprint_json``:
86+
87+
.. code-block:: das
88+
89+
options rtti
90+
91+
struct Config {
92+
name : string
93+
@optional debug : bool // omitted when false
94+
@rename="type" _type : string // JSON key is "type"
95+
@embed raw : string // embedded as raw JSON
96+
@unescape path : string // no escaping of backslashes
97+
@enum_as_int level : Priority // integer, not string
98+
}
99+
100+
let json_str = sprint_json(cfg, false)
101+
102+
See :ref:`tutorial_json` for runnable examples of every annotation.
103+
48104

49105

50106
++++++++++
File renamed without changes.

examples/test/CMakeLists.txt

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,6 @@ file(GLOB RUNTIME_ERRORS_SRC
4242
list(SORT RUNTIME_ERRORS_SRC)
4343
SOURCE_GROUP_FILES("runtime_errors" RUNTIME_ERRORS_SRC)
4444

45-
file(GLOB MIX_TEST_SRC
46-
"${CMAKE_CURRENT_SOURCE_DIR}/examples/test/misc/*.das"
47-
)
48-
list(SORT MIX_TEST_SRC)
49-
SOURCE_GROUP_FILES("mix" MIX_TEST_SRC)
50-
5145
file(GLOB_RECURSE MODULE_TEST_SRC
5246
"${CMAKE_CURRENT_SOURCE_DIR}/examples/test/module/*.*"
5347
)
@@ -68,7 +62,7 @@ UNITIZE_BUILD("examples/test/unit_tests" TEST_GENERATED_SRC)
6862
#DAS_AOT("hello_world.das" TEST_GENERATED_SRC daScriptTestAot daslang)
6963
SOURCE_GROUP_FILES("generated" TEST_GENERATED_SRC)
7064

71-
add_executable(daScriptTest ${TEST_GENERATED_SRC} ${UNIT_TEST_SRC} ${COMPILATION_FAIL_TEST_SRC} ${MIX_TEST_SRC} ${MODULE_TEST_SRC}
65+
add_executable(daScriptTest ${TEST_GENERATED_SRC} ${UNIT_TEST_SRC} ${COMPILATION_FAIL_TEST_SRC} ${MODULE_TEST_SRC}
7266
${TEST_MAIN_SRC} ${OPTIMIZATION_SRC} ${RUNTIME_ERRORS_SRC})
7367
TARGET_LINK_LIBRARIES(daScriptTest libDaScriptTest libDaScriptAot Threads::Threads ${DAS_MODULES_LIBS})
7468
TARGET_INCLUDE_DIRECTORIES(daScriptTest PRIVATE ${PROJECT_SOURCE_DIR}/examples/test)

examples/test/misc/sprint_json.das

Lines changed: 0 additions & 77 deletions
This file was deleted.

0 commit comments

Comments
 (0)