├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE-APACHEv2 ├── LICENSE-MIT ├── README.md ├── config.nims ├── fuzzer ├── fuzz_lexer.nim ├── fuzz_parser.nim └── readme.md ├── json_serialization.nim ├── json_serialization.nimble ├── json_serialization ├── format.nim ├── lexer.nim ├── parser.nim ├── reader.nim ├── reader_desc.nim ├── reader_impl.nim ├── std │ ├── net.nim │ ├── options.nim │ ├── sets.nim │ └── tables.nim ├── stew │ └── results.nim ├── types.nim ├── value_ops.nim └── writer.nim └── tests ├── cases └── comments.json ├── nim.cfg ├── test_all.nim ├── test_json_flavor.nim ├── test_lexer.nim ├── test_line_col.nim ├── test_parser.nim ├── test_reader.nim ├── test_serialization.nim ├── test_spec.nim ├── test_valueref.nim ├── test_vectors ├── LICENSE ├── README.md ├── test_parsing │ ├── i_number_double_huge_neg_exp.json │ ├── i_number_huge_exp.json │ ├── i_number_neg_int_huge_exp.json │ ├── i_number_pos_double_huge_exp.json │ ├── i_number_real_neg_overflow.json │ ├── i_number_real_pos_overflow.json │ ├── i_number_real_underflow.json │ ├── i_number_too_big_neg_int.json │ ├── i_number_too_big_pos_int.json │ ├── i_number_very_big_negative_int.json │ ├── i_object_key_lone_2nd_surrogate.json │ ├── i_string_1st_surrogate_but_2nd_missing.json │ ├── i_string_1st_valid_surrogate_2nd_invalid.json │ ├── i_string_UTF-16LE_with_BOM.json │ ├── i_string_UTF-8_invalid_sequence.json │ ├── i_string_UTF8_surrogate_U+D800.json │ ├── i_string_incomplete_surrogate_and_escape_valid.json │ ├── i_string_incomplete_surrogate_pair.json │ ├── i_string_incomplete_surrogates_escape_valid.json │ ├── i_string_invalid_lonely_surrogate.json │ ├── i_string_invalid_surrogate.json │ ├── i_string_invalid_utf-8.json │ ├── i_string_inverted_surrogates_U+1D11E.json │ ├── i_string_iso_latin_1.json │ ├── i_string_lone_second_surrogate.json │ ├── i_string_lone_utf8_continuation_byte.json │ ├── i_string_not_in_unicode_range.json │ ├── i_string_overlong_sequence_2_bytes.json │ ├── i_string_overlong_sequence_6_bytes.json │ ├── i_string_overlong_sequence_6_bytes_null.json │ ├── i_string_truncated-utf-8.json │ ├── i_string_utf16BE_no_BOM.json │ ├── i_string_utf16LE_no_BOM.json │ ├── i_structure_500_nested_arrays.json │ ├── i_structure_UTF-8_BOM_empty_object.json │ ├── n_array_1_true_without_comma.json │ ├── n_array_a_invalid_utf8.json │ ├── n_array_colon_instead_of_comma.json │ ├── n_array_comma_after_close.json │ ├── n_array_comma_and_number.json │ ├── n_array_double_comma.json │ ├── n_array_double_extra_comma.json │ ├── n_array_extra_close.json │ ├── n_array_extra_comma.json │ ├── n_array_incomplete.json │ ├── n_array_incomplete_invalid_value.json │ ├── n_array_inner_array_no_comma.json │ ├── n_array_invalid_utf8.json │ ├── n_array_items_separated_by_semicolon.json │ ├── n_array_just_comma.json │ ├── n_array_just_minus.json │ ├── n_array_missing_value.json │ ├── n_array_newlines_unclosed.json │ ├── n_array_number_and_comma.json │ ├── n_array_number_and_several_commas.json │ ├── n_array_spaces_vertical_tab_formfeed.json │ ├── n_array_star_inside.json │ ├── n_array_unclosed.json │ ├── n_array_unclosed_trailing_comma.json │ ├── n_array_unclosed_with_new_lines.json │ ├── n_array_unclosed_with_object_inside.json │ ├── n_incomplete_false.json │ ├── n_incomplete_null.json │ ├── n_incomplete_true.json │ ├── n_multidigit_number_then_00.json │ ├── n_number_++.json │ ├── n_number_+1.json │ ├── n_number_+Inf.json │ ├── n_number_-01.json │ ├── n_number_-1.0..json │ ├── n_number_-2..json │ ├── n_number_-NaN.json │ ├── n_number_.-1.json │ ├── n_number_.2e-3.json │ ├── n_number_0.1.2.json │ ├── n_number_0.3e+.json │ ├── n_number_0.3e.json │ ├── n_number_0.e1.json │ ├── n_number_0_capital_E+.json │ ├── n_number_0_capital_E.json │ ├── n_number_0e+.json │ ├── n_number_0e.json │ ├── n_number_1.0e+.json │ ├── n_number_1.0e-.json │ ├── n_number_1.0e.json │ ├── n_number_1_000.json │ ├── n_number_1eE2.json │ ├── n_number_2.e+3.json │ ├── n_number_2.e-3.json │ ├── n_number_2.e3.json │ ├── n_number_9.e+.json │ ├── n_number_Inf.json │ ├── n_number_NaN.json │ ├── n_number_U+FF11_fullwidth_digit_one.json │ ├── n_number_expression.json │ ├── n_number_hex_1_digit.json │ ├── n_number_hex_2_digits.json │ ├── n_number_infinity.json │ ├── n_number_invalid+-.json │ ├── n_number_invalid-negative-real.json │ ├── n_number_invalid-utf-8-in-bigger-int.json │ ├── n_number_invalid-utf-8-in-exponent.json │ ├── n_number_invalid-utf-8-in-int.json │ ├── n_number_minus_infinity.json │ ├── n_number_minus_sign_with_trailing_garbage.json │ ├── n_number_minus_space_1.json │ ├── n_number_neg_int_starting_with_zero.json │ ├── n_number_neg_real_without_int_part.json │ ├── n_number_neg_with_garbage_at_end.json │ ├── n_number_real_garbage_after_e.json │ ├── n_number_real_with_invalid_utf8_after_e.json │ ├── n_number_real_without_fractional_part.json │ ├── n_number_starting_with_dot.json │ ├── n_number_with_alpha.json │ ├── n_number_with_alpha_char.json │ ├── n_number_with_leading_zero.json │ ├── n_object_bad_value.json │ ├── n_object_bracket_key.json │ ├── n_object_comma_instead_of_colon.json │ ├── n_object_double_colon.json │ ├── n_object_emoji.json │ ├── n_object_garbage_at_end.json │ ├── n_object_key_with_single_quotes.json │ ├── n_object_lone_continuation_byte_in_key_and_trailing_comma.json │ ├── n_object_missing_colon.json │ ├── n_object_missing_key.json │ ├── n_object_missing_semicolon.json │ ├── n_object_missing_value.json │ ├── n_object_no-colon.json │ ├── n_object_non_string_key.json │ ├── n_object_non_string_key_but_huge_number_instead.json │ ├── n_object_repeated_null_null.json │ ├── n_object_several_trailing_commas.json │ ├── n_object_single_quote.json │ ├── n_object_trailing_comma.json │ ├── n_object_trailing_comment.json │ ├── n_object_trailing_comment_open.json │ ├── n_object_trailing_comment_slash_open.json │ ├── n_object_trailing_comment_slash_open_incomplete.json │ ├── n_object_two_commas_in_a_row.json │ ├── n_object_unquoted_key.json │ ├── n_object_unterminated-value.json │ ├── n_object_with_single_string.json │ ├── n_object_with_trailing_garbage.json │ ├── n_single_space.json │ ├── n_string_1_surrogate_then_escape.json │ ├── n_string_1_surrogate_then_escape_u.json │ ├── n_string_1_surrogate_then_escape_u1.json │ ├── n_string_1_surrogate_then_escape_u1x.json │ ├── n_string_accentuated_char_no_quotes.json │ ├── n_string_backslash_00.json │ ├── n_string_escape_x.json │ ├── n_string_escaped_backslash_bad.json │ ├── n_string_escaped_ctrl_char_tab.json │ ├── n_string_escaped_emoji.json │ ├── n_string_incomplete_escape.json │ ├── n_string_incomplete_escaped_character.json │ ├── n_string_incomplete_surrogate.json │ ├── n_string_incomplete_surrogate_escape_invalid.json │ ├── n_string_invalid-utf-8-in-escape.json │ ├── n_string_invalid_backslash_esc.json │ ├── n_string_invalid_unicode_escape.json │ ├── n_string_invalid_utf8_after_escape.json │ ├── n_string_leading_uescaped_thinspace.json │ ├── n_string_no_quotes_with_bad_escape.json │ ├── n_string_single_doublequote.json │ ├── n_string_single_quote.json │ ├── n_string_single_string_no_double_quotes.json │ ├── n_string_start_escape_unclosed.json │ ├── n_string_unescaped_ctrl_char.json │ ├── n_string_unescaped_newline.json │ ├── n_string_unescaped_tab.json │ ├── n_string_unicode_CapitalU.json │ ├── n_string_with_trailing_garbage.json │ ├── n_structure_100000_opening_arrays.json │ ├── n_structure_U+2060_word_joined.json │ ├── n_structure_UTF8_BOM_no_data.json │ ├── n_structure_angle_bracket_..json │ ├── n_structure_angle_bracket_null.json │ ├── n_structure_array_trailing_garbage.json │ ├── n_structure_array_with_extra_array_close.json │ ├── n_structure_array_with_unclosed_string.json │ ├── n_structure_ascii-unicode-identifier.json │ ├── n_structure_capitalized_True.json │ ├── n_structure_close_unopened_array.json │ ├── n_structure_comma_instead_of_closing_brace.json │ ├── n_structure_double_array.json │ ├── n_structure_end_array.json │ ├── n_structure_incomplete_UTF8_BOM.json │ ├── n_structure_lone-invalid-utf-8.json │ ├── n_structure_lone-open-bracket.json │ ├── n_structure_no_data.json │ ├── n_structure_null-byte-outside-string.json │ ├── n_structure_number_with_trailing_garbage.json │ ├── n_structure_object_followed_by_closing_object.json │ ├── n_structure_object_unclosed_no_value.json │ ├── n_structure_object_with_comment.json │ ├── n_structure_object_with_trailing_garbage.json │ ├── n_structure_open_array_apostrophe.json │ ├── n_structure_open_array_comma.json │ ├── n_structure_open_array_object.json │ ├── n_structure_open_array_open_object.json │ ├── n_structure_open_array_open_string.json │ ├── n_structure_open_array_string.json │ ├── n_structure_open_object.json │ ├── n_structure_open_object_close_array.json │ ├── n_structure_open_object_comma.json │ ├── n_structure_open_object_open_array.json │ ├── n_structure_open_object_open_string.json │ ├── n_structure_open_object_string_with_apostrophes.json │ ├── n_structure_open_open.json │ ├── n_structure_single_eacute.json │ ├── n_structure_single_star.json │ ├── n_structure_trailing_#.json │ ├── n_structure_uescaped_LF_before_string.json │ ├── n_structure_unclosed_array.json │ ├── n_structure_unclosed_array_partial_null.json │ ├── n_structure_unclosed_array_unfinished_false.json │ ├── n_structure_unclosed_array_unfinished_true.json │ ├── n_structure_unclosed_object.json │ ├── n_structure_unicode-identifier.json │ ├── n_structure_whitespace_U+2060_word_joiner.json │ ├── n_structure_whitespace_formfeed.json │ ├── y_array_arraysWithSpaces.json │ ├── y_array_empty-string.json │ ├── y_array_empty.json │ ├── y_array_ending_with_newline.json │ ├── y_array_false.json │ ├── y_array_heterogeneous.json │ ├── y_array_null.json │ ├── y_array_with_1_and_newline.json │ ├── y_array_with_leading_space.json │ ├── y_array_with_several_null.json │ ├── y_array_with_trailing_space.json │ ├── y_number.json │ ├── y_number_0e+1.json │ ├── y_number_0e1.json │ ├── y_number_after_space.json │ ├── y_number_double_close_to_zero.json │ ├── y_number_int_with_exp.json │ ├── y_number_minus_zero.json │ ├── y_number_negative_int.json │ ├── y_number_negative_one.json │ ├── y_number_negative_zero.json │ ├── y_number_real_capital_e.json │ ├── y_number_real_capital_e_neg_exp.json │ ├── y_number_real_capital_e_pos_exp.json │ ├── y_number_real_exponent.json │ ├── y_number_real_fraction_exponent.json │ ├── y_number_real_neg_exp.json │ ├── y_number_real_pos_exponent.json │ ├── y_number_simple_int.json │ ├── y_number_simple_real.json │ ├── y_object.json │ ├── y_object_basic.json │ ├── y_object_duplicated_key.json │ ├── y_object_duplicated_key_and_value.json │ ├── y_object_empty.json │ ├── y_object_empty_key.json │ ├── y_object_escaped_null_in_key.json │ ├── y_object_extreme_numbers.json │ ├── y_object_long_strings.json │ ├── y_object_simple.json │ ├── y_object_string_unicode.json │ ├── y_object_with_newlines.json │ ├── y_string_1_2_3_bytes_UTF-8_sequences.json │ ├── y_string_accepted_surrogate_pair.json │ ├── y_string_accepted_surrogate_pairs.json │ ├── y_string_allowed_escapes.json │ ├── y_string_backslash_and_u_escaped_zero.json │ ├── y_string_backslash_doublequotes.json │ ├── y_string_comments.json │ ├── y_string_double_escape_a.json │ ├── y_string_double_escape_n.json │ ├── y_string_escaped_control_character.json │ ├── y_string_escaped_noncharacter.json │ ├── y_string_in_array.json │ ├── y_string_in_array_with_leading_space.json │ ├── y_string_last_surrogates_1_and_2.json │ ├── y_string_nbsp_uescaped.json │ ├── y_string_nonCharacterInUTF-8_U+10FFFF.json │ ├── y_string_nonCharacterInUTF-8_U+FFFF.json │ ├── y_string_null_escape.json │ ├── y_string_one-byte-utf-8.json │ ├── y_string_pi.json │ ├── y_string_reservedCharacterInUTF-8_U+1BFFF.json │ ├── y_string_simple_ascii.json │ ├── y_string_space.json │ ├── y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json │ ├── y_string_three-byte-utf-8.json │ ├── y_string_two-byte-utf-8.json │ ├── y_string_u+2028_line_sep.json │ ├── y_string_u+2029_par_sep.json │ ├── y_string_uEscape.json │ ├── y_string_uescaped_newline.json │ ├── y_string_unescaped_char_delete.json │ ├── y_string_unicode.json │ ├── y_string_unicodeEscapedBackslash.json │ ├── y_string_unicode_2.json │ ├── y_string_unicode_U+10FFFE_nonchar.json │ ├── y_string_unicode_U+1FFFE_nonchar.json │ ├── y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json │ ├── y_string_unicode_U+2064_invisible_plus.json │ ├── y_string_unicode_U+FDD0_nonchar.json │ ├── y_string_unicode_U+FFFE_nonchar.json │ ├── y_string_unicode_escaped_double_quote.json │ ├── y_string_utf8.json │ ├── y_string_with_del_character.json │ ├── y_structure_lonely_false.json │ ├── y_structure_lonely_int.json │ ├── y_structure_lonely_negative_real.json │ ├── y_structure_lonely_null.json │ ├── y_structure_lonely_string.json │ ├── y_structure_lonely_true.json │ ├── y_structure_string_empty.json │ ├── y_structure_trailing_newline.json │ ├── y_structure_true_in_array.json │ └── y_structure_whitespace_array.json └── test_transform │ ├── number_-9223372036854775808.json │ ├── number_-9223372036854775809.json │ ├── number_1.0.json │ ├── number_1.000000000000000005.json │ ├── number_1000000000000000.json │ ├── number_10000000000000000999.json │ ├── number_1e-999.json │ ├── number_1e6.json │ ├── number_9223372036854775807.json │ ├── number_9223372036854775808.json │ ├── object_key_nfc_nfd.json │ ├── object_key_nfd_nfc.json │ ├── object_same_key_different_values.json │ ├── object_same_key_same_value.json │ ├── object_same_key_unclear_values.json │ ├── string_1_escaped_invalid_codepoint.json │ ├── string_1_invalid_codepoint.json │ ├── string_2_escaped_invalid_codepoints.json │ ├── string_2_invalid_codepoints.json │ ├── string_3_escaped_invalid_codepoints.json │ ├── string_3_invalid_codepoints.json │ └── string_with_escaped_NULL.json ├── test_writer.nim └── utils.nim /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build: 11 | uses: status-im/nimbus-common-workflow/.github/workflows/common.yml@main 12 | with: 13 | test-command: | 14 | env NIMLANG=c nimble test 15 | env NIMLANG=cpp nimble test 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | nimcache 2 | *.exe 3 | nimble.develop 4 | nimble-win.paths 5 | nimble-linux.paths 6 | build/ 7 | vendor/ 8 | -------------------------------------------------------------------------------- /LICENSE-APACHEv2: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2018 Status Research & Development GmbH 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Status Research & Development GmbH 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nim-json-serialization 2 | 3 | [![License: Apache](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 4 | [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) 5 | ![Stability: experimental](https://img.shields.io/badge/stability-experimental-orange.svg) 6 | ![Github action](https://github.com/status-im/nim-json-serialization/workflows/CI/badge.svg) 7 | 8 | Flexible JSON serialization does not rely on run-time type information. 9 | 10 | ## Overview 11 | nim-json-serialization offers rich features on top of [nim-serialization](https://github.com/status-im/nim-serialization) 12 | framework. The following is available but not an exhaustive list of features: 13 | 14 | - Decode into Nim data types efficiently without an intermediate token. 15 | - Able to parse full spec of JSON including the notorious JSON number. 16 | - Support stdlib/JsonNode out of the box. 17 | - While stdlib/JsonNode does not support the full spec of the JSON number, we offer an alternative `JsonValueRef`. 18 | - Skipping JSON value is an efficient process, no token is generated at all and at the same time, the grammar is checked. 19 | - Skipping is also free from custom serializer interference. 20 | - An entire JSON value can be parsed into a valid JSON document string. This string document can be parsed again without losing any information. 21 | - Custom serialization is easy and safe to implement with the help of many built-in parsers. 22 | - Nonstandard features are put behind flags. You can choose which features to switch on or off. 23 | - Because the intended usage of this library will be in a security-demanding application, we make sure malicious inputs will not crash 24 | this library through fuzz tests. 25 | - The user also can tweak certain limits of the lexer/parser behavior using the configuration object. 26 | - `createJsonFlavor` is a powerful way to prevent cross contamination between different subsystem using different custom serializar on the same type. 27 | 28 | ## Spec compliance 29 | nim-json-serialization implements [RFC8259](https://datatracker.ietf.org/doc/html/rfc8259) 30 | JSON spec and pass these test suites: 31 | 32 | - [JSONTestSuite](https://github.com/nst/JSONTestSuite) 33 | 34 | ## Switchable features 35 | Many of these switchable features are widely used features in various projects but are not standard JSON features. 36 | But you can access them using the flags: 37 | 38 | - **allowUnknownFields[=off]**: enable unknown fields to be skipped instead of throwing an error. 39 | - **requireAllFields[=off]**: if one of the required fields is missing, the serializer will throw an error. 40 | - **escapeHex[=off]**: JSON doesn't support `\xHH` escape sequence, but it is a common thing in many languages. 41 | - **relaxedEscape[=off]**: only '0x00'..'0x1F' can be prepended by escape char `\\`, turn this on and you can escape any char. 42 | - **portableInt[=off]**: set the limit of integer to `-2**53 + 1` and `+2**53 - 1`. 43 | - **trailingComma[=on]**: allow the presence of a trailing comma after the last object member or array element. 44 | - **allowComments[=on]**: JSON standard doesn't mention about comments. Turn this on to parse both C style comments of `//..EOL` and `/* .. */`. 45 | - **leadingFraction[=on]**: something like `.123` is not a valid JSON number, but its widespread usage sometimes creeps into JSON documents. 46 | - **integerPositiveSign[=on]**: `+123` is also not a valid JSON number, but since `-123` is a valid JSON number, why not parse it safely? 47 | 48 | ## Safety features 49 | You can modify these default configurations to suit your needs. 50 | 51 | - **nestedDepthLimit: 512**: maximum depth of the nested structure, they are a combination of objects and arrays depth(0=disable). 52 | - **arrayElementsLimit: 0**: maximum number of allowed array elements(0=disable). 53 | - **objectMembersLimit: 0**: maximum number of key-value pairs in an object(0=disable). 54 | - **integerDigitsLimit: 128**: limit the maximum digits of the integer part of JSON number. 55 | - **fractionDigitsLimit: 128**: limit the maximum digits of faction part of JSON number. 56 | - **exponentDigitsLimit: 32**: limit the maximum digits of the exponent part of JSON number. 57 | - **stringLengthLimit: 0**: limit the maximum bytes of string(0=disable). 58 | 59 | ## Special types 60 | 61 | - **JsonString**: Use this type if you want to parse a JSON value to a valid JSON document contained in a string. 62 | - **JsonVoid**: Use this type to skip a valid JSON value. 63 | - **JsonNumber**: Use this to parse a valid JSON number including the fraction and exponent part. 64 | - Please note that this type is a generic, it support `uint64` and `string` as generic param. 65 | - The generic param will define the integer and exponent part as `uint64` or `string`. 66 | - If the generic param is `uint64`, overflow can happen, or max digit limit will apply. 67 | - If the generic param is `string`, the max digit limit will apply. 68 | - The fraction part is always a string to keep the leading zero of the fractional number. 69 | - **JsonValueRef**: Use this type to parse any valid JSON value into something like stdlib/JsonNode. 70 | - `JsonValueRef` is using `JsonNumber` instead of `int` or `float` like stdlib/JsonNode. 71 | 72 | ## Flavor 73 | 74 | While flags and limits are runtime configuration, flavor is a powerful compile time mechanism to prevent 75 | cross contamination between different custom serializer operated the same type. For example, 76 | `json-rpc` subsystem dan `json-rest` subsystem maybe have different custom serializer for the same `UInt256`. 77 | 78 | Json-Flavor will make sure, the compiler picks the right serializer for the right subsystem. 79 | You can use `useDefaultSerializationIn` to add serializers of a flavor to a specific type. 80 | 81 | ```Nim 82 | # These are the parameters you can pass to `createJsonFlavor` to create a new flavor. 83 | 84 | FlavorName: untyped 85 | mimeTypeValue = "application/json" 86 | automaticObjectSerialization = false 87 | requireAllFields = true 88 | omitOptionalFields = true 89 | allowUnknownFields = true 90 | skipNullFields = false 91 | ``` 92 | 93 | ```Nim 94 | type 95 | OptionalFields = object 96 | one: Opt[string] 97 | two: Option[int] 98 | 99 | createJsonFlavor OptJson 100 | OptionalFields.useDefaultSerializationIn OptJson 101 | ``` 102 | 103 | `omitOptionalFields` is used by the Writer to ignore fields with null value. 104 | `skipNullFields` is used by the Reader to ignore fields with null value. 105 | 106 | ## Decoder example 107 | ```nim 108 | type 109 | NimServer = object 110 | name: string 111 | port: int 112 | 113 | MixedServer = object 114 | name: JsonValueRef 115 | port: int 116 | 117 | StringServer = object 118 | name: JsonString 119 | port: JsonString 120 | 121 | # decode into native Nim 122 | var nim_native = Json.decode(rawJson, NimServer) 123 | 124 | # decode into mixed Nim + JsonValueRef 125 | var nim_mixed = Json.decode(rawJson, MixedServer) 126 | 127 | # decode any value into string 128 | var nim_string = Json.decode(rawJson, StringServer) 129 | 130 | # decode any valid JSON 131 | var json_value = Json.decode(rawJson, JsonValueRef) 132 | ``` 133 | 134 | ## Load and save 135 | ```Nim 136 | var server = Json.loadFile("filename.json", Server) 137 | var server_string = Json.loadFile("filename.json", JsonString) 138 | 139 | Json.saveFile("filename.json", server) 140 | ``` 141 | 142 | ## Objects 143 | Decoding an object can be achieved via the `parseObject` template. 144 | To parse the value, you can use one of the helper functions or use `readValue`. 145 | `readObject` and `readObjectFields` iterators are also handy when creating a custom object parser. 146 | 147 | ```Nim 148 | proc readValue*(r: var JsonReader, table: var Table[string, int]) = 149 | parseObject(r, key): 150 | table[key] = r.parseInt(int) 151 | ``` 152 | 153 | ## Sets and list-like 154 | Similar to `Object`, sets and list or array-like data structures can be parsed using 155 | `parseArray` template. It comes in two variations, indexed and non-indexed. 156 | 157 | Built-in `readValue` for regular `seq` and `array` is implemented for you. 158 | No built-in `readValue` for `set` or `set-like` is provided, you must overload it yourself depending on your need. 159 | 160 | ```nim 161 | type 162 | HoldArray = object 163 | data: array[3, int] 164 | 165 | HoldSeq = object 166 | data: seq[int] 167 | 168 | WelderFlag = enum 169 | TIG 170 | MIG 171 | MMA 172 | 173 | Welder = object 174 | flags: set[WelderFlag] 175 | 176 | proc readValue*(r: var JsonReader, value: var HoldArray) = 177 | # parseArray with index, `i` can be any valid identifier 178 | r.parseArray(i): 179 | value.data[i] = r.parseInt(int) 180 | 181 | proc readValue*(r: var JsonReader, value: var HoldSeq) = 182 | # parseArray without index 183 | r.parseArray: 184 | let lastPos = value.data.len 185 | value.data.setLen(lastPos + 1) 186 | readValue(r, value.data[lastPos]) 187 | 188 | proc readValue*(r: var JsonReader, value: var Welder) = 189 | # populating set also okay 190 | r.parseArray: 191 | value.flags.incl r.parseInt(int).WelderFlag 192 | ``` 193 | 194 | ## Custom iterators 195 | Using these custom iterators, you can have access to sub-token elements. 196 | 197 | ```Nim 198 | customIntValueIt(r: var JsonReader; body: untyped) 199 | customNumberValueIt(r: var JsonReader; body: untyped) 200 | customStringValueIt(r: var JsonReader; limit: untyped; body: untyped) 201 | customStringValueIt(r: var JsonReader; body: untyped) 202 | ``` 203 | ## Convenience iterators 204 | 205 | ```Nim 206 | readArray(r: var JsonReader, ElemType: typedesc): ElemType 207 | readObjectFields(r: var JsonReader, KeyType: type): KeyType 208 | readObjectFields(r: var JsonReader): string 209 | readObject(r: var JsonReader, KeyType: type, ValueType: type): (KeyType, ValueType) 210 | ``` 211 | 212 | ## Helper procs 213 | When crafting a custom serializer, use these parsers, they are safe and intuitive. 214 | Avoid using the lexer directly. 215 | 216 | ```Nim 217 | tokKind(r: var JsonReader): JsonValueKind 218 | parseString(r: var JsonReader, limit: int): string 219 | parseString(r: var JsonReader): string 220 | parseBool(r: var JsonReader): bool 221 | parseNull(r: var JsonReader) 222 | parseNumber(r: var JsonReader, T: type): JsonNumber[T: string or uint64] 223 | parseNumber(r: var JsonReader, val: var JsonNumber) 224 | toInt(r: var JsonReader, val: JsonNumber, T: type SomeInteger, portable: bool): T 225 | parseInt(r: var JsonReader, T: type SomeInteger, portable: bool = false): T 226 | toFloat(r: var JsonReader, val: JsonNumber, T: type SomeFloat): T 227 | parseFloat(r: var JsonReader, T: type SomeFloat): T 228 | parseAsString(r: var JsonReader, val: var string) 229 | parseAsString(r: var JsonReader): JsonString 230 | parseValue(r: var JsonReader, T: type): JsonValueRef[T: string or uint64] 231 | parseValue(r: var JsonReader, val: var JsonValueRef) 232 | parseArray(r: var JsonReader; body: untyped) 233 | parseArray(r: var JsonReader; idx: untyped; body: untyped) 234 | parseObject(r: var JsonReader, key: untyped, body: untyped) 235 | parseObjectWithoutSkip(r: var JsonReader, key: untyped, body: untyped) 236 | parseObjectSkipNullFields(r: var JsonReader, key: untyped, body: untyped) 237 | parseObjectCustomKey(r: var JsonReader, keyAction: untyped, body: untyped) 238 | parseJsonNode(r: var JsonReader): JsonNode 239 | skipSingleJsValue(r: var JsonReader) 240 | readRecordValue[T](r: var JsonReader, value: var T) 241 | ``` 242 | 243 | ## Helper procs of JsonWriter 244 | 245 | ```Nim 246 | beginRecord(w: var JsonWriter, T: type) 247 | beginRecord(w: var JsonWriter) 248 | endRecord(w: var JsonWriter) 249 | 250 | writeObject(w: var JsonWriter, T: type) 251 | writeObject(w: var JsonWriter) 252 | 253 | writeFieldName(w: var JsonWriter, name: string) 254 | writeField(w: var JsonWriter, name: string, value: auto) 255 | 256 | iterator stepwiseArrayCreation[C](w: var JsonWriter, collection: C): auto 257 | writeIterable(w: var JsonWriter, collection: auto) 258 | writeArray[T](w: var JsonWriter, elements: openArray[T]) 259 | 260 | writeNumber[F,T](w: var JsonWriter[F], value: JsonNumber[T]) 261 | writeJsonValueRef[F,T](w: var JsonWriter[F], value: JsonValueRef[T]) 262 | ``` 263 | 264 | ## Enums 265 | 266 | ```Nim 267 | type 268 | Fruit = enum 269 | Apple = "Apple" 270 | Banana = "Banana" 271 | 272 | Drawer = enum 273 | One 274 | Two 275 | 276 | Number = enum 277 | Three = 3 278 | Four = 4 279 | 280 | Mixed = enum 281 | Six = 6 282 | Seven = "Seven" 283 | ``` 284 | 285 | nim-json-serialization automatically detect which representation an enum should be parsed. 286 | The detection occurs when parse JSON literal and from the enum declaration itself. 287 | 'Fruit' expect string literal. 'Drawer' or 'Number' expect numeric literal. 288 | 'Mixed' is disallowed. If the json literal does not match the expected enum style, 289 | exception will be raised. But you can configure individual enum type with: 290 | 291 | ```Nim 292 | configureJsonDeserialization( 293 | T: type[enum], allowNumericRepr: static[bool] = false, 294 | stringNormalizer: static[proc(s: string): string] = strictNormalize) 295 | 296 | # example: 297 | Mixed.configureJsonDeserialization(allowNumericRepr = true) # only at top level 298 | ``` 299 | 300 | When encode an enum, user is also given flexibility to configure at Flavor level 301 | or for individual enum type. 302 | 303 | ```Nim 304 | type 305 | EnumRepresentation* = enum 306 | EnumAsString 307 | EnumAsNumber 308 | EnumAsStringifiedNumber 309 | 310 | # examples: 311 | 312 | # Flavor level 313 | Json.flavorEnumRep(EnumAsString) # default flavor, can be called from non top level 314 | Flavor.flavorEnumRep(EnumAsNumber) # custom flavor, can be called from non top level 315 | 316 | # individual enum type no matter what flavor 317 | Fruit.configureJsonSerialization(EnumAsNumber) # only at top level 318 | 319 | # individual enum type of specific flavor 320 | MyJson.flavorEnumRep(Drawer, EnumAsString) # only at top level 321 | ``` 322 | 323 | ## License 324 | 325 | Licensed and distributed under either of 326 | 327 | * MIT license: [LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT 328 | 329 | or 330 | 331 | * Apache License, Version 2.0, ([LICENSE-APACHEv2](LICENSE-APACHEv2) or http://www.apache.org/licenses/LICENSE-2.0) 332 | 333 | at your option. These files may not be copied, modified, or distributed except according to those terms. 334 | -------------------------------------------------------------------------------- /config.nims: -------------------------------------------------------------------------------- 1 | # json-serialization 2 | # Copyright (c) 2023 Status Research & Development GmbH 3 | # Licensed under either of 4 | # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) 5 | # * MIT license ([LICENSE-MIT](LICENSE-MIT)) 6 | # at your option. 7 | # This file may not be copied, modified, or distributed except according to 8 | # those terms. 9 | 10 | if defined(windows): 11 | # disable timestamps in Windows PE headers - https://wiki.debian.org/ReproducibleBuilds/TimestampsInPEBinaries 12 | switch("passL", "-Wl,--no-insert-timestamp") 13 | # increase stack size, unless something else is setting the stack size 14 | if not defined(windowsNoSetStack): 15 | switch("passL", "-Wl,--stack,8388608") 16 | # https://github.com/nim-lang/Nim/issues/4057 17 | --tlsEmulation:off 18 | if defined(i386): 19 | # set the IMAGE_FILE_LARGE_ADDRESS_AWARE flag so we can use PAE, if enabled, and access more than 2 GiB of RAM 20 | switch("passL", "-Wl,--large-address-aware") 21 | 22 | # Avoid some rare stack corruption while using exceptions with a SEH-enabled 23 | # toolchain: https://github.com/status-im/nimbus-eth2/issues/3121 24 | switch("define", "nimRawSetjmp") 25 | 26 | # begin Nimble config (version 1) 27 | when defined(windows): 28 | when fileExists("nimble-win.paths"): 29 | include "nimble-win.paths" 30 | elif fileExists("nimble.paths"): 31 | include "nimble.paths" 32 | elif defined(linux): 33 | when fileExists("nimble-win.paths"): 34 | include "nimble-linux.paths" 35 | elif fileExists("nimble.paths"): 36 | include "nimble.paths" 37 | # end Nimble config 38 | -------------------------------------------------------------------------------- /fuzzer/fuzz_lexer.nim: -------------------------------------------------------------------------------- 1 | # json-serialization 2 | # Copyright (c) 2023 Status Research & Development GmbH 3 | # Licensed under either of 4 | # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) 5 | # * MIT license ([LICENSE-MIT](LICENSE-MIT)) 6 | # at your option. 7 | # This file may not be copied, modified, or distributed except according to 8 | # those terms. 9 | 10 | import 11 | testutils/fuzzing, 12 | faststreams, 13 | ../json_serialization/lexer 14 | 15 | template prepareLexer(T: type, payload: untyped) = 16 | var stream = unsafeMemoryInput(payload) 17 | var lex = init(JsonLexer, stream) 18 | var value: JsonValueRef[T] 19 | lex.scanValue(value) 20 | 21 | test: 22 | prepareLexer(string, payload) 23 | prepareLexer(uint64, payload) 24 | -------------------------------------------------------------------------------- /fuzzer/fuzz_parser.nim: -------------------------------------------------------------------------------- 1 | # json-serialization 2 | # Copyright (c) 2023 Status Research & Development GmbH 3 | # Licensed under either of 4 | # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) 5 | # * MIT license ([LICENSE-MIT](LICENSE-MIT)) 6 | # at your option. 7 | # This file may not be copied, modified, or distributed except according to 8 | # those terms. 9 | 10 | import 11 | testutils/fuzzing, 12 | faststreams, 13 | ../json_serialization/parser 14 | 15 | func toReader(input: openArray[byte]): JsonReader[DefaultFlavor] = 16 | var stream = unsafeMemoryInput(input) 17 | JsonReader[DefaultFlavor].init(stream) 18 | 19 | proc executeParser(payload: openArray[byte]) = 20 | try: 21 | var r = toReader(payload) 22 | let z = r.parseValue(uint64) 23 | discard z 24 | except JsonReaderError: 25 | discard 26 | 27 | test: 28 | executeParser(payload) 29 | -------------------------------------------------------------------------------- /fuzzer/readme.md: -------------------------------------------------------------------------------- 1 | ## Fuzz test manual 2 | 3 | You need to install [testutils](https://github.com/status-im/nim-testutils) and 4 | [chronicles](https://github.com/status-im/nim-chronicles). 5 | Then read documentation over there to prepare your execution environment. 6 | 7 | ### Compatibility 8 | 9 | These fuzzers can be compiled with Nim v1.6.16 or newer. 10 | 11 | ### Available fuzz test 12 | 13 | * fuzz_lexer 14 | * fuzz_parser 15 | 16 | ### Manually with libFuzzer/llvmFuzer 17 | #### Compiling 18 | ```sh 19 | nim c -d:llvmFuzzer -d:release -d:chronicles_log_level=FATAL --noMain --cc=clang --passC="-fsanitize=fuzzer" --passL="-fsanitize=fuzzer" fuzzer/fuzz_lexer 20 | ``` 21 | 22 | #### Starting the Fuzzer 23 | Starting the fuzzer is as simple as running the compiled program: 24 | ```sh 25 | ./fuzz_lexer corpus_dir -runs=1000000 26 | ``` 27 | 28 | To see the available options: 29 | ```sh 30 | ./fuzz_lexer test=1 31 | ``` 32 | 33 | You can also use the application to verify a specific test case: 34 | ```sh 35 | ./fuzz_lexer input_file 36 | ``` 37 | -------------------------------------------------------------------------------- /json_serialization.nim: -------------------------------------------------------------------------------- 1 | # json-serialization 2 | # Copyright (c) 2023 Status Research & Development GmbH 3 | # Licensed under either of 4 | # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) 5 | # * MIT license ([LICENSE-MIT](LICENSE-MIT)) 6 | # at your option. 7 | # This file may not be copied, modified, or distributed except according to 8 | # those terms. 9 | 10 | import 11 | serialization, json_serialization/[format, reader, writer] 12 | 13 | export 14 | serialization, format, reader, writer 15 | -------------------------------------------------------------------------------- /json_serialization.nimble: -------------------------------------------------------------------------------- 1 | # json-serialization 2 | # Copyright (c) 2023-2024 Status Research & Development GmbH 3 | # Licensed under either of 4 | # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) 5 | # * MIT license ([LICENSE-MIT](LICENSE-MIT)) 6 | # at your option. 7 | # This file may not be copied, modified, or distributed except according to 8 | # those terms. 9 | 10 | mode = ScriptMode.Verbose 11 | 12 | packageName = "json_serialization" 13 | version = "0.2.9" 14 | author = "Status Research & Development GmbH" 15 | description = "Flexible JSON serialization not relying on run-time type information" 16 | license = "Apache License 2.0" 17 | skipDirs = @["tests", "fuzzer"] 18 | 19 | requires "nim >= 1.6.0", 20 | "serialization", 21 | "stew", 22 | "results" 23 | 24 | let nimc = getEnv("NIMC", "nim") # Which nim compiler to use 25 | let lang = getEnv("NIMLANG", "c") # Which backend (c/cpp/js) 26 | let flags = getEnv("NIMFLAGS", "") # Extra flags for the compiler 27 | let verbose = getEnv("V", "") notin ["", "0"] 28 | 29 | let cfg = 30 | " --styleCheck:usages --styleCheck:error" & 31 | (if verbose: "" else: " --verbosity:0 --hints:off") & 32 | " --outdir:build --nimcache:build/nimcache -f" & 33 | " -d:nimOldCaseObjects" 34 | 35 | proc build(args, path: string) = 36 | exec nimc & " " & lang & " " & cfg & " " & flags & " " & args & " " & path 37 | 38 | proc run(args, path: string) = 39 | build args & " --mm:refc -r", path 40 | if (NimMajor, NimMinor) > (1, 6): 41 | build args & " --mm:orc -r", path 42 | 43 | task test, "Run all tests": 44 | for threads in ["--threads:off", "--threads:on"]: 45 | run threads, "tests/test_all" 46 | -------------------------------------------------------------------------------- /json_serialization/format.nim: -------------------------------------------------------------------------------- 1 | # json-serialization 2 | # Copyright (c) 2019-2023 Status Research & Development GmbH 3 | # Licensed under either of 4 | # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) 5 | # * MIT license ([LICENSE-MIT](LICENSE-MIT)) 6 | # at your option. 7 | # This file may not be copied, modified, or distributed except according to 8 | # those terms. 9 | 10 | import 11 | serialization/[formats, object_serialization] 12 | 13 | export 14 | formats 15 | 16 | serializationFormat Json, 17 | mimeType = "application/json" 18 | 19 | template supports*(_: type Json, T: type): bool = 20 | # The JSON format should support every type 21 | true 22 | 23 | type 24 | EnumRepresentation* = enum 25 | EnumAsString 26 | EnumAsNumber 27 | EnumAsStringifiedNumber 28 | 29 | template flavorUsesAutomaticObjectSerialization*(T: type DefaultFlavor): bool = true 30 | template flavorOmitsOptionalFields*(T: type DefaultFlavor): bool = true 31 | template flavorRequiresAllFields*(T: type DefaultFlavor): bool = false 32 | template flavorAllowsUnknownFields*(T: type DefaultFlavor): bool = false 33 | template flavorSkipNullFields*(T: type DefaultFlavor): bool = false 34 | 35 | var DefaultFlavorEnumRep {.compileTime.} = EnumAsString 36 | template flavorEnumRep*(T: type DefaultFlavor): EnumRepresentation = 37 | DefaultFlavorEnumRep 38 | 39 | template flavorEnumRep*(T: type DefaultFlavor, rep: static[EnumRepresentation]) = 40 | static: 41 | DefaultFlavorEnumRep = rep 42 | 43 | # If user choose to use `Json` instead of `DefaultFlavor`, it still goes to `DefaultFlavor` 44 | template flavorEnumRep*(T: type Json, rep: static[EnumRepresentation]) = 45 | static: 46 | DefaultFlavorEnumRep = rep 47 | 48 | # We create overloads of these traits to force the mixin treatment of the symbols 49 | type DummyFlavor* = object 50 | template flavorUsesAutomaticObjectSerialization*(T: type DummyFlavor): bool = true 51 | template flavorOmitsOptionalFields*(T: type DummyFlavor): bool = false 52 | template flavorRequiresAllFields*(T: type DummyFlavor): bool = false 53 | template flavorAllowsUnknownFields*(T: type DummyFlavor): bool = false 54 | template flavorSkipNullFields*(T: type DummyFlavor): bool = false 55 | 56 | template createJsonFlavor*(FlavorName: untyped, 57 | mimeTypeValue = "application/json", 58 | automaticObjectSerialization = false, 59 | requireAllFields = true, 60 | omitOptionalFields = true, 61 | allowUnknownFields = true, 62 | skipNullFields = false) {.dirty.} = 63 | type FlavorName* = object 64 | 65 | template Reader*(T: type FlavorName): type = Reader(Json, FlavorName) 66 | template Writer*(T: type FlavorName): type = Writer(Json, FlavorName) 67 | template PreferredOutputType*(T: type FlavorName): type = string 68 | template mimeType*(T: type FlavorName): string = mimeTypeValue 69 | 70 | template flavorUsesAutomaticObjectSerialization*(T: type FlavorName): bool = automaticObjectSerialization 71 | template flavorOmitsOptionalFields*(T: type FlavorName): bool = omitOptionalFields 72 | template flavorRequiresAllFields*(T: type FlavorName): bool = requireAllFields 73 | template flavorAllowsUnknownFields*(T: type FlavorName): bool = allowUnknownFields 74 | template flavorSkipNullFields*(T: type FlavorName): bool = skipNullFields 75 | 76 | var `FlavorName EnumRep` {.compileTime.} = EnumAsString 77 | template flavorEnumRep*(T: type FlavorName): EnumRepresentation = 78 | `FlavorName EnumRep` 79 | 80 | template flavorEnumRep*(T: type FlavorName, rep: static[EnumRepresentation]) = 81 | static: 82 | `FlavorName EnumRep` = rep 83 | -------------------------------------------------------------------------------- /json_serialization/reader.nim: -------------------------------------------------------------------------------- 1 | # json-serialization 2 | # Copyright (c) 2019-2023 Status Research & Development GmbH 3 | # Licensed under either of 4 | # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) 5 | # * MIT license ([LICENSE-MIT](LICENSE-MIT)) 6 | # at your option. 7 | # This file may not be copied, modified, or distributed except according to 8 | # those terms. 9 | 10 | 11 | import 12 | reader_desc, reader_impl, parser 13 | 14 | export 15 | reader_desc, reader_impl, parser 16 | -------------------------------------------------------------------------------- /json_serialization/reader_desc.nim: -------------------------------------------------------------------------------- 1 | # json-serialization 2 | # Copyright (c) 2019-2023 Status Research & Development GmbH 3 | # Licensed under either of 4 | # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) 5 | # * MIT license ([LICENSE-MIT](LICENSE-MIT)) 6 | # at your option. 7 | # This file may not be copied, modified, or distributed except according to 8 | # those terms. 9 | 10 | {.experimental: "notnil".} 11 | 12 | import 13 | std/[strformat], 14 | faststreams/inputs, 15 | serialization/[formats, errors, object_serialization], 16 | "."/[format, types, lexer] 17 | 18 | export 19 | inputs, format, types, errors, 20 | DefaultFlavor 21 | 22 | type 23 | JsonReader*[Flavor = DefaultFlavor] = object 24 | lex*: JsonLexer 25 | 26 | JsonReaderError* = object of JsonError 27 | line*, col*: int 28 | 29 | UnexpectedField* = object of JsonReaderError 30 | encounteredField*: string 31 | deserializedType*: cstring 32 | 33 | ExpectedTokenCategory* = enum 34 | etValue = "value" 35 | etBool = "bool literal" 36 | etInt = "integer" 37 | etEnumAny = "enum value (int / string)" 38 | etEnumString = "enum value (string)" 39 | etNumber = "number" 40 | etString = "string" 41 | etComma = "comma" 42 | etColon = "colon" 43 | etBracketLe = "array start bracket" 44 | etBracketRi = "array end bracker" 45 | etCurrlyLe = "object start bracket" 46 | etCurrlyRi = "object end bracket" 47 | 48 | GenericJsonReaderError* = object of JsonReaderError 49 | deserializedField*: string 50 | innerException*: ref CatchableError 51 | 52 | UnexpectedTokenError* = object of JsonReaderError 53 | encountedToken*: JsonValueKind 54 | expectedToken*: ExpectedTokenCategory 55 | 56 | UnexpectedValueError* = object of JsonReaderError 57 | 58 | IncompleteObjectError* = object of JsonReaderError 59 | objectType: cstring 60 | 61 | IntOverflowError* = object of JsonReaderError 62 | isNegative: bool 63 | absIntVal: BiggestUInt 64 | 65 | Json.setReader JsonReader 66 | 67 | {.push gcsafe, raises: [].} 68 | 69 | func valueStr(err: ref IntOverflowError): string = 70 | if err.isNegative: 71 | result.add '-' 72 | result.add($err.absIntVal) 73 | 74 | template tryFmt(expr: untyped): string = 75 | try: expr 76 | except CatchableError as err: err.msg 77 | 78 | method formatMsg*(err: ref JsonReaderError, filename: string): 79 | string {.gcsafe, raises: [].} = 80 | tryFmt: fmt"{filename}({err.line}, {err.col}) Error while reading json file: {err.msg}" 81 | 82 | method formatMsg*(err: ref UnexpectedField, filename: string): 83 | string {.gcsafe, raises: [].} = 84 | tryFmt: fmt"{filename}({err.line}, {err.col}) Unexpected field '{err.encounteredField}' while deserializing {err.deserializedType}" 85 | 86 | method formatMsg*(err: ref UnexpectedTokenError, filename: string): 87 | string {.gcsafe, raises: [].} = 88 | tryFmt: fmt"{filename}({err.line}, {err.col}) Unexpected token '{err.encountedToken}' in place of '{err.expectedToken}'" 89 | 90 | method formatMsg*(err: ref GenericJsonReaderError, filename: string): 91 | string {.gcsafe, raises: [].} = 92 | tryFmt: fmt"{filename}({err.line}, {err.col}) Exception encountered while deserializing '{err.deserializedField}': [{err.innerException.name}] {err.innerException.msg}" 93 | 94 | method formatMsg*(err: ref IntOverflowError, filename: string): 95 | string {.gcsafe, raises: [].} = 96 | tryFmt: fmt"{filename}({err.line}, {err.col}) The value '{err.valueStr}' is outside of the allowed range" 97 | 98 | method formatMsg*(err: ref UnexpectedValueError, filename: string): 99 | string {.gcsafe, raises: [].} = 100 | tryFmt: fmt"{filename}({err.line}, {err.col}) {err.msg}" 101 | 102 | method formatMsg*(err: ref IncompleteObjectError, filename: string): 103 | string {.gcsafe, raises: [].} = 104 | tryFmt: fmt"{filename}({err.line}, {err.col}) Not all required fields were specified when reading '{err.objectType}'" 105 | 106 | func assignLineNumber*(ex: ref JsonReaderError, lex: JsonLexer) = 107 | ex.line = lex.line 108 | ex.col = lex.tokenStartCol 109 | 110 | proc raiseUnexpectedToken*(lex: var JsonLexer, expected: ExpectedTokenCategory) 111 | {.noreturn, raises: [JsonReaderError, IOError].} = 112 | var ex = new UnexpectedTokenError 113 | ex.assignLineNumber(lex) 114 | ex.encountedToken = lex.tokKind 115 | ex.expectedToken = expected 116 | raise ex 117 | 118 | template raiseUnexpectedToken*(reader: JsonReader, expected: ExpectedTokenCategory) = 119 | raiseUnexpectedToken(reader.lex, expected) 120 | 121 | func raiseUnexpectedValue*( 122 | lex: JsonLexer, msg: string) {.noreturn, raises: [JsonReaderError].} = 123 | var ex = new UnexpectedValueError 124 | ex.assignLineNumber(lex) 125 | ex.msg = msg 126 | raise ex 127 | 128 | template raiseUnexpectedValue*(r: JsonReader, msg: string) = 129 | raiseUnexpectedValue(r.lex, msg) 130 | 131 | func raiseIntOverflow*( 132 | lex: JsonLexer, absIntVal: BiggestUInt, isNegative: bool) 133 | {.noreturn, raises: [JsonReaderError].} = 134 | var ex = new IntOverflowError 135 | ex.assignLineNumber(lex) 136 | ex.absIntVal = absIntVal 137 | ex.isNegative = isNegative 138 | raise ex 139 | 140 | template raiseIntOverflow*(r: JsonReader, absIntVal: BiggestUInt, isNegative: bool) = 141 | raiseIntOverflow(r.lex, absIntVal, isNegative) 142 | 143 | func raiseUnexpectedField*( 144 | lex: JsonLexer, fieldName: string, deserializedType: cstring) 145 | {.noreturn, raises: [JsonReaderError].} = 146 | var ex = new UnexpectedField 147 | ex.assignLineNumber(lex) 148 | ex.encounteredField = fieldName 149 | ex.deserializedType = deserializedType 150 | raise ex 151 | 152 | template raiseUnexpectedField*(r: JsonReader, fieldName: string, deserializedType: cstring) = 153 | raiseUnexpectedField(r.lex, fieldName, deserializedType) 154 | 155 | func raiseIncompleteObject*( 156 | lex: JsonLexer, objectType: cstring) 157 | {.noreturn, raises: [JsonReaderError].} = 158 | var ex = new IncompleteObjectError 159 | ex.assignLineNumber(lex) 160 | ex.objectType = objectType 161 | raise ex 162 | 163 | template raiseIncompleteObject*(r: JsonReader, objectType: cstring) = 164 | raiseIncompleteObject(r.lex, objectType) 165 | 166 | func handleReadException*(lex: JsonLexer, 167 | Record: type, 168 | fieldName: string, 169 | field: auto, 170 | err: ref CatchableError) {.raises: [JsonReaderError].} = 171 | var ex = new GenericJsonReaderError 172 | ex.assignLineNumber(lex) 173 | ex.deserializedField = fieldName 174 | ex.innerException = err 175 | raise ex 176 | 177 | template handleReadException*(r: JsonReader, 178 | Record: type, 179 | fieldName: string, 180 | field: auto, 181 | err: ref CatchableError) = 182 | handleReadException(r.lex, Record, fieldName, field, err) 183 | 184 | proc init*(T: type JsonReader, 185 | stream: InputStream, 186 | flags: JsonReaderFlags, 187 | conf: JsonReaderConf = defaultJsonReaderConf): T {.raises: [].} = 188 | result.lex = JsonLexer.init(stream, flags, conf) 189 | 190 | proc init*(T: type JsonReader, 191 | stream: InputStream, 192 | allowUnknownFields = false, 193 | requireAllFields = false): T {.raises: [].} = 194 | mixin flavorAllowsUnknownFields, flavorRequiresAllFields 195 | type Flavor = T.Flavor 196 | 197 | var flags = defaultJsonReaderFlags 198 | if allowUnknownFields or flavorAllowsUnknownFields(Flavor): 199 | flags.incl JsonReaderFlag.allowUnknownFields 200 | if requireAllFields or flavorRequiresAllFields(Flavor): 201 | flags.incl JsonReaderFlag.requireAllFields 202 | result.lex = JsonLexer.init(stream, flags) 203 | 204 | {.pop.} 205 | -------------------------------------------------------------------------------- /json_serialization/reader_impl.nim: -------------------------------------------------------------------------------- 1 | # json-serialization 2 | # Copyright (c) 2019-2023 Status Research & Development GmbH 3 | # Licensed under either of 4 | # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) 5 | # * MIT license ([LICENSE-MIT](LICENSE-MIT)) 6 | # at your option. 7 | # This file may not be copied, modified, or distributed except according to 8 | # those terms. 9 | 10 | {.experimental: "notnil".} 11 | 12 | import 13 | std/[enumutils, tables, macros, strformat, typetraits], 14 | stew/[enums, objects], 15 | faststreams/inputs, 16 | serialization/[object_serialization, errors], 17 | "."/[format, types, lexer, parser, reader_desc] 18 | 19 | from json import JsonNode 20 | 21 | export 22 | enumutils, inputs, format, types, errors, parser, reader_desc 23 | 24 | {.push gcsafe, raises: [].} 25 | 26 | # ------------------------------------------------------------------------------ 27 | # Private helpers 28 | # ------------------------------------------------------------------------------ 29 | 30 | func allowUnknownFields(r: JsonReader): bool = 31 | JsonReaderFlag.allowUnknownFields in r.lex.flags 32 | 33 | func requireAllFields(r: JsonReader): bool = 34 | JsonReaderFlag.requireAllFields in r.lex.flags 35 | 36 | func allocPtr[T](p: var ptr T) = 37 | p = create(T) 38 | 39 | func allocPtr[T](p: var ref T) = 40 | p = new(T) 41 | 42 | func isNotNilCheck[T](x: ref T not nil) {.compileTime.} = discard 43 | func isNotNilCheck[T](x: ptr T not nil) {.compileTime.} = discard 44 | 45 | func setBitInWord(x: var uint, bit: int) {.inline.} = 46 | let mask = uint(1) shl bit 47 | x = x or mask 48 | 49 | const bitsPerWord = sizeof(uint) * 8 50 | 51 | template setBitInArray[N](data: var array[N, uint], bitIdx: int) = 52 | when data.len > 1: 53 | setBitInWord(data[bitIdx div bitsPerWord], bitIdx mod bitsPerWord) 54 | else: 55 | setBitInWord(data[0], bitIdx) 56 | 57 | func isBitwiseSubsetOf[N](lhs, rhs: array[N, uint]): bool = 58 | for i in low(lhs) .. high(lhs): 59 | if (lhs[i] and rhs[i]) != lhs[i]: 60 | return false 61 | 62 | true 63 | 64 | # this construct catches `array[N, char]` which otherwise won't decompose into 65 | # openArray[char] - we treat any array-like thing-of-characters as a string in 66 | # the output 67 | template isCharArray[N](v: array[N, char]): bool = true 68 | template isCharArray(v: auto): bool = false 69 | 70 | proc parseStringEnum[T]( 71 | r: var JsonReader, value: var T, 72 | stringNormalizer: static[proc(s: string): string]) 73 | {.raises: [IOError, JsonReaderError].} = 74 | try: 75 | value = genEnumCaseStmt( 76 | T, r.parseString(), 77 | default = nil, ord(T.low), ord(T.high), stringNormalizer) 78 | except ValueError: 79 | const typeName = typetraits.name(T) 80 | r.raiseUnexpectedValue("Invalid value for '" & typeName & "'") 81 | 82 | func strictNormalize(s: string): string = # Match enum value exactly 83 | s 84 | 85 | proc parseEnum[T]( 86 | r: var JsonReader, value: var T, allowNumericRepr: static[bool] = false, 87 | stringNormalizer: static[proc(s: string): string] = strictNormalize) 88 | {.raises: [IOError, JsonReaderError].} = 89 | const style = T.enumStyle 90 | case r.tokKind 91 | of JsonValueKind.String: 92 | r.parseStringEnum(value, stringNormalizer) 93 | of JsonValueKind.Number: 94 | when allowNumericRepr: 95 | case style 96 | of EnumStyle.Numeric: 97 | if not value.checkedEnumAssign(r.parseInt(int)): 98 | const typeName = typetraits.name(T) 99 | r.raiseUnexpectedValue("Out of range for '" & typeName & "'") 100 | of EnumStyle.AssociatedStrings: 101 | r.raiseUnexpectedToken etEnumString 102 | else: 103 | r.raiseUnexpectedToken etEnumString 104 | else: 105 | case style 106 | of EnumStyle.Numeric: 107 | when allowNumericRepr: 108 | r.raiseUnexpectedToken etEnumAny 109 | else: 110 | r.raiseUnexpectedToken etEnumString 111 | of EnumStyle.AssociatedStrings: 112 | r.raiseUnexpectedToken etEnumString 113 | 114 | # ------------------------------------------------------------------------------ 115 | # Public iterators 116 | # ------------------------------------------------------------------------------ 117 | 118 | iterator readArray*(r: var JsonReader, ElemType: typedesc): ElemType 119 | {.raises: [IOError, SerializationError].} = 120 | mixin readValue 121 | 122 | r.parseArray: 123 | var res: ElemType 124 | readValue(r, res) 125 | yield res 126 | 127 | iterator readObjectFields*(r: var JsonReader, 128 | KeyType: type): KeyType 129 | {.raises: [IOError, SerializationError].} = 130 | mixin readValue 131 | 132 | r.parseObjectCustomKey: 133 | var key: KeyType 134 | readValue(r, key) 135 | do: 136 | yield key 137 | 138 | iterator readObject*(r: var JsonReader, 139 | KeyType: type, 140 | ValueType: type): (KeyType, ValueType) 141 | {.raises: [IOError, SerializationError].} = 142 | mixin readValue 143 | 144 | for fieldName in readObjectFields(r, KeyType): 145 | var value: ValueType 146 | readValue(r, value) 147 | yield (fieldName, value) 148 | 149 | # ------------------------------------------------------------------------------ 150 | # Public functions 151 | # ------------------------------------------------------------------------------ 152 | 153 | func isFieldExpected*(T: type): bool {.compileTime.} = 154 | T isnot Option 155 | 156 | func totalExpectedFields*(T: type): int {.compileTime.} = 157 | mixin isFieldExpected, 158 | enumAllSerializedFields 159 | 160 | enumAllSerializedFields(T): 161 | if isFieldExpected(FieldType): 162 | inc result 163 | 164 | func expectedFieldsBitmask*(TT: type): auto {.compileTime.} = 165 | type T = TT 166 | 167 | mixin isFieldExpected, 168 | enumAllSerializedFields 169 | 170 | const requiredWords = 171 | (totalSerializedFields(T) + bitsPerWord - 1) div bitsPerWord 172 | 173 | var res: array[requiredWords, uint] 174 | 175 | var i = 0 176 | enumAllSerializedFields(T): 177 | if isFieldExpected(FieldType): 178 | res[i div bitsPerWord].setBitInWord(i mod bitsPerWord) 179 | inc i 180 | 181 | res 182 | 183 | proc readRecordValue*[T](r: var JsonReader, value: var T) 184 | {.raises: [SerializationError, IOError].} = 185 | type 186 | ReaderType {.used.} = type r 187 | T = type value 188 | 189 | const someCode = T.totalSerializedFields > 0 190 | 191 | when someCode: 192 | const 193 | fieldsTable = T.fieldReadersTable(ReaderType) 194 | expectedFields = T.expectedFieldsBitmask 195 | 196 | var 197 | encounteredFields: typeof(expectedFields) 198 | mostLikelyNextField = 0 199 | 200 | r.parseObjectCustomKey: 201 | when someCode: 202 | let key = r.parseString() 203 | when T is tuple: 204 | let fieldIdx = mostLikelyNextField 205 | mostLikelyNextField += 1 206 | discard key 207 | else: 208 | let fieldIdx = findFieldIdx(fieldsTable, 209 | key, 210 | mostLikelyNextField) 211 | do: 212 | when someCode: 213 | if fieldIdx != -1: 214 | let reader = fieldsTable[fieldIdx].reader 215 | reader(value, r) 216 | encounteredFields.setBitInArray(fieldIdx) 217 | elif r.allowUnknownFields: 218 | r.skipSingleJsValue() 219 | else: 220 | const typeName = typetraits.name(T) 221 | r.raiseUnexpectedField(key, cstring typeName) 222 | 223 | when someCode: 224 | if r.requireAllFields and 225 | not expectedFields.isBitwiseSubsetOf(encounteredFields): 226 | const typeName = typetraits.name(T) 227 | r.raiseIncompleteObject(typeName) 228 | 229 | proc readValue*[T](r: var JsonReader, value: var T) 230 | {.gcsafe, raises: [SerializationError, IOError].} = 231 | ## Master field/object parser. This function relies on 232 | ## customised sub-mixins for particular object types. 233 | ## 234 | ## Customised readValue() examples: 235 | ## :: 236 | ## type 237 | ## FancyInt = distinct int 238 | ## FancyUInt = distinct uint 239 | ## 240 | ## proc readValue(reader: var JsonReader, value: var FancyInt) = 241 | ## ## Refer to another readValue() instance 242 | ## value = reader.readValue(int).FancyInt 243 | mixin readValue 244 | 245 | when value is JsonString: 246 | value = r.parseAsString() 247 | 248 | elif value is JsonNode: 249 | value = r.parseJsonNode() 250 | 251 | elif value is JsonNumber: 252 | r.parseNumber(value) 253 | 254 | elif value is JsonVoid: 255 | r.skipSingleJsValue() 256 | 257 | elif value is JsonValueRef: 258 | r.parseValue(value) 259 | 260 | elif value is string: 261 | value = r.parseString() 262 | 263 | elif value is seq[char]: 264 | let val = r.parseString() 265 | value.setLen(val.len) 266 | for i in 0.. 0 122 | 123 | func hasExponent*[T](x: JsonNumber[T]): bool = 124 | when T is string: 125 | x.exponent.len > 0 126 | else: 127 | x.exponent > 0 128 | 129 | func toInt*(sign: JsonSign): int = 130 | case sign: 131 | of JsonSign.None: 1 132 | of JsonSign.Pos: 0 133 | of JsonSign.Neg: -1 134 | 135 | func `==`*(lhs, rhs: JsonValueRef): bool = 136 | if lhs.isNil and rhs.isNil: 137 | return true 138 | 139 | if not lhs.isNil and rhs.isNil: 140 | return false 141 | 142 | if lhs.isNil and not rhs.isNil: 143 | return false 144 | 145 | if lhs.kind != rhs.kind: 146 | return false 147 | 148 | case lhs.kind 149 | of JsonValueKind.String: 150 | lhs.strVal == rhs.strVal 151 | of JsonValueKind.Number: 152 | lhs.numVal == rhs.numVal 153 | of JsonValueKind.Object: 154 | if lhs.objVal.len != rhs.objVal.len: 155 | return false 156 | for k, v in lhs.objVal: 157 | let rhsVal = rhs.objVal.getOrDefault(k, nil) 158 | if rhsVal.isNil: 159 | return false 160 | if rhsVal != v: 161 | return false 162 | true 163 | of JsonValueKind.Array: 164 | if lhs.arrayVal.len != rhs.arrayVal.len: 165 | return false 166 | for i, x in lhs.arrayVal: 167 | if x != rhs.arrayVal[i]: 168 | return false 169 | true 170 | of JsonValueKind.Bool: 171 | lhs.boolVal == rhs.boolVal 172 | of JsonValueKind.Null: 173 | true 174 | 175 | {.pop.} 176 | -------------------------------------------------------------------------------- /json_serialization/value_ops.nim: -------------------------------------------------------------------------------- 1 | # json-serialization 2 | # Copyright (c) 2023-2024 Status Research & Development GmbH 3 | # Licensed under either of 4 | # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) 5 | # * MIT license ([LICENSE-MIT](LICENSE-MIT)) 6 | # at your option. 7 | # This file may not be copied, modified, or distributed except according to 8 | # those terms. 9 | 10 | import 11 | std/[tables, strutils], 12 | ./types 13 | 14 | {.push gcsafe, raises: [].} 15 | 16 | proc len*(n: JsonValueRef): int = 17 | ## If `n` is a `JsonValueKind.Array`, it returns the number of elements. 18 | ## If `n` is a `JsonValueKind.Object`, it returns the number of pairs. 19 | ## Else it returns 0. 20 | case n.kind 21 | of JsonValueKind.Array: result = n.arrayVal.len 22 | of JsonValueKind.Object: result = n.objVal.len 23 | else: discard 24 | 25 | proc `[]`*(node: JsonValueRef, name: string): JsonValueRef {.inline.} = 26 | ## Gets a field from a `JsonValueKind.Object`, which must not be nil. 27 | assert(not isNil(node)) 28 | assert(node.kind == JsonValueKind.Object) 29 | node.objVal.getOrDefault(name, nil) 30 | 31 | proc `[]`*(node: JsonValueRef, index: int): JsonValueRef {.inline.} = 32 | ## Gets the node at `index` in an Array. Result is undefined if `index` 33 | ## is out of bounds, but as long as array bound checks are enabled it will 34 | ## result in an exception. 35 | assert(not isNil(node)) 36 | assert(node.kind == JsonValueKind.Array) 37 | node.arrayVal[index] 38 | 39 | proc contains*(node: JsonValueRef, key: string): bool = 40 | ## Checks if `key` exists in `node`. 41 | assert(node.kind == JsonValueKind.Object) 42 | node.objVal.hasKey(key) 43 | 44 | proc contains*(node: JsonValueRef, val: JsonValueRef): bool = 45 | ## Checks if `val` exists in array `node`. 46 | assert(node.kind == JsonValueKind.Array) 47 | find(node.arrayVal, val) >= 0 48 | 49 | proc `[]=`*(obj: JsonValueRef, key: string, val: JsonValueRef) {.inline.} = 50 | ## Sets a field from a `JsonValueKind.Object`. 51 | assert(obj.kind == JsonValueKind.Object) 52 | obj.objVal[key] = val 53 | 54 | proc `[]=`*(obj: JsonValueRef, index: int, val: JsonValueRef) {.inline.} = 55 | ## Sets a field from a `JsonValueKind.Array`. 56 | assert(obj.kind == JsonValueKind.Array) 57 | obj.arrayVal[index] = val 58 | 59 | proc `{}`*(node: JsonValueRef, keys: varargs[string]): JsonValueRef = 60 | ## Traverses the node and gets the given value. If any of the 61 | ## keys do not exist, returns ``nil``. Also returns ``nil`` if one of the 62 | ## intermediate data structures is not an object. 63 | result = node 64 | for key in keys: 65 | if isNil(result) or result.kind != JsonValueKind.Object: 66 | return nil 67 | result = result.objVal.getOrDefault(key) 68 | 69 | proc getOrDefault*(node: JsonValueRef, key: string): JsonValueRef = 70 | ## Gets a field from a `node`. If `node` is nil or not an object or 71 | ## value at `key` does not exist, returns nil 72 | if not isNil(node) and node.kind == JsonValueKind.Object: 73 | result = node.objVal.getOrDefault(key) 74 | 75 | proc delete*(obj: JsonValueRef, key: string) = 76 | ## Deletes ``obj[key]``. 77 | assert(obj.kind == JsonValueKind.Object) 78 | if not obj.objVal.hasKey(key): 79 | raise newException(IndexDefect, "key not in object") 80 | obj.objVal.del(key) 81 | 82 | func compare*(lhs, rhs: JsonValueRef): bool 83 | 84 | func compareObject(lhs, rhs: JsonValueRef): bool = 85 | ## assume lhs.len >= rhs.len 86 | ## null field and no field are treated equals 87 | for k, v in lhs.objVal: 88 | let rhsVal = rhs.objVal.getOrDefault(k, nil) 89 | if rhsVal.isNil: 90 | if v.kind != JsonValueKind.Null: 91 | return false 92 | else: 93 | continue 94 | if not compare(rhsVal, v): 95 | return false 96 | true 97 | 98 | func compare*(lhs, rhs: JsonValueRef): bool = 99 | ## The difference between `==` and `compare` 100 | ## lies in the object comparison. Null field `compare` 101 | ## to non existent field will return true. 102 | ## On the other hand, `==` will return false. 103 | 104 | if lhs.isNil and rhs.isNil: 105 | return true 106 | 107 | if not lhs.isNil and rhs.isNil: 108 | return false 109 | 110 | if lhs.isNil and not rhs.isNil: 111 | return false 112 | 113 | if lhs.kind != rhs.kind: 114 | return false 115 | 116 | case lhs.kind 117 | of JsonValueKind.String: 118 | lhs.strVal == rhs.strVal 119 | of JsonValueKind.Number: 120 | lhs.numVal == rhs.numVal 121 | of JsonValueKind.Object: 122 | if lhs.objVal.len >= rhs.objVal.len: 123 | compareObject(lhs, rhs) 124 | else: 125 | compareObject(rhs, lhs) 126 | of JsonValueKind.Array: 127 | if lhs.arrayVal.len != rhs.arrayVal.len: 128 | return false 129 | for i, x in lhs.arrayVal: 130 | if not compare(x, rhs.arrayVal[i]): 131 | return false 132 | true 133 | of JsonValueKind.Bool: 134 | lhs.boolVal == rhs.boolVal 135 | of JsonValueKind.Null: 136 | true 137 | 138 | {.pop.} 139 | -------------------------------------------------------------------------------- /json_serialization/writer.nim: -------------------------------------------------------------------------------- 1 | # json-serialization 2 | # Copyright (c) 2019-2023 Status Research & Development GmbH 3 | # Licensed under either of 4 | # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) 5 | # * MIT license ([LICENSE-MIT](LICENSE-MIT)) 6 | # at your option. 7 | # This file may not be copied, modified, or distributed except according to 8 | # those terms. 9 | 10 | import 11 | std/[json, typetraits], 12 | faststreams/[outputs, textio], 13 | serialization, 14 | "."/[format, types] 15 | 16 | export 17 | outputs, format, types, JsonString, DefaultFlavor 18 | 19 | type 20 | JsonWriterState = enum 21 | RecordExpected 22 | RecordStarted 23 | AfterField 24 | 25 | JsonWriter*[Flavor = DefaultFlavor] = object 26 | stream*: OutputStream 27 | hasTypeAnnotations: bool 28 | hasPrettyOutput*: bool # read-only 29 | nestingLevel*: int # read-only 30 | state: JsonWriterState 31 | 32 | Json.setWriter JsonWriter, 33 | PreferredOutput = string 34 | 35 | func init*(W: type JsonWriter, stream: OutputStream, 36 | pretty = false, typeAnnotations = false): W = 37 | W(stream: stream, 38 | hasPrettyOutput: pretty, 39 | hasTypeAnnotations: typeAnnotations, 40 | nestingLevel: if pretty: 0 else: -1, 41 | state: RecordExpected) 42 | 43 | proc beginRecord*(w: var JsonWriter, T: type) 44 | proc beginRecord*(w: var JsonWriter) 45 | proc writeValue*(w: var JsonWriter, value: auto) {.gcsafe, raises: [IOError].} 46 | 47 | # If it's an optional field, test for it's value before write something. 48 | # If it's non optional field, the field is always written. 49 | template shouldWriteObjectField*[FieldType](field: FieldType): bool = true 50 | 51 | template append(x: untyped) = 52 | write w.stream, x 53 | 54 | template indent = 55 | for i in 0 ..< w.nestingLevel: 56 | append ' ' 57 | 58 | template `$`*(s: JsonString): string = 59 | string(s) 60 | 61 | proc writeFieldName*(w: var JsonWriter, name: string) = 62 | # this is implemented as a separate proc in order to 63 | # keep the code bloat from `writeField` to a minimum 64 | doAssert w.state != RecordExpected 65 | 66 | if w.state == AfterField: 67 | append ',' 68 | 69 | if w.hasPrettyOutput: 70 | append '\n' 71 | 72 | indent() 73 | 74 | append '"' 75 | append name 76 | append '"' 77 | append ':' 78 | if w.hasPrettyOutput: append ' ' 79 | 80 | w.state = RecordExpected 81 | 82 | proc writeField*( 83 | w: var JsonWriter, name: string, value: auto) {.raises: [IOError].} = 84 | mixin writeValue 85 | mixin flavorOmitsOptionalFields, shouldWriteObjectField 86 | 87 | type 88 | Writer = typeof w 89 | Flavor = Writer.Flavor 90 | 91 | when flavorOmitsOptionalFields(Flavor): 92 | if shouldWriteObjectField(value): 93 | w.writeFieldName(name) 94 | w.writeValue(value) 95 | w.state = AfterField 96 | else: 97 | w.writeFieldName(name) 98 | w.writeValue(value) 99 | w.state = AfterField 100 | 101 | template fieldWritten*(w: var JsonWriter) = 102 | w.state = AfterField 103 | 104 | proc beginRecord*(w: var JsonWriter) = 105 | doAssert w.state == RecordExpected 106 | 107 | append '{' 108 | if w.hasPrettyOutput: 109 | w.nestingLevel += 2 110 | 111 | w.state = RecordStarted 112 | 113 | proc beginRecord*(w: var JsonWriter, T: type) = 114 | w.beginRecord() 115 | if w.hasTypeAnnotations: w.writeField("$type", typetraits.name(T)) 116 | 117 | proc endRecord*(w: var JsonWriter) = 118 | doAssert w.state != RecordExpected 119 | 120 | if w.hasPrettyOutput: 121 | append '\n' 122 | w.nestingLevel -= 2 123 | indent() 124 | 125 | append '}' 126 | 127 | template endRecordField*(w: var JsonWriter) = 128 | endRecord(w) 129 | w.state = AfterField 130 | 131 | iterator stepwiseArrayCreation*[C](w: var JsonWriter, collection: C): auto = 132 | append '[' 133 | 134 | if w.hasPrettyOutput: 135 | append '\n' 136 | w.nestingLevel += 2 137 | indent() 138 | 139 | var first = true 140 | for e in collection: 141 | if not first: 142 | append ',' 143 | if w.hasPrettyOutput: 144 | append '\n' 145 | indent() 146 | 147 | w.state = RecordExpected 148 | yield e 149 | first = false 150 | 151 | if w.hasPrettyOutput: 152 | append '\n' 153 | w.nestingLevel -= 2 154 | indent() 155 | 156 | append ']' 157 | 158 | proc writeIterable*(w: var JsonWriter, collection: auto) = 159 | mixin writeValue 160 | for e in w.stepwiseArrayCreation(collection): 161 | w.writeValue(e) 162 | 163 | proc writeArray*[T](w: var JsonWriter, elements: openArray[T]) = 164 | writeIterable(w, elements) 165 | 166 | template writeObject*(w: var JsonWriter, T: type, body: untyped) = 167 | w.beginRecord(T) 168 | body 169 | w.endRecord() 170 | 171 | template writeObject*(w: var JsonWriter, body: untyped) = 172 | w.beginRecord() 173 | body 174 | w.endRecord() 175 | 176 | # this construct catches `array[N, char]` which otherwise won't decompose into 177 | # openArray[char] - we treat any array-like thing-of-characters as a string in 178 | # the output 179 | template isStringLike(v: string|cstring|openArray[char]|seq[char]): bool = true 180 | template isStringLike[N](v: array[N, char]): bool = true 181 | template isStringLike(v: auto): bool = false 182 | 183 | template writeObjectField*[FieldType, RecordType](w: var JsonWriter, 184 | record: RecordType, 185 | fieldName: static string, 186 | field: FieldType) = 187 | mixin writeFieldIMPL, writeValue 188 | 189 | w.writeFieldName(fieldName) 190 | when RecordType is tuple: 191 | w.writeValue(field) 192 | else: 193 | type R = type record 194 | w.writeFieldIMPL(FieldTag[R, fieldName], field, record) 195 | 196 | proc writeRecordValue*(w: var JsonWriter, value: auto) 197 | {.gcsafe, raises: [IOError].} = 198 | mixin enumInstanceSerializedFields, writeObjectField 199 | mixin flavorOmitsOptionalFields, shouldWriteObjectField 200 | 201 | type RecordType = type value 202 | w.beginRecord RecordType 203 | value.enumInstanceSerializedFields(fieldName, fieldValue): 204 | when fieldValue isnot JsonVoid: 205 | type 206 | Writer = typeof w 207 | Flavor = Writer.Flavor 208 | when flavorOmitsOptionalFields(Flavor): 209 | if shouldWriteObjectField(fieldValue): 210 | writeObjectField(w, value, fieldName, fieldValue) 211 | w.state = AfterField 212 | else: 213 | writeObjectField(w, value, fieldName, fieldValue) 214 | w.state = AfterField 215 | else: 216 | discard fieldName 217 | w.endRecord() 218 | 219 | proc writeNumber*[F,T](w: var JsonWriter[F], value: JsonNumber[T]) = 220 | if value.sign == JsonSign.Neg: 221 | append '-' 222 | 223 | when T is uint64: 224 | w.stream.writeText value.integer 225 | else: 226 | append value.integer 227 | 228 | if value.fraction.len > 0: 229 | append '.' 230 | append value.fraction 231 | 232 | template writeExp(body: untyped) = 233 | when T is uint64: 234 | if value.exponent > 0: 235 | body 236 | else: 237 | if value.exponent.len > 0: 238 | body 239 | 240 | writeExp: 241 | append 'e' 242 | if value.sign == JsonSign.Neg: 243 | append '-' 244 | when T is uint64: 245 | w.stream.writeText value.exponent 246 | else: 247 | append value.exponent 248 | 249 | proc writeJsonValueRef*[F,T](w: var JsonWriter[F], value: JsonValueRef[T]) = 250 | if value.isNil: 251 | append "null" 252 | return 253 | 254 | case value.kind 255 | of JsonValueKind.String: 256 | w.writeValue(value.strVal) 257 | of JsonValueKind.Number: 258 | w.writeNumber(value.numVal) 259 | of JsonValueKind.Object: 260 | w.beginRecord typeof(value) 261 | for k, v in value.objVal: 262 | w.writeField(k, v) 263 | w.endRecord() 264 | of JsonValueKind.Array: 265 | w.writeArray(value.arrayVal) 266 | of JsonValueKind.Bool: 267 | if value.boolVal: 268 | append "true" 269 | else: 270 | append "false" 271 | of JsonValueKind.Null: 272 | append "null" 273 | 274 | template writeEnumImpl(w: var JsonWriter, value, enumRep) = 275 | mixin writeValue 276 | when enumRep == EnumAsString: 277 | w.writeValue $value 278 | elif enumRep == EnumAsNumber: 279 | w.stream.writeText(value.int) 280 | elif enumRep == EnumAsStringifiedNumber: 281 | w.writeValue $value.int 282 | 283 | template writeValue*(w: var JsonWriter, value: enum) = 284 | # We extract this as a template because 285 | # if we put it into `proc writeValue` below 286 | # the Nim compiler generic cache mechanism 287 | # will mess up with the compile time 288 | # conditional selection 289 | type Flavor = type(w).Flavor 290 | writeEnumImpl(w, value, Flavor.flavorEnumRep()) 291 | 292 | proc writeValue*(w: var JsonWriter, value: auto) {.gcsafe, raises: [IOError].} = 293 | mixin writeValue 294 | 295 | when value is JsonNode: 296 | append if w.hasPrettyOutput: value.pretty 297 | else: $value 298 | 299 | elif value is JsonString: 300 | append string(value) 301 | 302 | elif value is JsonVoid: 303 | discard 304 | 305 | elif value is JsonNumber: 306 | w.writeNumber(value) 307 | 308 | elif value is JsonValueRef: 309 | w.writeJsonValueRef(value) 310 | 311 | elif value is ref: 312 | if value == nil: 313 | append "null" 314 | else: 315 | writeValue(w, value[]) 316 | 317 | elif isStringLike(value): 318 | when value is cstring: 319 | if value == nil: 320 | append "null" 321 | return 322 | 323 | append '"' 324 | 325 | template addPrefixSlash(c) = 326 | append '\\' 327 | append c 328 | 329 | for c in value: 330 | case c 331 | of '\L': addPrefixSlash 'n' 332 | of '\b': addPrefixSlash 'b' 333 | of '\f': addPrefixSlash 'f' 334 | of '\t': addPrefixSlash 't' 335 | of '\r': addPrefixSlash 'r' 336 | of '"' : addPrefixSlash '\"' 337 | of '\0'..'\7': 338 | append "\\u000" 339 | append char(ord('0') + ord(c)) 340 | of '\14'..'\31': 341 | append "\\u00" 342 | # TODO: Should this really use a decimal representation? 343 | # Or perhaps $ord(c) returns hex? 344 | # This is potentially a bug in Nim's json module. 345 | append $ord(c) 346 | of '\\': addPrefixSlash '\\' 347 | else: append c 348 | 349 | append '"' 350 | 351 | elif value is bool: 352 | append if value: "true" else: "false" 353 | 354 | elif value is range: 355 | when low(typeof(value)) < 0: 356 | w.stream.writeText int64(value) 357 | else: 358 | w.stream.writeText uint64(value) 359 | 360 | elif value is SomeInteger: 361 | w.stream.writeText value 362 | 363 | elif value is SomeFloat: 364 | # TODO Implement writeText for floats 365 | # to avoid the allocation here: 366 | append $value 367 | 368 | elif value is (seq or array or openArray) or 369 | (value is distinct and distinctBase(value) is (seq or array or openArray)): 370 | when value is distinct: 371 | w.writeArray(distinctBase value) 372 | else: 373 | w.writeArray(value) 374 | 375 | elif value is (distinct or object or tuple): 376 | mixin flavorUsesAutomaticObjectSerialization 377 | 378 | type Flavor = JsonWriter.Flavor 379 | const isAutomatic = 380 | flavorUsesAutomaticObjectSerialization(Flavor) 381 | 382 | when not isAutomatic: 383 | const typeName = typetraits.name(type value) 384 | {.error: "Please override writeValue for the " & typeName & " type (or import the module where the override is provided)".} 385 | 386 | when value is distinct: 387 | writeRecordValue(w, distinctBase(value, recursive = false)) 388 | else: 389 | writeRecordValue(w, value) 390 | else: 391 | const typeName = typetraits.name(value.type) 392 | {.fatal: "Failed to convert to JSON an unsupported type: " & typeName.} 393 | 394 | proc toJson*(v: auto, pretty = false, typeAnnotations = false): string = 395 | mixin writeValue 396 | 397 | var 398 | s = memoryOutput() 399 | w = JsonWriter[DefaultFlavor].init(s, pretty, typeAnnotations) 400 | w.writeValue v 401 | s.getOutput(string) 402 | 403 | template serializesAsTextInJson*(T: type[enum]) = 404 | template writeValue*(w: var JsonWriter, val: T) = 405 | w.writeValue $val 406 | 407 | template configureJsonSerialization*( 408 | T: type[enum], enumRep: static[EnumRepresentation]) = 409 | proc writeValue*(w: var JsonWriter, 410 | value: T) {.gcsafe, raises: [IOError].} = 411 | writeEnumImpl(w, value, enumRep) 412 | 413 | template configureJsonSerialization*(Flavor: type, 414 | T: type[enum], 415 | enumRep: static[EnumRepresentation]) = 416 | when Flavor is Json: 417 | proc writeValue*(w: var JsonWriter[DefaultFlavor], 418 | value: T) {.gcsafe, raises: [IOError].} = 419 | writeEnumImpl(w, value, enumRep) 420 | else: 421 | proc writeValue*(w: var JsonWriter[Flavor], 422 | value: T) {.gcsafe, raises: [IOError].} = 423 | writeEnumImpl(w, value, enumRep) 424 | -------------------------------------------------------------------------------- /tests/cases/comments.json: -------------------------------------------------------------------------------- 1 | /* comments */ 2 | { 3 | "tasks": [ 4 | { 5 | "label": "nimbus-eth2 build" 6 | } 7 | ] // another commet 8 | } 9 | -------------------------------------------------------------------------------- /tests/nim.cfg: -------------------------------------------------------------------------------- 1 | # json-serialization 2 | # Copyright (c) 2019-2023 Status Research & Development GmbH 3 | # Licensed under either of 4 | # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) 5 | # * MIT license ([LICENSE-MIT](LICENSE-MIT)) 6 | # at your option. 7 | # This file may not be copied, modified, or distributed except according to 8 | # those terms. 9 | 10 | --threads:on 11 | 12 | -------------------------------------------------------------------------------- /tests/test_all.nim: -------------------------------------------------------------------------------- 1 | # json-serialization 2 | # Copyright (c) 2019-2024 Status Research & Development GmbH 3 | # Licensed under either of 4 | # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) 5 | # * MIT license ([LICENSE-MIT](LICENSE-MIT)) 6 | # at your option. 7 | # This file may not be copied, modified, or distributed except according to 8 | # those terms. 9 | 10 | {. warning[UnusedImport]:off .} 11 | 12 | import 13 | test_lexer, 14 | test_serialization, 15 | test_json_flavor, 16 | test_spec, 17 | test_parser, 18 | test_line_col, 19 | test_reader, 20 | test_writer, 21 | test_valueref 22 | -------------------------------------------------------------------------------- /tests/test_json_flavor.nim: -------------------------------------------------------------------------------- 1 | # json-serialization 2 | # Copyright (c) 2019-2023 Status Research & Development GmbH 3 | # Licensed under either of 4 | # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) 5 | # * MIT license ([LICENSE-MIT](LICENSE-MIT)) 6 | # at your option. 7 | # This file may not be copied, modified, or distributed except according to 8 | # those terms. 9 | 10 | import 11 | std/[strutils, options], 12 | unittest2, 13 | results, 14 | serialization, 15 | ../json_serialization/stew/results, 16 | ../json_serialization/std/options, 17 | ../json_serialization 18 | 19 | createJsonFlavor StringyJson 20 | 21 | proc writeValue*( 22 | w: var JsonWriter[StringyJson], val: SomeInteger) {.raises: [IOError].} = 23 | writeValue(w, $val) 24 | 25 | proc readValue*(r: var JsonReader[StringyJson], v: var SomeSignedInt) = 26 | try: 27 | v = type(v) parseBiggestInt readValue(r, string) 28 | except ValueError as err: 29 | r.raiseUnexpectedValue("A signed integer encoded as string " & err.msg) 30 | 31 | proc readValue*(r: var JsonReader[StringyJson], v: var SomeUnsignedInt) = 32 | try: 33 | v = type(v) parseBiggestUInt readValue(r, string) 34 | except ValueError as err: 35 | r.raiseUnexpectedValue("An unsigned integer encoded as string " & err.msg) 36 | 37 | type 38 | Container = object 39 | name: string 40 | x: int 41 | y: uint64 42 | list: seq[int64] 43 | 44 | OptionalFields = object 45 | one: Opt[string] 46 | two: Option[int] 47 | 48 | SpecialTypes = object 49 | one: JsonVoid 50 | two: JsonNumber[uint64] 51 | three: JsonNumber[string] 52 | four: JsonValueRef[uint64] 53 | 54 | ListOnly = object 55 | list: JsonString 56 | 57 | Container.useDefaultSerializationIn StringyJson 58 | 59 | createJsonFlavor OptJson 60 | OptionalFields.useDefaultSerializationIn OptJson 61 | 62 | const 63 | jsonText = """ 64 | { 65 | "one": "this text will gone", 66 | "two": -789.0009E-19, 67 | "three": 999.776000E+33, 68 | "four" : { 69 | "apple": [1, true, "three"], 70 | "banana": { 71 | "chip": 123, 72 | "z": null, 73 | "v": false 74 | } 75 | } 76 | } 77 | 78 | """ 79 | jsonTextWithNullFields = """ 80 | { 81 | "list": null 82 | } 83 | """ 84 | 85 | createJsonFlavor NullyFields, 86 | skipNullFields = true, 87 | requireAllFields = false 88 | 89 | Container.useDefaultSerializationIn NullyFields 90 | ListOnly.useDefaultSerializationIn NullyFields 91 | 92 | suite "Test JsonFlavor": 93 | test "basic test": 94 | let c = Container(name: "c", x: -10, y: 20, list: @[1'i64, 2, 25]) 95 | let encoded = StringyJson.encode(c) 96 | check encoded == """{"name":"c","x":"-10","y":"20","list":["1","2","25"]}""" 97 | 98 | let decoded = StringyJson.decode(encoded, Container) 99 | check decoded == Container(name: "c", x: -10, y: 20, list: @[1, 2, 25]) 100 | 101 | test "optional fields": 102 | let a = OptionalFields(one: Opt.some("hello")) 103 | let b = OptionalFields(two: some(567)) 104 | let c = OptionalFields(one: Opt.some("burn"), two: some(333)) 105 | 106 | let aa = OptJson.encode(a) 107 | check aa == """{"one":"hello"}""" 108 | 109 | let bb = OptJson.encode(b) 110 | check bb == """{"two":567}""" 111 | 112 | let cc = OptJson.encode(c) 113 | check cc == """{"one":"burn","two":333}""" 114 | 115 | test "Write special types": 116 | let vv = Json.decode(jsonText, SpecialTypes) 117 | let xx = Json.encode(vv) 118 | var ww = Json.decode(xx, SpecialTypes) 119 | ww.three.expSign = JsonSign.Pos # the rest of it should identical to vv 120 | check: 121 | ww == vv 122 | xx == """{"two":-789.0009e-19,"three":999.776000e33,"four":{"apple":[1,true,"three"],"banana":{"chip":123,"z":null,"v":false}}}""" 123 | 124 | test "object with null fields": 125 | expect JsonReaderError: 126 | let x = Json.decode(jsonTextWithNullFields, Container) 127 | discard x 128 | 129 | let x = NullyFields.decode(jsonTextWithNullFields, Container) 130 | check x.list.len == 0 131 | 132 | # field should not processed at all 133 | let y = NullyFields.decode(jsonTextWithNullFields, ListOnly) 134 | check y.list.string.len == 0 135 | 136 | test "Enum value representation primitives": 137 | when NullyFields.flavorEnumRep() == EnumAsString: 138 | check true 139 | elif NullyFields.flavorEnumRep() == EnumAsNumber: 140 | check false 141 | elif NullyFields.flavorEnumRep() == EnumAsStringifiedNumber: 142 | check false 143 | 144 | NullyFields.flavorEnumRep(EnumAsNumber) 145 | when NullyFields.flavorEnumRep() == EnumAsString: 146 | check false 147 | elif NullyFields.flavorEnumRep() == EnumAsNumber: 148 | check true 149 | elif NullyFields.flavorEnumRep() == EnumAsStringifiedNumber: 150 | check false 151 | 152 | NullyFields.flavorEnumRep(EnumAsStringifiedNumber) 153 | when NullyFields.flavorEnumRep() == EnumAsString: 154 | check false 155 | elif NullyFields.flavorEnumRep() == EnumAsNumber: 156 | check false 157 | elif NullyFields.flavorEnumRep() == EnumAsStringifiedNumber: 158 | check true 159 | 160 | test "Enum value representation of custom flavor": 161 | type 162 | ExoticFruits = enum 163 | DragonFruit 164 | SnakeFruit 165 | StarFruit 166 | 167 | NullyFields.flavorEnumRep(EnumAsNumber) 168 | let u = NullyFields.encode(DragonFruit) 169 | check u == "0" 170 | 171 | NullyFields.flavorEnumRep(EnumAsString) 172 | let v = NullyFields.encode(SnakeFruit) 173 | check v == "\"SnakeFruit\"" 174 | 175 | NullyFields.flavorEnumRep(EnumAsStringifiedNumber) 176 | let w = NullyFields.encode(StarFruit) 177 | check w == "\"2\"" 178 | 179 | test "EnumAsString of custom flavor": 180 | type 181 | Fruit = enum 182 | Banana = "BaNaNa" 183 | Apple = "ApplE" 184 | JackFruit = "VVV" 185 | 186 | NullyFields.flavorEnumRep(EnumAsString) 187 | let u = NullyFields.encode(Banana) 188 | check u == "\"BaNaNa\"" 189 | 190 | let v = NullyFields.encode(Apple) 191 | check v == "\"ApplE\"" 192 | 193 | let w = NullyFields.encode(JackFruit) 194 | check w == "\"VVV\"" 195 | 196 | NullyFields.flavorEnumRep(EnumAsStringifiedNumber) 197 | let x = NullyFields.encode(JackFruit) 198 | check x == "\"2\"" 199 | 200 | NullyFields.flavorEnumRep(EnumAsNumber) 201 | let z = NullyFields.encode(Banana) 202 | check z == "0" -------------------------------------------------------------------------------- /tests/test_line_col.nim: -------------------------------------------------------------------------------- 1 | # json-serialization 2 | # Copyright (c) 2023 Status Research & Development GmbH 3 | # Licensed under either of 4 | # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) 5 | # * MIT license ([LICENSE-MIT](LICENSE-MIT)) 6 | # at your option. 7 | # This file may not be copied, modified, or distributed except according to 8 | # those terms. 9 | 10 | import 11 | faststreams, 12 | unittest2 13 | 14 | # we want to test lexer internals 15 | # hence use include instead of import 16 | include 17 | ../json_serialization/lexer 18 | 19 | type 20 | TestCase = object 21 | line: int 22 | col: int 23 | text: string 24 | flags: JsonReaderFlags 25 | conf: JsonReaderConf 26 | 27 | func tc(line: int, col: int, text: string): TestCase = 28 | TestCase( 29 | line: line, 30 | col: col, 31 | text: text, 32 | flags: defaultJsonReaderFlags, 33 | conf: defaultJsonReaderConf, 34 | ) 35 | 36 | func tc(line: int, col: int, text: string, flags: JsonReaderFlags): TestCase = 37 | TestCase( 38 | line: line, 39 | col: col, 40 | text: text, 41 | flags: flags, 42 | conf: defaultJsonReaderConf, 43 | ) 44 | 45 | when false: 46 | func tc(line: int, col: int, text: string, conf: JsonReaderConf): TestCase = 47 | TestCase( 48 | line: line, 49 | col: col, 50 | text: text, 51 | flags: defaultJsonReaderFlags, 52 | conf: conf, 53 | ) 54 | 55 | func noComment(): JsonReaderFlags = 56 | result = defaultJsonReaderFlags 57 | result.excl JsonReaderFlag.allowComments 58 | 59 | func noTrailingComma(): JsonReaderFlags = 60 | result = defaultJsonReaderFlags 61 | result.excl JsonReaderFlag.trailingComma 62 | 63 | const testCases = [ 64 | tc(2, 19, """ 65 | { 66 | "a" : 1234.567 // comments 67 | } 68 | """, noComment()), 69 | 70 | tc(2, 19, """ 71 | { 72 | "a" : 1234.567 /* comments */ 73 | } 74 | """, noComment()), 75 | 76 | tc(4, 3, """ 77 | { 78 | "a" : 1234.567 /* comments 79 | } 80 | """), 81 | 82 | tc(2, 8, """ 83 | { 84 | "a" 1234.567 85 | } 86 | """), 87 | 88 | tc(3, 3, """ 89 | { 90 | 91 | , 92 | 93 | """), 94 | 95 | tc(4, 1, """ 96 | { 97 | "a": 98 | 1 , 99 | } 100 | """, noTrailingComma()), 101 | 102 | tc(3, 4, """ 103 | [ 104 | 105 | , 106 | 107 | """), 108 | 109 | tc(2, 3, """ 110 | [ 111 | b 112 | ] 113 | """), 114 | 115 | tc(4, 1, """ 116 | [ 117 | 1 118 | , 119 | ] 120 | """, noTrailingComma()), 121 | ] 122 | 123 | suite "Test line col": 124 | for i, tc in testCases: 125 | test $i: 126 | var stream = unsafeMemoryInput(tc.text) 127 | var lex = init(JsonLexer, stream, tc.flags, tc.conf) 128 | var value: JsonValueRef[uint64] 129 | lex.scanValue(value) 130 | check: 131 | lex.err != errNone 132 | lex.line == tc.line 133 | lex.tokenStartCol == tc.col 134 | -------------------------------------------------------------------------------- /tests/test_parser.nim: -------------------------------------------------------------------------------- 1 | # json-serialization 2 | # Copyright (c) 2023 Status Research & Development GmbH 3 | # Licensed under either of 4 | # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) 5 | # * MIT license ([LICENSE-MIT](LICENSE-MIT)) 6 | # at your option. 7 | # This file may not be copied, modified, or distributed except according to 8 | # those terms. 9 | 10 | import 11 | std/[strutils, os, json], 12 | faststreams, 13 | unittest2, 14 | ../json_serialization/parser, 15 | ../json_serialization/value_ops, 16 | ./utils 17 | 18 | createJsonFlavor NullFields, 19 | skipNullFields = true 20 | 21 | func toReader(input: string): JsonReader[DefaultFlavor] = 22 | var stream = unsafeMemoryInput(input) 23 | JsonReader[DefaultFlavor].init(stream) 24 | 25 | func toReaderNullFields(input: string): JsonReader[NullFields] = 26 | var stream = unsafeMemoryInput(input) 27 | JsonReader[NullFields].init(stream) 28 | 29 | suite "Custom iterators": 30 | test "customIntValueIt": 31 | var value: int 32 | var r = toReader"77663399" 33 | r.customIntValueIt: 34 | value = value * 10 + it 35 | check value == 77663399 36 | 37 | test "customNumberValueIt": 38 | var value: int 39 | var frac: int 40 | var exponent: int 41 | var r = toReader"123.456e789" 42 | r.customNumberValueIt: 43 | if part == IntegerPart: 44 | value = value * 10 + it 45 | elif part == FractionPart: 46 | frac = frac * 10 + it 47 | elif part == ExponentPart: 48 | exponent = exponent * 10 + it 49 | check: 50 | value == 123 51 | frac == 456 52 | exponent == 789 53 | 54 | test "customStringValueIt": 55 | var text: string 56 | var r = toReader "\"hello \\t world\"" 57 | r.customStringValueIt: 58 | text.add it 59 | 60 | expect JsonReaderError: 61 | r.customStringValueIt(10): 62 | text.add it 63 | 64 | check text == "hello \t world" 65 | 66 | suite "Public parser": 67 | test "parseArray": 68 | proc parse(r: var JsonReader, list: var seq[bool]) 69 | {.gcsafe, raises: [IOError, JsonReaderError].} = 70 | r.parseArray: 71 | list.add r.parseBool() 72 | 73 | var r = toReader"[true, true, false]" 74 | var list: seq[bool] 75 | r.parse(list) 76 | check list.len == 3 77 | check list == @[true, true, false] 78 | 79 | test "parseArray with idx": 80 | proc parse(r: var JsonReader, list: var seq[bool]) 81 | {.gcsafe, raises: [IOError, JsonReaderError].} = 82 | r.parseArray(i): 83 | list.add (i mod 2) == 0 84 | list.add r.parseBool() 85 | 86 | var r = toReader"[true, true, false]" 87 | var list: seq[bool] 88 | r.parse(list) 89 | check list.len == 6 90 | check list == @[true, true, false, true, true, false] 91 | 92 | test "parseObject": 93 | type 94 | Duck = object 95 | id: string 96 | ok: bool 97 | 98 | proc parse(r: var JsonReader, list: var seq[Duck]) = 99 | r.parseObject(key): 100 | list.add Duck( 101 | id: key, 102 | ok: r.parseBool() 103 | ) 104 | 105 | var r = toReader "{\"a\": true, \"b\": false}" 106 | var list: seq[Duck] 107 | r.parse(list) 108 | 109 | check list.len == 2 110 | check list == @[Duck(id:"a", ok:true), Duck(id:"b", ok:false)] 111 | 112 | test "parseNumber uint64": 113 | var r = toReader "-1234.0007e+88" 114 | let val = r.parseNumber(uint64) 115 | check: 116 | val.sign == JsonSign.Neg 117 | val.integer == 1234 118 | val.fraction == "0007" 119 | val.expSign == JsonSign.Pos 120 | val.exponent == 88 121 | 122 | test "parseNumber string": 123 | var r = toReader "-1234.0007e+88" 124 | let val = r.parseNumber(string) 125 | check: 126 | val.sign == JsonSign.Neg 127 | val.integer == "1234" 128 | val.fraction == "0007" 129 | val.expSign == JsonSign.Pos 130 | val.exponent == "88" 131 | 132 | func highPlus(T: type): string = 133 | result = $(T.high) 134 | result[^1] = char(result[^1].int + 1) 135 | 136 | func lowMin(T: type): string = 137 | result = $(T.low) 138 | result[^1] = char(result[^1].int + 1) 139 | 140 | template testParseIntI(T: type) = 141 | var r = toReader $(T.high) 142 | var val = r.parseInt(T) 143 | check val == T.high 144 | 145 | expect JsonReaderError: 146 | var r = toReader highPlus(T) 147 | let val = r.parseInt(T) 148 | discard val 149 | 150 | r = toReader $(T.low) 151 | val = r.parseInt(T) 152 | check val == T.low 153 | 154 | expect JsonReaderError: 155 | var r = toReader lowMin(T) 156 | let val = r.parseInt(T) 157 | discard val 158 | 159 | template testParseIntU(T: type) = 160 | var r = toReader $(T.high) 161 | let val = r.parseInt(T) 162 | check val == T.high 163 | 164 | expect JsonReaderError: 165 | var r = toReader highPlus(T) 166 | let val = r.parseInt(T) 167 | discard val 168 | 169 | test "parseInt uint8": 170 | testParseIntU(uint8) 171 | 172 | test "parseInt int8": 173 | testParseIntI(int8) 174 | 175 | test "parseInt uint16": 176 | testParseIntU(uint16) 177 | 178 | test "parseInt int16": 179 | testParseIntI(int16) 180 | 181 | test "parseInt uint32": 182 | testParseIntU(uint32) 183 | 184 | test "parseInt int32": 185 | testParseIntI(int32) 186 | 187 | test "parseInt uint64": 188 | testParseIntU(uint64) 189 | 190 | test "parseInt int64": 191 | testParseIntI(int64) 192 | 193 | test "parseInt portable overflow": 194 | expect JsonReaderError: 195 | var r = toReader $(minPortableInt - 1) 196 | let val = r.parseInt(int64, true) 197 | discard val 198 | 199 | expect JsonReaderError: 200 | var r = toReader $(maxPortableInt + 1) 201 | let val = r.parseInt(int64, true) 202 | discard val 203 | 204 | when sizeof(int) == 8: 205 | expect JsonReaderError: 206 | var r = toReader $(minPortableInt - 1) 207 | let val = r.parseInt(int, true) 208 | discard val 209 | 210 | expect JsonReaderError: 211 | var r = toReader $(maxPortableInt + 1) 212 | let val = r.parseInt(int, true) 213 | discard val 214 | 215 | test "parseFloat": 216 | var 217 | r = toReader "56.009" 218 | val = r.parseFloat(float64) 219 | check val == 56.009 220 | 221 | r = toReader "-56.009e6" 222 | val = r.parseFloat(float64) 223 | check val == -56009000.0 224 | 225 | template testParseAsString(fileName: string) = 226 | try: 227 | var stream = memFileInput(fileName) 228 | var r = JsonReader[DefaultFlavor].init(stream) 229 | let val = r.parseAsString() 230 | var xr = toReader val.string 231 | let xval = xr.parseAsString() 232 | check val == xval 233 | except JsonReaderError as ex: 234 | debugEcho ex.formatMsg(fileName) 235 | check false 236 | 237 | test "parseAsString": 238 | for fileName in walkDirRec(transformPath): 239 | let (_, name) = fileName.splitPath() 240 | if name notin allowedToFail: 241 | testParseAsString(fileName) 242 | 243 | for fileName in walkDirRec(parsingPath): 244 | let (_, name) = fileName.splitPath() 245 | if name.startsWith("y_"): 246 | testParseAsString(fileName) 247 | # test cases starts with i_ are allowed to 248 | # fail or success depending on the implementation details 249 | elif name.startsWith("i_"): 250 | if name notin allowedToFail: 251 | testParseAsString(fileName) 252 | 253 | test "parseAsString of null fields": 254 | var r = toReaderNullFields("""{"something":null, "bool":null, "string":null}""") 255 | let res = r.parseAsString() 256 | check res.string == """{"something":null,"bool":null,"string":null}""" 257 | 258 | var y = toReader("""{"something":null, "bool":null, "string":null}""") 259 | let yy = y.parseAsString() 260 | check yy.string == """{"something":null,"bool":null,"string":null}""" 261 | 262 | proc execParseObject(r: var JsonReader): int = 263 | r.parseObject(key): 264 | discard key 265 | let val = r.parseAsString() 266 | discard val 267 | inc result 268 | 269 | test "parseObject of null fields": 270 | var r = toReaderNullFields("""{"something":null, "bool":true, "string":null}""") 271 | check execParseObject(r) == 1 272 | 273 | var y = toReader("""{"something":null,"bool":true,"string":"moon"}""") 274 | check execParseObject(y) == 3 275 | 276 | var z = toReaderNullFields("""{"something":null,"bool":true,"string":"moon"}""") 277 | check execParseObject(z) == 2 278 | 279 | test "parseJsonNode of null fields": 280 | var r = toReaderNullFields("""{"something":null, "bool":true, "string":null}""") 281 | let n = r.parseJsonNode() 282 | check: 283 | n["something"].kind == JNull 284 | n["bool"].kind == JBool 285 | n["string"].kind == JNull 286 | 287 | var y = toReader("""{"something":null,"bool":true,"string":"moon"}""") 288 | let z = y.parseJsonNode() 289 | check: 290 | z["something"].kind == JNull 291 | z["bool"].kind == JBool 292 | z["string"].kind == JString 293 | 294 | test "parseValue of null fields": 295 | var r = toReaderNullFields("""{"something":null, "bool":true, "string":null}""") 296 | let n = r.parseValue(uint64) 297 | check: 298 | n["something"].kind == JsonValueKind.Null 299 | n["bool"].kind == JsonValueKind.Bool 300 | n["string"].kind == JsonValueKind.Null 301 | 302 | var y = toReader("""{"something":null,"bool":true,"string":"moon"}""") 303 | let z = y.parseValue(uint64) 304 | check: 305 | z["something"].kind == JsonValueKind.Null 306 | z["bool"].kind == JsonValueKind.Bool 307 | z["string"].kind == JsonValueKind.String 308 | 309 | test "JsonValueRef comparison": 310 | var x = JsonValueRef[uint64](kind: JsonValueKind.Null) 311 | var n = JsonValueRef[uint64](nil) 312 | check x != n 313 | check n != x 314 | check x == x 315 | check n == n 316 | 317 | const 318 | jsonText = """ 319 | 320 | { 321 | "string" : "hello world", 322 | "number" : -123.456, 323 | "int": 789, 324 | "bool" : true , 325 | "null" : null , 326 | "array" : [ true, 567.89 , "string in array" , null, [ 123 ] ], 327 | "object" : { 328 | "abc" : 444.008 , 329 | "def": false 330 | } 331 | } 332 | 333 | """ 334 | 335 | suite "Parse to runtime dynamic structure": 336 | test "parse to json node": 337 | var r = toReader(jsonText) 338 | let n = r.parseJsonNode() 339 | check: 340 | n["string"].str == "hello world" 341 | n["number"].fnum == -123.456 342 | n["int"].num == 789 343 | n["bool"].bval == true 344 | n["array"].len == 5 345 | n["array"][0].bval == true 346 | n["array"][1].fnum == 567.89 347 | n["array"][2].str == "string in array" 348 | n["array"][3].kind == JNull 349 | n["array"][4].kind == JArray 350 | n["array"][4].len == 1 351 | n["array"][4][0].num == 123 352 | n["object"]["abc"].fnum == 444.008 353 | n["object"]["def"].bval == false 354 | 355 | test "parseValue": 356 | var r = toReader(jsonText) 357 | let n = r.parseValue(uint64) 358 | check: 359 | n["string"].strVal == "hello world" 360 | n["bool"].boolVal == true 361 | n["array"].len == 5 362 | n["array"][0].boolVal == true 363 | n["array"][2].strVal == "string in array" 364 | n["array"][3].kind == JsonValueKind.Null 365 | n["array"][4].kind == JsonValueKind.Array 366 | n["array"][4].len == 1 367 | n["object"]["def"].boolVal == false 368 | 369 | test "nim v2 regression #23233": 370 | # Nim compiler bug #23233 will prevent 371 | # compilation if both JsonValueRef[uint64] and JsonValueRef[string] 372 | # are instantiated at together. 373 | var r1 = toReader(jsonText) 374 | let n1 = r1.parseValue(uint64) 375 | discard n1 376 | 377 | var r2 = toReader(jsonText) 378 | let n2 = r2.parseValue(string) 379 | discard n2 380 | -------------------------------------------------------------------------------- /tests/test_reader.nim: -------------------------------------------------------------------------------- 1 | # json-serialization 2 | # Copyright (c) 2023 Status Research & Development GmbH 3 | # Licensed under either of 4 | # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) 5 | # * MIT license ([LICENSE-MIT](LICENSE-MIT)) 6 | # at your option. 7 | # This file may not be copied, modified, or distributed except according to 8 | # those terms. 9 | 10 | import 11 | std/json, 12 | faststreams, 13 | unittest2, 14 | serialization, 15 | ../json_serialization/reader 16 | 17 | createJsonFlavor NullFields, 18 | skipNullFields = true 19 | 20 | func toReader(input: string): JsonReader[DefaultFlavor] = 21 | var stream = unsafeMemoryInput(input) 22 | JsonReader[DefaultFlavor].init(stream) 23 | 24 | func toReaderNullFields(input: string): JsonReader[NullFields] = 25 | var stream = unsafeMemoryInput(input) 26 | JsonReader[NullFields].init(stream) 27 | 28 | const 29 | jsonText = """ 30 | 31 | { 32 | "string" : "hello world", 33 | "number" : -123.456, 34 | "int": 789, 35 | "bool" : true , 36 | "null" : null , 37 | "array" : [ true, 567.89 , "string in array" , null, [ 123 ] ] 38 | } 39 | 40 | """ 41 | 42 | jsonText2 = """ 43 | 44 | { 45 | "string" : 25, 46 | "number" : 123, 47 | "int": 789, 48 | "bool" : 22 , 49 | "null" : 0 , 50 | } 51 | 52 | """ 53 | 54 | jsonText3 = """ 55 | 56 | { 57 | "one": [1,true,null], 58 | "two": 123, 59 | "three": "help", 60 | "four": "012", 61 | "five": "345", 62 | "six": true, 63 | "seven": 555, 64 | "eight": "mTwo", 65 | "nine": 77, 66 | "ten": 88, 67 | "eleven": 88.55, 68 | "twelve": [true, false], 69 | "thirteen": [3,4], 70 | "fourteen": { 71 | "one": "world", 72 | "two": false 73 | } 74 | } 75 | 76 | """ 77 | 78 | type 79 | MasterEnum = enum 80 | mOne 81 | mTwo 82 | mThree 83 | 84 | SecondObject = object 85 | one: string 86 | two: bool 87 | 88 | MasterReader = object 89 | one: JsonString 90 | two: JsonNode 91 | three: string 92 | four: seq[char] 93 | five: array[3, char] 94 | six: bool 95 | seven: ref int 96 | eight: MasterEnum 97 | nine: int32 98 | ten: float64 99 | eleven: float64 100 | twelve: seq[bool] 101 | thirteen: array[mTwo..mThree, int] 102 | fourteen: SecondObject 103 | 104 | SpecialTypes = object 105 | `string`: JsonVoid 106 | `number`: JsonNumber[uint64] 107 | `int` : JsonNumber[string] 108 | `bool` : JsonValueRef[uint64] 109 | `null` : JsonValueRef[uint64] 110 | `array` : JsonString 111 | 112 | suite "JsonReader basic test": 113 | test "readArray iterator": 114 | var r = toReader "[false, true, false]" 115 | var list: seq[bool] 116 | for x in r.readArray(bool): 117 | list.add x 118 | check list == @[false, true, false] 119 | 120 | test "readObjectFields iterator": 121 | var r = toReader jsonText 122 | var keys: seq[string] 123 | var vals: seq[string] 124 | for key in r.readObjectFields(string): 125 | keys.add key 126 | let val = r.parseAsString() 127 | vals.add val.string 128 | check keys == @["string", "number", "int", "bool", "null", "array"] 129 | 130 | test "readObject iterator": 131 | var r = toReader jsonText2 132 | var keys: seq[string] 133 | var vals: seq[uint64] 134 | for k, v in r.readObject(string, uint64): 135 | keys.add k 136 | vals.add v 137 | 138 | check: 139 | keys == @["string", "number", "int", "bool", "null"] 140 | vals == @[25'u64, 123, 789, 22, 0] 141 | 142 | test "readValue": 143 | try: 144 | var r = toReader jsonText3 145 | var valOrig: MasterReader 146 | r.readValue(valOrig) 147 | # workaround for https://github.com/nim-lang/Nim/issues/24274 148 | let val = valOrig 149 | check: 150 | val.one == JsonString("[1,true,null]") 151 | val.two.num == 123 152 | val.three == "help" 153 | val.four == "012" 154 | val.five == "345" 155 | val.six == true 156 | val.seven[] == 555 157 | val.eight == mTwo 158 | val.nine == 77 159 | val.ten == 88 160 | val.eleven == 88.55 161 | val.twelve == [true, false] 162 | val.thirteen == [3,4] 163 | val.fourteen == SecondObject(one: "world", two: false) 164 | 165 | except JsonReaderError as ex: 166 | debugEcho ex.formatMsg("jsonText3") 167 | check false 168 | 169 | test "Special Types": 170 | var r = toReader jsonText 171 | var val: SpecialTypes 172 | r.readValue(val) 173 | 174 | check: 175 | val.`number`.sign == JsonSign.Neg 176 | val.`number`.integer == 123 177 | val.`number`.fraction == "456" 178 | val.`int`.integer == "789" 179 | val.`bool`.kind == JsonValueKind.Bool 180 | val.`bool`.boolVal == true 181 | val.`null`.kind == JsonValueKind.Null 182 | val.`array`.string == """[true,567.89,"string in array",null,[123]]""" 183 | 184 | proc execReadObjectFields(r: var JsonReader): int = 185 | for key in r.readObjectFields(): 186 | let val = r.parseAsString() 187 | discard val 188 | inc result 189 | 190 | test "readObjectFields of null fields": 191 | var r = toReaderNullFields("""{"something":null, "bool":true, "string":null}""") 192 | check execReadObjectFields(r) == 1 193 | 194 | var y = toReader("""{"something":null,"bool":true,"string":"moon"}""") 195 | check execReadObjectFields(y) == 3 196 | 197 | var z = toReaderNullFields("""{"something":null,"bool":true,"string":"moon"}""") 198 | check execReadObjectFields(z) == 2 199 | 200 | proc execReadObject(r: var JsonReader): int = 201 | for k, v in r.readObject(string, int): 202 | inc result 203 | 204 | test "readObjectFields of null fields": 205 | var r = toReaderNullFields("""{"something":null, "bool":123, "string":null}""") 206 | check execReadObject(r) == 1 207 | 208 | expect JsonReaderError: 209 | var y = toReader("""{"something":null,"bool":78,"string":345}""") 210 | check execReadObject(y) == 3 211 | 212 | var z = toReaderNullFields("""{"something":null,"bool":999,"string":100}""") 213 | check execReadObject(z) == 2 214 | 215 | test "readValue of array": 216 | var r = toReader "[false, true, false]" 217 | check r.readValue(array[3, bool]) == [false, true, false] 218 | 219 | test "readValue of array error": 220 | var r = toReader "[false, true, false]" 221 | expect JsonReaderError: 222 | discard r.readValue(array[2, bool]) 223 | -------------------------------------------------------------------------------- /tests/test_spec.nim: -------------------------------------------------------------------------------- 1 | # json-serialization 2 | # Copyright (c) 2023 Status Research & Development GmbH 3 | # Licensed under either of 4 | # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) 5 | # * MIT license ([LICENSE-MIT](LICENSE-MIT)) 6 | # at your option. 7 | # This file may not be copied, modified, or distributed except according to 8 | # those terms. 9 | 10 | import 11 | std/[strutils, os], 12 | faststreams, 13 | ./utils 14 | 15 | # we want to test lexer internals 16 | # hence use include instead of import 17 | include 18 | ../json_serialization/lexer 19 | 20 | proc yCase(fileName, name: string): bool {.raises:[IOError].} = 21 | var stream = memFileInput(fileName) 22 | var lex = init(JsonLexer, stream) 23 | var value: string 24 | lex.scanValue(value) 25 | if lex.err != errNone: 26 | debugEcho name, " FAIL ", lex.err 27 | debugEcho value 28 | return false 29 | true 30 | 31 | proc nCase(fileName, name: string): bool {.raises:[IOError].} = 32 | var stream = memFileInput(fileName) 33 | var lex = init(JsonLexer, stream, {}) 34 | var value: string 35 | lex.scanValue(value) 36 | discard lex.nonws() 37 | if lex.ok and lex.readable: 38 | # contains garbage 39 | return true 40 | if lex.err == errNone: 41 | debugEcho name, " FAIL, should error" 42 | debugEcho value 43 | return false 44 | true 45 | 46 | for fileName in walkDirRec(parsingPath): 47 | let (_, name) = fileName.splitPath() 48 | if name.startsWith("y_"): 49 | doAssert yCase(fileName, name) 50 | elif name.startsWith("n_"): 51 | doAssert nCase(fileName, name) 52 | # test cases starts with i_ are allowed to 53 | # fail or success depending on the implementation details 54 | elif name.startsWith("i_"): 55 | if name notin allowedToFail: 56 | doAssert yCase(fileName, name) 57 | 58 | for fileName in walkDirRec(transformPath): 59 | let (_, name) = fileName.splitPath() 60 | if name notin allowedToFail: 61 | doAssert yCase(fileName, name) 62 | -------------------------------------------------------------------------------- /tests/test_valueref.nim: -------------------------------------------------------------------------------- 1 | # json-serialization 2 | # Copyright (c) 2024 Status Research & Development GmbH 3 | # Licensed under either of 4 | # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) 5 | # * MIT license ([LICENSE-MIT](LICENSE-MIT)) 6 | # at your option. 7 | # This file may not be copied, modified, or distributed except according to 8 | # those terms. 9 | 10 | import 11 | unittest2, 12 | ../json_serialization, 13 | ../json_serialization/value_ops 14 | 15 | func jsonBool(x: bool): JsonValueRef[uint64] = 16 | JsonValueRef[uint64](kind: JsonValueKind.Bool, boolVal: x) 17 | 18 | func jsonNull(): JsonValueRef[uint64] = 19 | JsonValueRef[uint64](kind: JsonValueKind.Null) 20 | 21 | suite "Test JsonValueRef": 22 | let objA = JsonValueRef[uint64]( 23 | kind: JsonValueKind.Object, 24 | objVal: [ 25 | ("a", jsonBool(true)), 26 | ].toOrderedTable 27 | ) 28 | 29 | let objA2 = JsonValueRef[uint64]( 30 | kind: JsonValueKind.Object, 31 | objVal: [ 32 | ("a", jsonBool(true)), 33 | ].toOrderedTable 34 | ) 35 | 36 | let objABNull = JsonValueRef[uint64]( 37 | kind: JsonValueKind.Object, 38 | objVal: [ 39 | ("a", jsonBool(true)), 40 | ("b", jsonNull()) 41 | ].toOrderedTable 42 | ) 43 | 44 | let objAB = JsonValueRef[uint64]( 45 | kind: JsonValueKind.Object, 46 | objVal: [ 47 | ("a", jsonBool(true)), 48 | ("b", jsonBool(true)) 49 | ].toOrderedTable 50 | ) 51 | 52 | let objInArrayA = JsonValueRef[uint64]( 53 | kind: JsonValueKind.Array, 54 | arrayVal: @[ 55 | objA 56 | ] 57 | ) 58 | 59 | let objInArrayA2 = JsonValueRef[uint64]( 60 | kind: JsonValueKind.Array, 61 | arrayVal: @[ 62 | objA2 63 | ] 64 | ) 65 | 66 | let objInArrayAB = JsonValueRef[uint64]( 67 | kind: JsonValueKind.Array, 68 | arrayVal: @[ 69 | objAB 70 | ] 71 | ) 72 | 73 | let objInArrayABNull = JsonValueRef[uint64]( 74 | kind: JsonValueKind.Array, 75 | arrayVal: @[ 76 | objABNull 77 | ] 78 | ) 79 | 80 | let objInObjA = JsonValueRef[uint64]( 81 | kind: JsonValueKind.Object, 82 | objVal: [ 83 | ("x", objA) 84 | ].toOrderedTable 85 | ) 86 | 87 | let objInObjA2 = JsonValueRef[uint64]( 88 | kind: JsonValueKind.Object, 89 | objVal: [ 90 | ("x", objA2) 91 | ].toOrderedTable 92 | ) 93 | 94 | let objInObjAB = JsonValueRef[uint64]( 95 | kind: JsonValueKind.Object, 96 | objVal: [ 97 | ("x", objAB) 98 | ].toOrderedTable 99 | ) 100 | 101 | let objInObjABNull = JsonValueRef[uint64]( 102 | kind: JsonValueKind.Object, 103 | objVal: [ 104 | ("x", objABNull) 105 | ].toOrderedTable 106 | ) 107 | 108 | test "Test table keys equality": 109 | check objA != objAB 110 | check objA == objA2 111 | check objA != objABNull 112 | check objAB != objABNull 113 | 114 | check objInArrayA != objInArrayAB 115 | check objInArrayA != objInArrayABNull 116 | check objInArrayA == objInArrayA2 117 | check objInArrayAB != objInArrayABNull 118 | 119 | check objInObjA != objInObjAB 120 | check objInObjA != objInObjABNull 121 | check objInObjA == objInObjA2 122 | check objInObjAB != objInObjABNull 123 | 124 | test "Test compare": 125 | check compare(objA, objAB) == false 126 | check compare(objA, objA2) == true 127 | check compare(objA, objABNull) == true 128 | check compare(objAB, objABNull) == false 129 | 130 | check compare(objInArrayA, objInArrayAB) == false 131 | check compare(objInArrayA, objInArrayABNull) == true 132 | check compare(objInArrayA, objInArrayA2) == true 133 | check compare(objInArrayAB, objInArrayABNull) == false 134 | 135 | check compare(objInObjA, objInObjAB) == false 136 | check compare(objInObjA, objInObjABNull) == true 137 | check compare(objInObjA, objInObjA2) == true 138 | check compare(objInObjAB, objInObjABNull) == false 139 | -------------------------------------------------------------------------------- /tests/test_vectors/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Nicolas Seriot 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/test_vectors/README.md: -------------------------------------------------------------------------------- 1 | # JSON Parsing Test Suite 2 | A comprehensive test suite for RFC 8259 compliant JSON parsers 3 | 4 | This repository was created as an appendix to the article [Parsing JSON is a Minefield 💣](http://seriot.ch/parsing_json.php). 5 | 6 | **/parsers/** 7 | 8 | This directory contains several parsers and tiny wrappers to turn the parsers into JSON validators, by returning a specific value. 9 | 10 | - `0` the parser did accept the content 11 | - `1` the parser did reject the content 12 | - `>1` the process did crash 13 | - `timeout` happens after 5 seconds 14 | 15 | **/test\_parsing/** 16 | 17 | The name of these files tell if their contents should be accepted or rejected. 18 | 19 | - `y_` content must be accepted by parsers 20 | - `n_` content must be rejected by parsers 21 | - `i_` parsers are free to accept or reject content 22 | 23 | **/test\_transform/** 24 | 25 | These files contain weird structures and characters that parsers may understand differently, eg: 26 | 27 | - huge numbers 28 | - dictionaries with similar keys 29 | - NULL characters 30 | - escaped invalid strings 31 | 32 | These files were used to produce `results/transform.html`. 33 | 34 | **/run_tests.py** 35 | 36 | Run all parsers with all files: 37 | 38 | $ python3 run_tests.py 39 | 40 | Run all parsers with a specific file: 41 | 42 | $ python3 run_tests.py file.json 43 | 44 | Run specific parsers with all files: 45 | 46 | $ echo '["Python 2.7.10", "Python 3.5.2"]' > python_only.json 47 | $ python3 run_tests.py --filter=python_only.json 48 | 49 | The script writes logs in `results/logs.txt`. 50 | 51 | The script then reads `logs.txt` and generates `results/parsing.html`. 52 | 53 | **/results/** 54 | 55 | JSON Parsing Tests 56 | -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_number_double_huge_neg_exp.json: -------------------------------------------------------------------------------- 1 | [123.456e-789] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_number_huge_exp.json: -------------------------------------------------------------------------------- 1 | [0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_number_neg_int_huge_exp.json: -------------------------------------------------------------------------------- 1 | [-1e+9999] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_number_pos_double_huge_exp.json: -------------------------------------------------------------------------------- 1 | [1.5e+9999] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_number_real_neg_overflow.json: -------------------------------------------------------------------------------- 1 | [-123123e100000] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_number_real_pos_overflow.json: -------------------------------------------------------------------------------- 1 | [123123e100000] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_number_real_underflow.json: -------------------------------------------------------------------------------- 1 | [123e-10000000] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_number_too_big_neg_int.json: -------------------------------------------------------------------------------- 1 | [-123123123123123123123123123123] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_number_too_big_pos_int.json: -------------------------------------------------------------------------------- 1 | [100000000000000000000] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_number_very_big_negative_int.json: -------------------------------------------------------------------------------- 1 | [-237462374673276894279832749832423479823246327846] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_object_key_lone_2nd_surrogate.json: -------------------------------------------------------------------------------- 1 | {"\uDFAA":0} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_string_1st_surrogate_but_2nd_missing.json: -------------------------------------------------------------------------------- 1 | ["\uDADA"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_string_1st_valid_surrogate_2nd_invalid.json: -------------------------------------------------------------------------------- 1 | ["\uD888\u1234"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_string_UTF-16LE_with_BOM.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status-im/nim-json-serialization/2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf/tests/test_vectors/test_parsing/i_string_UTF-16LE_with_BOM.json -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_string_UTF-8_invalid_sequence.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status-im/nim-json-serialization/2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf/tests/test_vectors/test_parsing/i_string_UTF-8_invalid_sequence.json -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_string_UTF8_surrogate_U+D800.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status-im/nim-json-serialization/2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf/tests/test_vectors/test_parsing/i_string_UTF8_surrogate_U+D800.json -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_string_incomplete_surrogate_and_escape_valid.json: -------------------------------------------------------------------------------- 1 | ["\uD800\n"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_string_incomplete_surrogate_pair.json: -------------------------------------------------------------------------------- 1 | ["\uDd1ea"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_string_incomplete_surrogates_escape_valid.json: -------------------------------------------------------------------------------- 1 | ["\uD800\uD800\n"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_string_invalid_lonely_surrogate.json: -------------------------------------------------------------------------------- 1 | ["\ud800"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_string_invalid_surrogate.json: -------------------------------------------------------------------------------- 1 | ["\ud800abc"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_string_invalid_utf-8.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status-im/nim-json-serialization/2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf/tests/test_vectors/test_parsing/i_string_invalid_utf-8.json -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_string_inverted_surrogates_U+1D11E.json: -------------------------------------------------------------------------------- 1 | ["\uDd1e\uD834"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_string_iso_latin_1.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status-im/nim-json-serialization/2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf/tests/test_vectors/test_parsing/i_string_iso_latin_1.json -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_string_lone_second_surrogate.json: -------------------------------------------------------------------------------- 1 | ["\uDFAA"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_string_lone_utf8_continuation_byte.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status-im/nim-json-serialization/2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf/tests/test_vectors/test_parsing/i_string_lone_utf8_continuation_byte.json -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_string_not_in_unicode_range.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status-im/nim-json-serialization/2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf/tests/test_vectors/test_parsing/i_string_not_in_unicode_range.json -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_string_overlong_sequence_2_bytes.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status-im/nim-json-serialization/2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf/tests/test_vectors/test_parsing/i_string_overlong_sequence_2_bytes.json -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_string_overlong_sequence_6_bytes.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status-im/nim-json-serialization/2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf/tests/test_vectors/test_parsing/i_string_overlong_sequence_6_bytes.json -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_string_overlong_sequence_6_bytes_null.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status-im/nim-json-serialization/2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf/tests/test_vectors/test_parsing/i_string_overlong_sequence_6_bytes_null.json -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_string_truncated-utf-8.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status-im/nim-json-serialization/2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf/tests/test_vectors/test_parsing/i_string_truncated-utf-8.json -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_string_utf16BE_no_BOM.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status-im/nim-json-serialization/2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf/tests/test_vectors/test_parsing/i_string_utf16BE_no_BOM.json -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_string_utf16LE_no_BOM.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status-im/nim-json-serialization/2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf/tests/test_vectors/test_parsing/i_string_utf16LE_no_BOM.json -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_structure_500_nested_arrays.json: -------------------------------------------------------------------------------- 1 | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/i_structure_UTF-8_BOM_empty_object.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_array_1_true_without_comma.json: -------------------------------------------------------------------------------- 1 | [1 true] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_array_a_invalid_utf8.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status-im/nim-json-serialization/2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf/tests/test_vectors/test_parsing/n_array_a_invalid_utf8.json -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_array_colon_instead_of_comma.json: -------------------------------------------------------------------------------- 1 | ["": 1] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_array_comma_after_close.json: -------------------------------------------------------------------------------- 1 | [""], -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_array_comma_and_number.json: -------------------------------------------------------------------------------- 1 | [,1] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_array_double_comma.json: -------------------------------------------------------------------------------- 1 | [1,,2] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_array_double_extra_comma.json: -------------------------------------------------------------------------------- 1 | ["x",,] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_array_extra_close.json: -------------------------------------------------------------------------------- 1 | ["x"]] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_array_extra_comma.json: -------------------------------------------------------------------------------- 1 | ["",] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_array_incomplete.json: -------------------------------------------------------------------------------- 1 | ["x" -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_array_incomplete_invalid_value.json: -------------------------------------------------------------------------------- 1 | [x -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_array_inner_array_no_comma.json: -------------------------------------------------------------------------------- 1 | [3[4]] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_array_invalid_utf8.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status-im/nim-json-serialization/2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf/tests/test_vectors/test_parsing/n_array_invalid_utf8.json -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_array_items_separated_by_semicolon.json: -------------------------------------------------------------------------------- 1 | [1:2] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_array_just_comma.json: -------------------------------------------------------------------------------- 1 | [,] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_array_just_minus.json: -------------------------------------------------------------------------------- 1 | [-] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_array_missing_value.json: -------------------------------------------------------------------------------- 1 | [ , ""] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_array_newlines_unclosed.json: -------------------------------------------------------------------------------- 1 | ["a", 2 | 4 3 | ,1, -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_array_number_and_comma.json: -------------------------------------------------------------------------------- 1 | [1,] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_array_number_and_several_commas.json: -------------------------------------------------------------------------------- 1 | [1,,] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_array_spaces_vertical_tab_formfeed.json: -------------------------------------------------------------------------------- 1 | [" a"\f] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_array_star_inside.json: -------------------------------------------------------------------------------- 1 | [*] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_array_unclosed.json: -------------------------------------------------------------------------------- 1 | ["" -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_array_unclosed_trailing_comma.json: -------------------------------------------------------------------------------- 1 | [1, -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_array_unclosed_with_new_lines.json: -------------------------------------------------------------------------------- 1 | [1, 2 | 1 3 | ,1 -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_array_unclosed_with_object_inside.json: -------------------------------------------------------------------------------- 1 | [{} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_incomplete_false.json: -------------------------------------------------------------------------------- 1 | [fals] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_incomplete_null.json: -------------------------------------------------------------------------------- 1 | [nul] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_incomplete_true.json: -------------------------------------------------------------------------------- 1 | [tru] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_multidigit_number_then_00.json: -------------------------------------------------------------------------------- 1 | 123 -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_++.json: -------------------------------------------------------------------------------- 1 | [++1234] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_+1.json: -------------------------------------------------------------------------------- 1 | [+1] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_+Inf.json: -------------------------------------------------------------------------------- 1 | [+Inf] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_-01.json: -------------------------------------------------------------------------------- 1 | [-01] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_-1.0..json: -------------------------------------------------------------------------------- 1 | [-1.0.] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_-2..json: -------------------------------------------------------------------------------- 1 | [-2.] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_-NaN.json: -------------------------------------------------------------------------------- 1 | [-NaN] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_.-1.json: -------------------------------------------------------------------------------- 1 | [.-1] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_.2e-3.json: -------------------------------------------------------------------------------- 1 | [.2e-3] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_0.1.2.json: -------------------------------------------------------------------------------- 1 | [0.1.2] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_0.3e+.json: -------------------------------------------------------------------------------- 1 | [0.3e+] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_0.3e.json: -------------------------------------------------------------------------------- 1 | [0.3e] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_0.e1.json: -------------------------------------------------------------------------------- 1 | [0.e1] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_0_capital_E+.json: -------------------------------------------------------------------------------- 1 | [0E+] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_0_capital_E.json: -------------------------------------------------------------------------------- 1 | [0E] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_0e+.json: -------------------------------------------------------------------------------- 1 | [0e+] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_0e.json: -------------------------------------------------------------------------------- 1 | [0e] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_1.0e+.json: -------------------------------------------------------------------------------- 1 | [1.0e+] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_1.0e-.json: -------------------------------------------------------------------------------- 1 | [1.0e-] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_1.0e.json: -------------------------------------------------------------------------------- 1 | [1.0e] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_1_000.json: -------------------------------------------------------------------------------- 1 | [1 000.0] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_1eE2.json: -------------------------------------------------------------------------------- 1 | [1eE2] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_2.e+3.json: -------------------------------------------------------------------------------- 1 | [2.e+3] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_2.e-3.json: -------------------------------------------------------------------------------- 1 | [2.e-3] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_2.e3.json: -------------------------------------------------------------------------------- 1 | [2.e3] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_9.e+.json: -------------------------------------------------------------------------------- 1 | [9.e+] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_Inf.json: -------------------------------------------------------------------------------- 1 | [Inf] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_NaN.json: -------------------------------------------------------------------------------- 1 | [NaN] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_U+FF11_fullwidth_digit_one.json: -------------------------------------------------------------------------------- 1 | [1] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_expression.json: -------------------------------------------------------------------------------- 1 | [1+2] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_hex_1_digit.json: -------------------------------------------------------------------------------- 1 | [0x1] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_hex_2_digits.json: -------------------------------------------------------------------------------- 1 | [0x42] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_infinity.json: -------------------------------------------------------------------------------- 1 | [Infinity] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_invalid+-.json: -------------------------------------------------------------------------------- 1 | [0e+-1] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_invalid-negative-real.json: -------------------------------------------------------------------------------- 1 | [-123.123foo] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_invalid-utf-8-in-bigger-int.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status-im/nim-json-serialization/2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf/tests/test_vectors/test_parsing/n_number_invalid-utf-8-in-bigger-int.json -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_invalid-utf-8-in-exponent.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status-im/nim-json-serialization/2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf/tests/test_vectors/test_parsing/n_number_invalid-utf-8-in-exponent.json -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_invalid-utf-8-in-int.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status-im/nim-json-serialization/2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf/tests/test_vectors/test_parsing/n_number_invalid-utf-8-in-int.json -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_minus_infinity.json: -------------------------------------------------------------------------------- 1 | [-Infinity] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_minus_sign_with_trailing_garbage.json: -------------------------------------------------------------------------------- 1 | [-foo] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_minus_space_1.json: -------------------------------------------------------------------------------- 1 | [- 1] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_neg_int_starting_with_zero.json: -------------------------------------------------------------------------------- 1 | [-012] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_neg_real_without_int_part.json: -------------------------------------------------------------------------------- 1 | [-.123] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_neg_with_garbage_at_end.json: -------------------------------------------------------------------------------- 1 | [-1x] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_real_garbage_after_e.json: -------------------------------------------------------------------------------- 1 | [1ea] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_real_with_invalid_utf8_after_e.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status-im/nim-json-serialization/2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf/tests/test_vectors/test_parsing/n_number_real_with_invalid_utf8_after_e.json -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_real_without_fractional_part.json: -------------------------------------------------------------------------------- 1 | [1.] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_starting_with_dot.json: -------------------------------------------------------------------------------- 1 | [.123] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_with_alpha.json: -------------------------------------------------------------------------------- 1 | [1.2a-3] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_with_alpha_char.json: -------------------------------------------------------------------------------- 1 | [1.8011670033376514H-308] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_number_with_leading_zero.json: -------------------------------------------------------------------------------- 1 | [012] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_object_bad_value.json: -------------------------------------------------------------------------------- 1 | ["x", truth] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_object_bracket_key.json: -------------------------------------------------------------------------------- 1 | {[: "x"} 2 | -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_object_comma_instead_of_colon.json: -------------------------------------------------------------------------------- 1 | {"x", null} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_object_double_colon.json: -------------------------------------------------------------------------------- 1 | {"x"::"b"} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_object_emoji.json: -------------------------------------------------------------------------------- 1 | {🇨🇭} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_object_garbage_at_end.json: -------------------------------------------------------------------------------- 1 | {"a":"a" 123} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_object_key_with_single_quotes.json: -------------------------------------------------------------------------------- 1 | {key: 'value'} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_object_lone_continuation_byte_in_key_and_trailing_comma.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status-im/nim-json-serialization/2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf/tests/test_vectors/test_parsing/n_object_lone_continuation_byte_in_key_and_trailing_comma.json -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_object_missing_colon.json: -------------------------------------------------------------------------------- 1 | {"a" b} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_object_missing_key.json: -------------------------------------------------------------------------------- 1 | {:"b"} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_object_missing_semicolon.json: -------------------------------------------------------------------------------- 1 | {"a" "b"} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_object_missing_value.json: -------------------------------------------------------------------------------- 1 | {"a": -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_object_no-colon.json: -------------------------------------------------------------------------------- 1 | {"a" -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_object_non_string_key.json: -------------------------------------------------------------------------------- 1 | {1:1} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_object_non_string_key_but_huge_number_instead.json: -------------------------------------------------------------------------------- 1 | {9999E9999:1} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_object_repeated_null_null.json: -------------------------------------------------------------------------------- 1 | {null:null,null:null} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_object_several_trailing_commas.json: -------------------------------------------------------------------------------- 1 | {"id":0,,,,,} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_object_single_quote.json: -------------------------------------------------------------------------------- 1 | {'a':0} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_object_trailing_comma.json: -------------------------------------------------------------------------------- 1 | {"id":0,} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_object_trailing_comment.json: -------------------------------------------------------------------------------- 1 | {"a":"b"}/**/ -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_object_trailing_comment_open.json: -------------------------------------------------------------------------------- 1 | {"a":"b"}/**// -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_object_trailing_comment_slash_open.json: -------------------------------------------------------------------------------- 1 | {"a":"b"}// -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_object_trailing_comment_slash_open_incomplete.json: -------------------------------------------------------------------------------- 1 | {"a":"b"}/ -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_object_two_commas_in_a_row.json: -------------------------------------------------------------------------------- 1 | {"a":"b",,"c":"d"} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_object_unquoted_key.json: -------------------------------------------------------------------------------- 1 | {a: "b"} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_object_unterminated-value.json: -------------------------------------------------------------------------------- 1 | {"a":"a -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_object_with_single_string.json: -------------------------------------------------------------------------------- 1 | { "foo" : "bar", "a" } -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_object_with_trailing_garbage.json: -------------------------------------------------------------------------------- 1 | {"a":"b"}# -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_single_space.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_1_surrogate_then_escape.json: -------------------------------------------------------------------------------- 1 | ["\uD800\"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_1_surrogate_then_escape_u.json: -------------------------------------------------------------------------------- 1 | ["\uD800\u"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_1_surrogate_then_escape_u1.json: -------------------------------------------------------------------------------- 1 | ["\uD800\u1"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_1_surrogate_then_escape_u1x.json: -------------------------------------------------------------------------------- 1 | ["\uD800\u1x"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_accentuated_char_no_quotes.json: -------------------------------------------------------------------------------- 1 | [é] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_backslash_00.json: -------------------------------------------------------------------------------- 1 | ["\"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_escape_x.json: -------------------------------------------------------------------------------- 1 | ["\x00"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_escaped_backslash_bad.json: -------------------------------------------------------------------------------- 1 | ["\\\"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_escaped_ctrl_char_tab.json: -------------------------------------------------------------------------------- 1 | ["\ "] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_escaped_emoji.json: -------------------------------------------------------------------------------- 1 | ["\🌀"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_incomplete_escape.json: -------------------------------------------------------------------------------- 1 | ["\"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_incomplete_escaped_character.json: -------------------------------------------------------------------------------- 1 | ["\u00A"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_incomplete_surrogate.json: -------------------------------------------------------------------------------- 1 | ["\uD834\uDd"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_incomplete_surrogate_escape_invalid.json: -------------------------------------------------------------------------------- 1 | ["\uD800\uD800\x"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_invalid-utf-8-in-escape.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status-im/nim-json-serialization/2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf/tests/test_vectors/test_parsing/n_string_invalid-utf-8-in-escape.json -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_invalid_backslash_esc.json: -------------------------------------------------------------------------------- 1 | ["\a"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_invalid_unicode_escape.json: -------------------------------------------------------------------------------- 1 | ["\uqqqq"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_invalid_utf8_after_escape.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status-im/nim-json-serialization/2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf/tests/test_vectors/test_parsing/n_string_invalid_utf8_after_escape.json -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_leading_uescaped_thinspace.json: -------------------------------------------------------------------------------- 1 | [\u0020"asd"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_no_quotes_with_bad_escape.json: -------------------------------------------------------------------------------- 1 | [\n] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_single_doublequote.json: -------------------------------------------------------------------------------- 1 | " -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_single_quote.json: -------------------------------------------------------------------------------- 1 | ['single quote'] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_single_string_no_double_quotes.json: -------------------------------------------------------------------------------- 1 | abc -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_start_escape_unclosed.json: -------------------------------------------------------------------------------- 1 | ["\ -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_unescaped_ctrl_char.json: -------------------------------------------------------------------------------- 1 | ["aa"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_unescaped_newline.json: -------------------------------------------------------------------------------- 1 | ["new 2 | line"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_unescaped_tab.json: -------------------------------------------------------------------------------- 1 | [" "] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_unicode_CapitalU.json: -------------------------------------------------------------------------------- 1 | "\UA66D" -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_string_with_trailing_garbage.json: -------------------------------------------------------------------------------- 1 | ""x -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_U+2060_word_joined.json: -------------------------------------------------------------------------------- 1 | [⁠] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_UTF8_BOM_no_data.json: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_angle_bracket_..json: -------------------------------------------------------------------------------- 1 | <.> -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_angle_bracket_null.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_array_trailing_garbage.json: -------------------------------------------------------------------------------- 1 | [1]x -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_array_with_extra_array_close.json: -------------------------------------------------------------------------------- 1 | [1]] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_array_with_unclosed_string.json: -------------------------------------------------------------------------------- 1 | ["asd] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_ascii-unicode-identifier.json: -------------------------------------------------------------------------------- 1 | aå -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_capitalized_True.json: -------------------------------------------------------------------------------- 1 | [True] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_close_unopened_array.json: -------------------------------------------------------------------------------- 1 | 1] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_comma_instead_of_closing_brace.json: -------------------------------------------------------------------------------- 1 | {"x": true, -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_double_array.json: -------------------------------------------------------------------------------- 1 | [][] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_end_array.json: -------------------------------------------------------------------------------- 1 | ] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_incomplete_UTF8_BOM.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status-im/nim-json-serialization/2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf/tests/test_vectors/test_parsing/n_structure_incomplete_UTF8_BOM.json -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_lone-invalid-utf-8.json: -------------------------------------------------------------------------------- 1 | � -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_lone-open-bracket.json: -------------------------------------------------------------------------------- 1 | [ -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_no_data.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status-im/nim-json-serialization/2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf/tests/test_vectors/test_parsing/n_structure_no_data.json -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_null-byte-outside-string.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_number_with_trailing_garbage.json: -------------------------------------------------------------------------------- 1 | 2@ -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_object_followed_by_closing_object.json: -------------------------------------------------------------------------------- 1 | {}} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_object_unclosed_no_value.json: -------------------------------------------------------------------------------- 1 | {"": -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_object_with_comment.json: -------------------------------------------------------------------------------- 1 | {"a":/*comment*/"b"} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_object_with_trailing_garbage.json: -------------------------------------------------------------------------------- 1 | {"a": true} "x" -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_open_array_apostrophe.json: -------------------------------------------------------------------------------- 1 | [' -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_open_array_comma.json: -------------------------------------------------------------------------------- 1 | [, -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_open_array_open_object.json: -------------------------------------------------------------------------------- 1 | [{ -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_open_array_open_string.json: -------------------------------------------------------------------------------- 1 | ["a -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_open_array_string.json: -------------------------------------------------------------------------------- 1 | ["a" -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_open_object.json: -------------------------------------------------------------------------------- 1 | { -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_open_object_close_array.json: -------------------------------------------------------------------------------- 1 | {] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_open_object_comma.json: -------------------------------------------------------------------------------- 1 | {, -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_open_object_open_array.json: -------------------------------------------------------------------------------- 1 | {[ -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_open_object_open_string.json: -------------------------------------------------------------------------------- 1 | {"a -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_open_object_string_with_apostrophes.json: -------------------------------------------------------------------------------- 1 | {'a' -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_open_open.json: -------------------------------------------------------------------------------- 1 | ["\{["\{["\{["\{ -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_single_eacute.json: -------------------------------------------------------------------------------- 1 | � -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_single_star.json: -------------------------------------------------------------------------------- 1 | * -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_trailing_#.json: -------------------------------------------------------------------------------- 1 | {"a":"b"}#{} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_uescaped_LF_before_string.json: -------------------------------------------------------------------------------- 1 | [\u000A""] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_unclosed_array.json: -------------------------------------------------------------------------------- 1 | [1 -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_unclosed_array_partial_null.json: -------------------------------------------------------------------------------- 1 | [ false, nul -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_unclosed_array_unfinished_false.json: -------------------------------------------------------------------------------- 1 | [ true, fals -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_unclosed_array_unfinished_true.json: -------------------------------------------------------------------------------- 1 | [ false, tru -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_unclosed_object.json: -------------------------------------------------------------------------------- 1 | {"asd":"asd" -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_unicode-identifier.json: -------------------------------------------------------------------------------- 1 | å -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_whitespace_U+2060_word_joiner.json: -------------------------------------------------------------------------------- 1 | [⁠] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/n_structure_whitespace_formfeed.json: -------------------------------------------------------------------------------- 1 | [ ] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_array_arraysWithSpaces.json: -------------------------------------------------------------------------------- 1 | [[] ] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_array_empty-string.json: -------------------------------------------------------------------------------- 1 | [""] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_array_empty.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_array_ending_with_newline.json: -------------------------------------------------------------------------------- 1 | ["a"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_array_false.json: -------------------------------------------------------------------------------- 1 | [false] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_array_heterogeneous.json: -------------------------------------------------------------------------------- 1 | [null, 1, "1", {}] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_array_null.json: -------------------------------------------------------------------------------- 1 | [null] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_array_with_1_and_newline.json: -------------------------------------------------------------------------------- 1 | [1 2 | ] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_array_with_leading_space.json: -------------------------------------------------------------------------------- 1 | [1] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_array_with_several_null.json: -------------------------------------------------------------------------------- 1 | [1,null,null,null,2] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_array_with_trailing_space.json: -------------------------------------------------------------------------------- 1 | [2] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_number.json: -------------------------------------------------------------------------------- 1 | [123e65] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_number_0e+1.json: -------------------------------------------------------------------------------- 1 | [0e+1] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_number_0e1.json: -------------------------------------------------------------------------------- 1 | [0e1] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_number_after_space.json: -------------------------------------------------------------------------------- 1 | [ 4] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_number_double_close_to_zero.json: -------------------------------------------------------------------------------- 1 | [-0.000000000000000000000000000000000000000000000000000000000000000000000000000001] 2 | -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_number_int_with_exp.json: -------------------------------------------------------------------------------- 1 | [20e1] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_number_minus_zero.json: -------------------------------------------------------------------------------- 1 | [-0] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_number_negative_int.json: -------------------------------------------------------------------------------- 1 | [-123] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_number_negative_one.json: -------------------------------------------------------------------------------- 1 | [-1] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_number_negative_zero.json: -------------------------------------------------------------------------------- 1 | [-0] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_number_real_capital_e.json: -------------------------------------------------------------------------------- 1 | [1E22] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_number_real_capital_e_neg_exp.json: -------------------------------------------------------------------------------- 1 | [1E-2] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_number_real_capital_e_pos_exp.json: -------------------------------------------------------------------------------- 1 | [1E+2] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_number_real_exponent.json: -------------------------------------------------------------------------------- 1 | [123e45] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_number_real_fraction_exponent.json: -------------------------------------------------------------------------------- 1 | [123.456e78] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_number_real_neg_exp.json: -------------------------------------------------------------------------------- 1 | [1e-2] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_number_real_pos_exponent.json: -------------------------------------------------------------------------------- 1 | [1e+2] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_number_simple_int.json: -------------------------------------------------------------------------------- 1 | [123] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_number_simple_real.json: -------------------------------------------------------------------------------- 1 | [123.456789] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_object.json: -------------------------------------------------------------------------------- 1 | {"asd":"sdf", "dfg":"fgh"} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_object_basic.json: -------------------------------------------------------------------------------- 1 | {"asd":"sdf"} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_object_duplicated_key.json: -------------------------------------------------------------------------------- 1 | {"a":"b","a":"c"} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_object_duplicated_key_and_value.json: -------------------------------------------------------------------------------- 1 | {"a":"b","a":"b"} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_object_empty.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_object_empty_key.json: -------------------------------------------------------------------------------- 1 | {"":0} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_object_escaped_null_in_key.json: -------------------------------------------------------------------------------- 1 | {"foo\u0000bar": 42} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_object_extreme_numbers.json: -------------------------------------------------------------------------------- 1 | { "min": -1.0e+28, "max": 1.0e+28 } -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_object_long_strings.json: -------------------------------------------------------------------------------- 1 | {"x":[{"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}], "id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_object_simple.json: -------------------------------------------------------------------------------- 1 | {"a":[]} -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_object_string_unicode.json: -------------------------------------------------------------------------------- 1 | {"title":"\u041f\u043e\u043b\u0442\u043e\u0440\u0430 \u0417\u0435\u043c\u043b\u0435\u043a\u043e\u043f\u0430" } -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_object_with_newlines.json: -------------------------------------------------------------------------------- 1 | { 2 | "a": "b" 3 | } -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_1_2_3_bytes_UTF-8_sequences.json: -------------------------------------------------------------------------------- 1 | ["\u0060\u012a\u12AB"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_accepted_surrogate_pair.json: -------------------------------------------------------------------------------- 1 | ["\uD801\udc37"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_accepted_surrogate_pairs.json: -------------------------------------------------------------------------------- 1 | ["\ud83d\ude39\ud83d\udc8d"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_allowed_escapes.json: -------------------------------------------------------------------------------- 1 | ["\"\\\/\b\f\n\r\t"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_backslash_and_u_escaped_zero.json: -------------------------------------------------------------------------------- 1 | ["\\u0000"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_backslash_doublequotes.json: -------------------------------------------------------------------------------- 1 | ["\""] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_comments.json: -------------------------------------------------------------------------------- 1 | ["a/*b*/c/*d//e"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_double_escape_a.json: -------------------------------------------------------------------------------- 1 | ["\\a"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_double_escape_n.json: -------------------------------------------------------------------------------- 1 | ["\\n"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_escaped_control_character.json: -------------------------------------------------------------------------------- 1 | ["\u0012"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_escaped_noncharacter.json: -------------------------------------------------------------------------------- 1 | ["\uFFFF"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_in_array.json: -------------------------------------------------------------------------------- 1 | ["asd"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_in_array_with_leading_space.json: -------------------------------------------------------------------------------- 1 | [ "asd"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_last_surrogates_1_and_2.json: -------------------------------------------------------------------------------- 1 | ["\uDBFF\uDFFF"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_nbsp_uescaped.json: -------------------------------------------------------------------------------- 1 | ["new\u00A0line"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_nonCharacterInUTF-8_U+10FFFF.json: -------------------------------------------------------------------------------- 1 | ["􏿿"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_nonCharacterInUTF-8_U+FFFF.json: -------------------------------------------------------------------------------- 1 | ["￿"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_null_escape.json: -------------------------------------------------------------------------------- 1 | ["\u0000"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_one-byte-utf-8.json: -------------------------------------------------------------------------------- 1 | ["\u002c"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_pi.json: -------------------------------------------------------------------------------- 1 | ["π"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_reservedCharacterInUTF-8_U+1BFFF.json: -------------------------------------------------------------------------------- 1 | ["𛿿"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_simple_ascii.json: -------------------------------------------------------------------------------- 1 | ["asd "] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_space.json: -------------------------------------------------------------------------------- 1 | " " -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json: -------------------------------------------------------------------------------- 1 | ["\uD834\uDd1e"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_three-byte-utf-8.json: -------------------------------------------------------------------------------- 1 | ["\u0821"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_two-byte-utf-8.json: -------------------------------------------------------------------------------- 1 | ["\u0123"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_u+2028_line_sep.json: -------------------------------------------------------------------------------- 1 | ["
"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_u+2029_par_sep.json: -------------------------------------------------------------------------------- 1 | ["
"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_uEscape.json: -------------------------------------------------------------------------------- 1 | ["\u0061\u30af\u30EA\u30b9"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_uescaped_newline.json: -------------------------------------------------------------------------------- 1 | ["new\u000Aline"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_unescaped_char_delete.json: -------------------------------------------------------------------------------- 1 | [""] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_unicode.json: -------------------------------------------------------------------------------- 1 | ["\uA66D"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_unicodeEscapedBackslash.json: -------------------------------------------------------------------------------- 1 | ["\u005C"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_unicode_2.json: -------------------------------------------------------------------------------- 1 | ["⍂㈴⍂"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_unicode_U+10FFFE_nonchar.json: -------------------------------------------------------------------------------- 1 | ["\uDBFF\uDFFE"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_unicode_U+1FFFE_nonchar.json: -------------------------------------------------------------------------------- 1 | ["\uD83F\uDFFE"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json: -------------------------------------------------------------------------------- 1 | ["\u200B"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_unicode_U+2064_invisible_plus.json: -------------------------------------------------------------------------------- 1 | ["\u2064"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_unicode_U+FDD0_nonchar.json: -------------------------------------------------------------------------------- 1 | ["\uFDD0"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_unicode_U+FFFE_nonchar.json: -------------------------------------------------------------------------------- 1 | ["\uFFFE"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_unicode_escaped_double_quote.json: -------------------------------------------------------------------------------- 1 | ["\u0022"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_utf8.json: -------------------------------------------------------------------------------- 1 | ["€𝄞"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_string_with_del_character.json: -------------------------------------------------------------------------------- 1 | ["aa"] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_structure_lonely_false.json: -------------------------------------------------------------------------------- 1 | false -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_structure_lonely_int.json: -------------------------------------------------------------------------------- 1 | 42 -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_structure_lonely_negative_real.json: -------------------------------------------------------------------------------- 1 | -0.1 -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_structure_lonely_null.json: -------------------------------------------------------------------------------- 1 | null -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_structure_lonely_string.json: -------------------------------------------------------------------------------- 1 | "asd" -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_structure_lonely_true.json: -------------------------------------------------------------------------------- 1 | true -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_structure_string_empty.json: -------------------------------------------------------------------------------- 1 | "" -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_structure_trailing_newline.json: -------------------------------------------------------------------------------- 1 | ["a"] 2 | -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_structure_true_in_array.json: -------------------------------------------------------------------------------- 1 | [true] -------------------------------------------------------------------------------- /tests/test_vectors/test_parsing/y_structure_whitespace_array.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /tests/test_vectors/test_transform/number_-9223372036854775808.json: -------------------------------------------------------------------------------- 1 | [-9223372036854775808] 2 | -------------------------------------------------------------------------------- /tests/test_vectors/test_transform/number_-9223372036854775809.json: -------------------------------------------------------------------------------- 1 | [-9223372036854775809] 2 | -------------------------------------------------------------------------------- /tests/test_vectors/test_transform/number_1.0.json: -------------------------------------------------------------------------------- 1 | [1.0] 2 | -------------------------------------------------------------------------------- /tests/test_vectors/test_transform/number_1.000000000000000005.json: -------------------------------------------------------------------------------- 1 | [1.000000000000000005] 2 | -------------------------------------------------------------------------------- /tests/test_vectors/test_transform/number_1000000000000000.json: -------------------------------------------------------------------------------- 1 | [1000000000000000] 2 | -------------------------------------------------------------------------------- /tests/test_vectors/test_transform/number_10000000000000000999.json: -------------------------------------------------------------------------------- 1 | [10000000000000000999] 2 | -------------------------------------------------------------------------------- /tests/test_vectors/test_transform/number_1e-999.json: -------------------------------------------------------------------------------- 1 | [1E-999] 2 | -------------------------------------------------------------------------------- /tests/test_vectors/test_transform/number_1e6.json: -------------------------------------------------------------------------------- 1 | [1E6] 2 | -------------------------------------------------------------------------------- /tests/test_vectors/test_transform/number_9223372036854775807.json: -------------------------------------------------------------------------------- 1 | [9223372036854775807] 2 | -------------------------------------------------------------------------------- /tests/test_vectors/test_transform/number_9223372036854775808.json: -------------------------------------------------------------------------------- 1 | [9223372036854775808] 2 | -------------------------------------------------------------------------------- /tests/test_vectors/test_transform/object_key_nfc_nfd.json: -------------------------------------------------------------------------------- 1 | {"é":"NFC","é":"NFD"} -------------------------------------------------------------------------------- /tests/test_vectors/test_transform/object_key_nfd_nfc.json: -------------------------------------------------------------------------------- 1 | {"é":"NFD","é":"NFC"} -------------------------------------------------------------------------------- /tests/test_vectors/test_transform/object_same_key_different_values.json: -------------------------------------------------------------------------------- 1 | {"a":1,"a":2} -------------------------------------------------------------------------------- /tests/test_vectors/test_transform/object_same_key_same_value.json: -------------------------------------------------------------------------------- 1 | {"a":1,"a":1} -------------------------------------------------------------------------------- /tests/test_vectors/test_transform/object_same_key_unclear_values.json: -------------------------------------------------------------------------------- 1 | {"a":0, "a":-0} 2 | -------------------------------------------------------------------------------- /tests/test_vectors/test_transform/string_1_escaped_invalid_codepoint.json: -------------------------------------------------------------------------------- 1 | ["\uD800"] -------------------------------------------------------------------------------- /tests/test_vectors/test_transform/string_1_invalid_codepoint.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status-im/nim-json-serialization/2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf/tests/test_vectors/test_transform/string_1_invalid_codepoint.json -------------------------------------------------------------------------------- /tests/test_vectors/test_transform/string_2_escaped_invalid_codepoints.json: -------------------------------------------------------------------------------- 1 | ["\uD800\uD800"] -------------------------------------------------------------------------------- /tests/test_vectors/test_transform/string_2_invalid_codepoints.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status-im/nim-json-serialization/2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf/tests/test_vectors/test_transform/string_2_invalid_codepoints.json -------------------------------------------------------------------------------- /tests/test_vectors/test_transform/string_3_escaped_invalid_codepoints.json: -------------------------------------------------------------------------------- 1 | ["\uD800\uD800\uD800"] -------------------------------------------------------------------------------- /tests/test_vectors/test_transform/string_3_invalid_codepoints.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status-im/nim-json-serialization/2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf/tests/test_vectors/test_transform/string_3_invalid_codepoints.json -------------------------------------------------------------------------------- /tests/test_vectors/test_transform/string_with_escaped_NULL.json: -------------------------------------------------------------------------------- 1 | ["A\u0000B"] -------------------------------------------------------------------------------- /tests/test_writer.nim: -------------------------------------------------------------------------------- 1 | # json-serialization 2 | # Copyright (c) 2024 Status Research & Development GmbH 3 | # Licensed under either of 4 | # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) 5 | # * MIT license ([LICENSE-MIT](LICENSE-MIT)) 6 | # at your option. 7 | # This file may not be copied, modified, or distributed except according to 8 | # those terms. 9 | 10 | import 11 | unittest2, 12 | ../json_serialization/stew/results, 13 | ../json_serialization/std/options, 14 | ../json_serialization 15 | 16 | type 17 | ObjectWithOptionalFields = object 18 | a: Opt[int] 19 | b: Option[string] 20 | c: int 21 | 22 | OWOF = object 23 | a: Opt[int] 24 | b: Option[string] 25 | c: int 26 | 27 | createJsonFlavor YourJson, 28 | omitOptionalFields = false 29 | 30 | createJsonFlavor MyJson, 31 | omitOptionalFields = true 32 | 33 | ObjectWithOptionalFields.useDefaultSerializationIn YourJson 34 | ObjectWithOptionalFields.useDefaultSerializationIn MyJson 35 | 36 | type 37 | FruitX = enum 38 | BananaX = "BaNaNa" 39 | AppleX = "ApplE" 40 | GrapeX = "VVV" 41 | 42 | Drawer = enum 43 | One 44 | Two 45 | 46 | FruitX.configureJsonSerialization(EnumAsString) 47 | Json.configureJsonSerialization(Drawer, EnumAsNumber) 48 | MyJson.configureJsonSerialization(Drawer, EnumAsString) 49 | 50 | proc writeValue*(w: var JsonWriter, val: OWOF) 51 | {.gcsafe, raises: [IOError].} = 52 | w.writeObject(OWOF): 53 | w.writeField("a", val.a) 54 | w.writeField("b", val.b) 55 | w.writeField("c", val.c) 56 | 57 | suite "Test writer": 58 | test "stdlib option top level some YourJson": 59 | var val = some(123) 60 | let json = YourJson.encode(val) 61 | check json == "123" 62 | 63 | test "stdlib option top level none YourJson": 64 | var val = none(int) 65 | let json = YourJson.encode(val) 66 | check json == "null" 67 | 68 | test "stdlib option top level some MyJson": 69 | var val = some(123) 70 | let json = MyJson.encode(val) 71 | check json == "123" 72 | 73 | test "stdlib option top level none MyJson": 74 | var val = none(int) 75 | let json = MyJson.encode(val) 76 | check json == "null" 77 | 78 | test "results option top level some YourJson": 79 | var val = Opt.some(123) 80 | let json = YourJson.encode(val) 81 | check json == "123" 82 | 83 | test "results option top level none YourJson": 84 | var val = Opt.none(int) 85 | let json = YourJson.encode(val) 86 | check json == "null" 87 | 88 | test "results option top level some MyJson": 89 | var val = Opt.some(123) 90 | let json = MyJson.encode(val) 91 | check json == "123" 92 | 93 | test "results option top level none MyJson": 94 | var val = Opt.none(int) 95 | let json = MyJson.encode(val) 96 | check json == "null" 97 | 98 | test "stdlib option array some YourJson": 99 | var val = [some(123), some(345)] 100 | let json = YourJson.encode(val) 101 | check json == "[123,345]" 102 | 103 | test "stdlib option array none YourJson": 104 | var val = [some(123), none(int), some(777)] 105 | let json = YourJson.encode(val) 106 | check json == "[123,null,777]" 107 | 108 | test "stdlib option array some MyJson": 109 | var val = [some(123), some(345)] 110 | let json = MyJson.encode(val) 111 | check json == "[123,345]" 112 | 113 | test "stdlib option array none MyJson": 114 | var val = [some(123), none(int), some(777)] 115 | let json = MyJson.encode(val) 116 | check json == "[123,null,777]" 117 | 118 | test "results option array some YourJson": 119 | var val = [Opt.some(123), Opt.some(345)] 120 | let json = YourJson.encode(val) 121 | check json == "[123,345]" 122 | 123 | test "results option array none YourJson": 124 | var val = [Opt.some(123), Opt.none(int), Opt.some(777)] 125 | let json = YourJson.encode(val) 126 | check json == "[123,null,777]" 127 | 128 | test "results option array some MyJson": 129 | var val = [Opt.some(123), Opt.some(345)] 130 | let json = MyJson.encode(val) 131 | check json == "[123,345]" 132 | 133 | test "results option array none MyJson": 134 | var val = [Opt.some(123), Opt.none(int), Opt.some(777)] 135 | let json = MyJson.encode(val) 136 | check json == "[123,null,777]" 137 | 138 | test "object with optional fields": 139 | let x = ObjectWithOptionalFields( 140 | a: Opt.some(123), 141 | b: some("nano"), 142 | c: 456, 143 | ) 144 | 145 | let y = ObjectWithOptionalFields( 146 | a: Opt.none(int), 147 | b: none(string), 148 | c: 999, 149 | ) 150 | 151 | let u = YourJson.encode(x) 152 | check u == """{"a":123,"b":"nano","c":456}""" 153 | 154 | let v = YourJson.encode(y) 155 | check v == """{"a":null,"b":null,"c":999}""" 156 | 157 | let xx = MyJson.encode(x) 158 | check xx == """{"a":123,"b":"nano","c":456}""" 159 | 160 | let yy = MyJson.encode(y) 161 | check yy == """{"c":999}""" 162 | 163 | test "writeField with object with optional fields": 164 | let x = OWOF( 165 | a: Opt.some(123), 166 | b: some("nano"), 167 | c: 456, 168 | ) 169 | 170 | let y = OWOF( 171 | a: Opt.none(int), 172 | b: none(string), 173 | c: 999, 174 | ) 175 | 176 | let xx = MyJson.encode(x) 177 | check xx == """{"a":123,"b":"nano","c":456}""" 178 | let yy = MyJson.encode(y) 179 | check yy == """{"c":999}""" 180 | 181 | let uu = YourJson.encode(x) 182 | check uu == """{"a":123,"b":"nano","c":456}""" 183 | let vv = YourJson.encode(y) 184 | check vv == """{"a":null,"b":null,"c":999}""" 185 | 186 | test "Enum value representation primitives": 187 | when DefaultFlavor.flavorEnumRep() == EnumAsString: 188 | check true 189 | elif DefaultFlavor.flavorEnumRep() == EnumAsNumber: 190 | check false 191 | elif DefaultFlavor.flavorEnumRep() == EnumAsStringifiedNumber: 192 | check false 193 | 194 | DefaultFlavor.flavorEnumRep(EnumAsNumber) 195 | when DefaultFlavor.flavorEnumRep() == EnumAsString: 196 | check false 197 | elif DefaultFlavor.flavorEnumRep() == EnumAsNumber: 198 | check true 199 | elif DefaultFlavor.flavorEnumRep() == EnumAsStringifiedNumber: 200 | check false 201 | 202 | DefaultFlavor.flavorEnumRep(EnumAsStringifiedNumber) 203 | when DefaultFlavor.flavorEnumRep() == EnumAsString: 204 | check false 205 | elif DefaultFlavor.flavorEnumRep() == EnumAsNumber: 206 | check false 207 | elif DefaultFlavor.flavorEnumRep() == EnumAsStringifiedNumber: 208 | check true 209 | 210 | test "Enum value representation of DefaultFlavor": 211 | type 212 | ExoticFruits = enum 213 | DragonFruit 214 | SnakeFruit 215 | StarFruit 216 | 217 | DefaultFlavor.flavorEnumRep(EnumAsNumber) 218 | let u = Json.encode(DragonFruit) 219 | check u == "0" 220 | 221 | DefaultFlavor.flavorEnumRep(EnumAsString) 222 | let v = Json.encode(SnakeFruit) 223 | check v == "\"SnakeFruit\"" 224 | 225 | DefaultFlavor.flavorEnumRep(EnumAsStringifiedNumber) 226 | let w = Json.encode(StarFruit) 227 | check w == "\"2\"" 228 | 229 | test "EnumAsString of DefaultFlavor/Json": 230 | type 231 | Fruit = enum 232 | Banana = "BaNaNa" 233 | Apple = "ApplE" 234 | JackFruit = "VVV" 235 | 236 | ObjectWithEnumField = object 237 | fruit: Fruit 238 | 239 | Json.flavorEnumRep(EnumAsString) 240 | let u = Json.encode(Banana) 241 | check u == "\"BaNaNa\"" 242 | 243 | let v = Json.encode(Apple) 244 | check v == "\"ApplE\"" 245 | 246 | let w = Json.encode(JackFruit) 247 | check w == "\"VVV\"" 248 | 249 | Json.flavorEnumRep(EnumAsStringifiedNumber) 250 | let x = Json.encode(JackFruit) 251 | check x == "\"2\"" 252 | 253 | Json.flavorEnumRep(EnumAsNumber) 254 | let z = Json.encode(Banana) 255 | check z == "0" 256 | 257 | let obj = ObjectWithEnumField(fruit: Banana) 258 | let zz = Json.encode(obj) 259 | check zz == """{"fruit":0}""" 260 | 261 | test "Individual enum configuration": 262 | Json.flavorEnumRep(EnumAsNumber) 263 | # Although the flavor config is EnumAsNumber 264 | # FruitX is configured as EnumAsAstring 265 | let z = Json.encode(BananaX) 266 | check z == "\"BaNaNa\"" 267 | 268 | # configuration: Json.configureJsonSerialization(Drawer, EnumAsNumber) 269 | let u = Json.encode(Two) 270 | check u == "1" 271 | 272 | # configuration: MyJson.configureJsonSerialization(Drawer, EnumAsString) 273 | let v = MyJson.encode(One) 274 | check v == "\"One\"" 275 | -------------------------------------------------------------------------------- /tests/utils.nim: -------------------------------------------------------------------------------- 1 | # json-serialization 2 | # Copyright (c) 2019-2023 Status Research & Development GmbH 3 | # Licensed under either of 4 | # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) 5 | # * MIT license ([LICENSE-MIT](LICENSE-MIT)) 6 | # at your option. 7 | # This file may not be copied, modified, or distributed except according to 8 | # those terms. 9 | 10 | import 11 | strutils 12 | 13 | # `dedent` exists in newer Nim version and doesn't behave the same 14 | func test_dedent*(s: string): string = 15 | var 16 | s = s.strip(leading = false) 17 | minIndent = high(int) 18 | for l in s.splitLines: 19 | let indent = count(l, ' ') 20 | if indent == 0: continue 21 | if indent < minIndent: minIndent = indent 22 | s.unindent(minIndent) 23 | 24 | const 25 | parsingPath* = "tests/test_vectors/test_parsing" 26 | transformPath* = "tests/test_vectors/test_transform" 27 | 28 | const 29 | allowedToFail* = [ 30 | "string_1_escaped_invalid_codepoint.json", 31 | "string_3_escaped_invalid_codepoints.json", 32 | "i_number_huge_exp.json", 33 | "i_string_1st_surrogate_but_2nd_missing.json", 34 | "i_string_incomplete_surrogate_and_escape_valid.json", 35 | "i_string_invalid_lonely_surrogate.json", 36 | "i_string_invalid_surrogate.json", 37 | "i_string_inverted_surrogates_U+1D11E.json", 38 | "i_string_UTF-16LE_with_BOM.json", 39 | "i_string_utf16BE_no_BOM.json", 40 | "i_string_utf16LE_no_BOM.json", 41 | "i_structure_UTF-8_BOM_empty_object.json", 42 | ] 43 | --------------------------------------------------------------------------------