├── .git-blame-ignore-revs ├── .github └── workflows │ ├── changelog.yml │ └── pr-number.yml ├── .gitignore ├── .ocamlformat ├── .ocamlformat-ignore ├── CHANGES.md ├── CODEOWNERS ├── LICENSE.md ├── Makefile ├── README.md ├── bench ├── README.md ├── bench.json ├── buf.ml ├── dune ├── generic.ml └── mocks.ml ├── bin ├── dune └── ydump.ml ├── doc ├── dune └── index.mld ├── dune-project ├── examples ├── constructing.ml ├── dune ├── filtering.json └── filtering.ml ├── lib ├── basic.cppo.ml ├── basic.cppo.mli ├── codec.ml ├── codec.mli ├── common.ml ├── common.mli ├── dune ├── json5 │ ├── ast.ml │ ├── basic.ml │ ├── dune │ ├── errors.ml │ ├── errors.mli │ ├── let_syntax.ml │ ├── lexer.ml │ ├── parser.ml │ ├── read.ml │ ├── safe.ml │ ├── unescape.ml │ ├── yojson_five.ml │ └── yojson_five.mli ├── lexer_utils.mll ├── monomorphic.ml ├── monomorphic.mli ├── mucppo │ ├── dune │ └── mucppo.ml ├── prettyprint.ml ├── raw.cppo.ml ├── raw.cppo.mli ├── read.mli ├── read.mll ├── safe.cppo.ml ├── safe.cppo.mli ├── safe_to_basic.ml ├── safe_to_basic.mli ├── t.cppo.ml ├── t.cppo.mli ├── type.ml ├── util.ml ├── util.mli ├── write.ml ├── write.mli ├── write2.ml ├── write2.mli ├── yojson.ml └── yojson.mli ├── test ├── compliance │ ├── dune │ ├── test.ml │ └── test_cases │ │ ├── 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_crtl_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 ├── dune ├── fixtures.ml ├── fixtures.mli ├── pretty │ ├── atd.expected.json │ ├── atd.ml │ ├── dune │ ├── sample.json │ ├── test.expected.json │ └── test.ml ├── test.ml ├── test_monomorphic.ml ├── test_monomorphic.mli ├── test_read.ml ├── test_read.mli ├── test_util.ml ├── test_util.mli ├── test_write.ml ├── test_write.mli ├── testable.ml └── testable.mli ├── test_json5 ├── dune └── test.ml ├── yojson-bench.opam ├── yojson-five.opam └── yojson.opam /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # Introduced ocamlformat 2 | 3101ec295fbc791a1e9384f50178846198a607c7 3 | -------------------------------------------------------------------------------- /.github/workflows/changelog.yml: -------------------------------------------------------------------------------- 1 | name: Changelog check 2 | 3 | on: 4 | pull_request: 5 | branches: [ master ] 6 | types: [ opened, synchronize, reopened, labeled, unlabeled ] 7 | 8 | jobs: 9 | Changelog-Entry-Check: 10 | name: Check Changelog Action 11 | runs-on: ubuntu-24.04 12 | steps: 13 | - uses: tarides/changelog-check-action@v1 14 | -------------------------------------------------------------------------------- /.github/workflows/pr-number.yml: -------------------------------------------------------------------------------- 1 | name: PR number update 2 | on: [pull_request_target] 3 | jobs: 4 | PR-Number-Update: 5 | runs-on: ubuntu-24.04 6 | steps: 7 | - name: Update PR number 8 | uses: tarides/pr-number-action@v1.1 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /_build/ 2 | /_opam/ 3 | .merlin 4 | /*.install 5 | -------------------------------------------------------------------------------- /.ocamlformat: -------------------------------------------------------------------------------- 1 | version=0.24.1 2 | profile=conventional 3 | -------------------------------------------------------------------------------- /.ocamlformat-ignore: -------------------------------------------------------------------------------- 1 | # disable ocamlformat on files that are processed by CPPO 2 | lib/*.cppo.ml* 3 | lib/monomorphic.ml 4 | lib/write.ml* 5 | lib/prettyprint.ml 6 | lib/type.ml 7 | lib/util.ml 8 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | ## unreleased 2 | 3 | ### Added 4 | 5 | ### Changed 6 | 7 | ### Deprecated 8 | 9 | ### Fixed 10 | 11 | ### Removed 12 | 13 | ### Security 14 | 15 | ## 3.0.0 16 | 17 | *2025-05-29* 18 | 19 | ### Changed 20 | 21 | - Floats are now always output to JSON in a standard-conformant way or not at 22 | all (raising an exception). This makes the `std` variants of functions 23 | identical to the non-`std` variants and the `std` arguments have no effect. 24 | Users are encouraged to switch to the non-`std` affixed variants, the others 25 | will be deprecated in the future. (#184, @Leonidas-from-XIV) 26 | - Bumped the minimum required version of OCaml for the main package to 4.08 27 | since the CI dropped the support. This however allows removing the dependency 28 | on the `seq` library, so the depencency cone becomes slightly smaller. (#194, 29 | @Leonidas-from-XIV) 30 | 31 | ### Fixed 32 | 33 | - Fixed handling of escape sequences in JSON5. Known escapes like \b will be 34 | properly unescaped and undefined escape sequences will unescape to the 35 | character itself as per spec (#187, @david-maison-TrustInSoft) 36 | - Fixed tests failing on Windows due to disagreements with the length of an 37 | input channel and the text mode conversion (#192, @Leonidas-from-XIV) 38 | 39 | ### Removed 40 | 41 | - Removed support for Tuple and Variant in JSON. It was a non-standard 42 | extension that was rarely used, so this simplifies the Yojson types and the 43 | parser more standard-conforming (#105, #158, #185 @Leonidas-from-XIV) 44 | 45 | ## 2.2.2 46 | 47 | *2024-06-27* 48 | 49 | ### Added 50 | 51 | - Add locations in the JSON5 parser error messages (@gcluzel, #182) 52 | 53 | ## 2.2.1 54 | 55 | *2024-06-04* 56 | 57 | ### Fixed 58 | 59 | - Don't expose `Yojson_five` internals anymore (@Leonidas_from_XIV, #180) 60 | 61 | ## 2.2.0 62 | 63 | *2024-05-31* 64 | 65 | ### Added 66 | 67 | - Added support for JSON5 (@dhilst, @gorm-issuu, @gertsonderby, #152) 68 | 69 | ### Removed 70 | 71 | - Remove CPPO dependency to make the Yojson installation lighter 72 | (@Leonidas-from-XIV, #175) 73 | 74 | ## 2.1.2 75 | 76 | *2023-11-23* 77 | 78 | ### Fixed 79 | 80 | - Fix the error location reported in the exception. Regression in 2.1.1 81 | (reported by @johnridesabike, regression test by @ElectreAAS, fix by 82 | @Leonidas-from-XIV, #171, #172) 83 | 84 | ## 2.1.1 85 | 86 | *2023-10-10* 87 | 88 | ### Changed 89 | 90 | - Make `Basic`, `Safe` & `Raw` seperate compilation units that get exposed by 91 | the main module as suggested by @hhugo to enable JSOO to discard unused 92 | modules. No API changes should be observable. (#84, #167 @Leonidas-from-XIV) 93 | - Removed forward refs in the parser to make dead-code elimination in JSOO 94 | better (#168, @hhugo) 95 | 96 | ## 2.1.0 97 | 98 | *2023-04-26* 99 | 100 | ### Added 101 | 102 | - Add `Yojson.Raw.Util` module to provide combinators for extracting fields 103 | from `Yojson.Raw.t` values. (@tmcgilchrist, #163) 104 | 105 | - Add `Util.path` function to recurse into an object through a list of keys. 106 | (@cuihtlauac, @Leonidas-from-XIV, #157) 107 | 108 | ## 2.0.2 109 | 110 | *2022-08-09* 111 | 112 | ### Added 113 | 114 | - Expanded documentation of exceptions (@sim642, #148) 115 | 116 | ### Removed 117 | 118 | - Removed undocumented and unused functions `write_float_fast` and 119 | `write_std_float_fast` from `Yojson`, `Yojson.Basic` and `Yojson.Safe` 120 | (@sim642, #149) 121 | 122 | ### Fixed 123 | 124 | - Fix out-of-bounds error occurring when parsing object field names 125 | with atdgen parsers using `map_ident` or `map_lexeme` (@mjambon, #150) 126 | 127 | ## 2.0.1 128 | 129 | *2022-06-28* 130 | 131 | ### Fixed 132 | 133 | - Fix pretty-printing behavior for lists/objects inside lists (@c-cube 134 | #141, @mjambon #142) 135 | 136 | ## 2.0.0 137 | 138 | *2022-06-02* 139 | 140 | ### Removed 141 | 142 | - Removed dependency on easy-format and removed `pretty_format` from 143 | `Yojson`, `Yojson.Basic`, `Yojson.Safe` and `Yojson.Raw`. (@c-cube, #90) 144 | - Removed dependency on `biniou`, simplifying the chain of dependencies. This 145 | changes some APIs: 146 | * `Bi_outbuf.t` in signatures is replaced with `Buffer.t` 147 | * `to_outbuf` becomes `to_buffer` and `stream_to_outbuf` becomes 148 | `stream_to_buffer` 149 | (@Leonidas-from-XIV, #74, and @gasche, #132) 150 | - Removed `yojson-biniou` library 151 | - Removed deprecated `json` type aliasing type `t` which has been available 152 | since 1.6.0 (@Leonidas-from-XIV, #100). 153 | - Removed `json_max` type (@Leonidas-from-XIV, #103) 154 | - Removed constraint that the "root" value being rendered (via either 155 | `pretty_print` or `to_string`) must be an object or array. (@cemerick, #121) 156 | - Removed `validate_json` as it only made sense if the type was called `json`. 157 | (@Leonidas-from-XIV, #137) 158 | 159 | ### Add 160 | 161 | - Add an opam package `yojson-bench` to deal with benchmarks dependency 162 | (@tmcgilchrist, #117) 163 | - Add a benchmark to judge the respective performance of providing a buffer vs 164 | letting Yojson create an internal (#134, @Leonidas-from-XIV) 165 | - Add an optional `suf` keyword argument was added to functions that write 166 | serialized JSON, thus allowing NDJSON output. Most functions default to not 167 | adding any suffix except for `to_file` (#124, @panglesd) and functions 168 | writing sequences of values where the default is `\n` (#135, 169 | @Leonidas-from-XIV) 170 | 171 | ### Change 172 | 173 | - The `stream_from_*` and `stream_to_*` functions now use a `Seq.t` instead of a 174 | `Stream.t`, and they are renamed into `seq_from_*` and `seq_to_*` (@gasche, #131). 175 | 176 | ### Fix 177 | 178 | - Avoid copying unnecessarily large amounts of strings when parsing (#85, #108, 179 | @Leonidas-from-XIV) 180 | - Fix `stream_to_file` (#133, @tcoopman and @gasche) 181 | 182 | ## 1.7.0 183 | 184 | *2019-02-14* 185 | 186 | ### Add 187 | 188 | - Add documented `write_t` and `read_t` to modules defining a JSON ast type for compatibility 189 | with atdgen 190 | 191 | ## 1.6.0 192 | 193 | *2019-01-30* 194 | 195 | ### Deprecate 196 | 197 | - `json` types are deprecated in favor of their new `t` aliases, ahead of their removal in the next 198 | major release (#73, @Leonidas-from-XIV) 199 | 200 | ### Add 201 | 202 | - Add a type `t` and monomorphic `equal`, `pp` and `show` (#73, @Leonidas-from-XIV) 203 | 204 | ## 1.5.0 205 | 206 | ### Change 207 | 208 | - Use dune as a build system (#67, @Leonidas-from-XIV) 209 | - reraise exceptions in `finish_string` instead of silencing them by raising a `Failure _` 210 | - raise finalizer exceptions in `from_channel` and `from_lexbuf` readers 211 | 212 | ### Fix 213 | 214 | - Fix a race condition in builds (#57, @avsm) 215 | 216 | ## 1.2.0 217 | 218 | *2014-12-26* 219 | 220 | - new function `Yojson.Safe.buffer_json` for saving a raw JSON string while 221 | parsing in order to parse later 222 | 223 | ## 1.1.8 224 | 225 | *2014-01-19* 226 | 227 | - cmxs is now generated for supported platforms 228 | 229 | ## 1.1.7 230 | 231 | *2013-05-24* 232 | 233 | - tolerate double quoted boolean "true" and "false" when a boolean is expected 234 | 235 | ## 1.1.6 236 | 237 | *2013-05-16* 238 | 239 | - fix a bug in float printing. now print number of significant figures rather 240 | than decimal places for `write_float_prec` and `write_std_float_prec` 241 | 242 | ## 1.1.5 243 | 244 | *2013-03-19* 245 | 246 | - new function `Yojson.sort` to sort fields in objects, and corresponding 247 | cmdline option. 248 | 249 | ## 1.1.4 250 | 251 | *2012-12-31* 252 | 253 | - proper support for escaped code points above U+FFFF 254 | 255 | ## 1.1.3 256 | 257 | *2012-03-19* 258 | 259 | - new function `Yojson.to_output` for writing to an OO channel; requires 260 | `biniou` >= 1.0.2 261 | 262 | ## 1.1.2 263 | 264 | *2012-02-27* 265 | 266 | - various enhancements 267 | 268 | ## 1.1.1 269 | 270 | *2012-02-07* 271 | 272 | - ydump now implies -s i.e. multiple whitespace-separated records are accepted. 273 | 274 | ## 1.1.0 275 | 276 | *2012-01-26* 277 | 278 | - `Yojson.Biniou` becomes `Yojson_biniou`, package `yojson.biniou` 279 | 280 | ## 1.0.2 281 | 282 | *2011-04-27* 283 | 284 | - improved error messages showing several lookahead bytes 285 | - factored out `lexer_state` and `init_lexer` definitions 286 | - added `read_null_if_possible` function (used by `atdgen`) 287 | 288 | ## 1.0.1 289 | 290 | *2011-01-22* 291 | 292 | - fixed serialization of negative ints using the `write_int` function (affects 293 | `atdgen`) 294 | 295 | ## 1.0.0 296 | 297 | *2010-12-04* 298 | 299 | - now requires `biniou` version 1.0.0 or higher 300 | 301 | ## 0.8.1 302 | 303 | *2010-09-13* 304 | 305 | - added `INSTALL` file 306 | 307 | ## 0.8.0 308 | 309 | *2010-08-04* 310 | 311 | - first release 312 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # This file helps Github suggest reviewers of pull requests based on which 2 | # files changed. 3 | # Learn more at https://help.github.com/articles/about-codeowners/ 4 | 5 | * @NathanReb @panglesd @Leonidas-from-XIV 6 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2012, Martin Jambon 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * Neither the name of nor the names of its contributors may be used to 13 | endorse or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: 3 | @dune build @install @examples 4 | 5 | .PHONY: run-examples 6 | run-examples: 7 | dune exec examples/filtering.exe < examples/filtering.json 8 | 9 | .PHONY: install 10 | install: 11 | @dune install 12 | 13 | .PHONY: uninstall 14 | uninstall: 15 | @dune uninstall 16 | 17 | .PHONY: bench 18 | bench: 19 | @dune build --display=quiet @bench-generic-sexp --force 20 | @dune build --display=quiet @bench-buffer-sexp --force 21 | 22 | .PHONY: bench-local 23 | bench-local: 24 | @dune build @bench --force 25 | 26 | .PHONY: clean 27 | clean: 28 | @dune clean 29 | 30 | .PHONY: test 31 | test: 32 | @dune runtest --force 33 | 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Yojson: JSON library for OCaml 2 | ============================== 3 | 4 | [![Build Status](https://img.shields.io/endpoint?url=https%3A%2F%2Focaml.ci.dev%2Fbadge%2Focaml-community%2Fyojson%2Fmaster&logo=ocaml)](https://ocaml.ci.dev/github/ocaml-community/yojson) 5 | 6 | This library parses JSON data into a nested OCaml tree data structure. 7 | 8 | 9 | Library documentation 10 | --------------------- 11 | 12 | Currently at https://ocaml-community.github.io/yojson/ 13 | 14 | 15 | Examples 16 | -------- 17 | 18 | A simple example on how to parse JSON from a string literal. 19 | 20 | ```ocaml 21 | let json_string = {| 22 | {"number" : 42, 23 | "string" : "yes", 24 | "list": ["for", "sure", 42]}|} 25 | (* val json_string : string *) 26 | 27 | let json = Yojson.Safe.from_string json_string 28 | (* val json : Yojson.Safe.t *) 29 | 30 | let () = Format.printf "Parsed to %a" Yojson.Safe.pp json 31 | ``` 32 | 33 | 34 | Related tooling 35 | --------------- 36 | 37 | `Yojson` is a pretty common choice for parsing JSON in OCaml, as such it is the 38 | base for a number of tools and libraries that are built on top of it. 39 | 40 | * [`ppx_deriving_yojson`](https://github.com/ocaml-ppx/ppx_deriving_yojson) to 41 | automatically generate code that converts between `Yojson.Safe.t` and custom 42 | OCaml types 43 | * [`ppx_yojson_conv`](https://github.com/janestreet/ppx_yojson_conv), an 44 | alternative to `ppx_deriving_yojson` from Jane Street with different design 45 | decisions 46 | * [`atd`](https://github.com/ahrefs/atd), generates mapping code from `.atd` 47 | specification files and can be used in multiple languages 48 | * [`jsonm`](https://erratique.ch/software/jsonm) is an alternate JSON parser 49 | that parses JSON into a stream of items, so the complete data structure does 50 | not have to be in memory. 51 | 52 | Help wanted 53 | ----------- 54 | 55 | Yojson is developed and maintained by volunteers — users like you. 56 | [Various issues](https://github.com/ocaml-community/yojson/issues) are in need 57 | of attention. If you'd like to contribute, please leave a comment on the issue 58 | you're interested in, or create a new issue. Experienced contributors will 59 | guide you as needed. 60 | 61 | There are many simple ways of making a positive impact. For example, 62 | you can... 63 | 64 | * Use the software in your project. 65 | * Give a demo to your colleagues. 66 | * Share the passion on your blog. 67 | * Tweet about what you're doing with `Yojson`. 68 | * Report difficulties by creating new issues. We'll triage them. 69 | * Ask questions on StackOverflow. 70 | * Answer questions on 71 | [StackOverflow](https://stackoverflow.com/search?q=yojson). 72 | * Discuss usage on the [OCaml forums](https://discuss.ocaml.org/). 73 | * Pick a [task](https://github.com/ocaml-community/yojson/issues) that's easy 74 | for you. 75 | 76 | Check out in particular 77 | [good first time issues](https://github.com/ocaml-community/yojson/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+time+issue%22) 78 | and other issues with which 79 | [we could use some help](https://github.com/ocaml-community/yojson/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22). 80 | 81 | 82 | License 83 | ------- 84 | 85 | `Yojson` is licensed under the 3-clause BSD license, see `LICENSE.md` for 86 | details. 87 | -------------------------------------------------------------------------------- /bench/README.md: -------------------------------------------------------------------------------- 1 | Benchmark for Yojson 2 | ==================== 3 | 4 | These benchmarks require `Core_bench` which is not a dependency of Yojson, 5 | because it is not part of the regular installation/testing flow. This is solely 6 | meant for developers that are worried about performance changes in Yojson. 7 | 8 | This is also why it is deemed appropriate to depend on a benchmarking library 9 | with this many dependencies: we rather spend time writing benchmarks than to 10 | build a benchmarking library if a perfectly good one already exists in OCaml. 11 | 12 | Install 13 | ------- 14 | 15 | `opam install core_benchmark` 16 | 17 | Running the benchmark 18 | --------------------- 19 | 20 | `make bench` in the top level directory. 21 | -------------------------------------------------------------------------------- /bench/bench.json: -------------------------------------------------------------------------------- 1 | {"id":"2581o62fp36dn","name":"JAMES SMITH","display_url":"http://www.42theanswer.com/displayProfile.do?uid=363812639","type":"person","image_url":"http://photo.42theanswer.com/photos/015/867/424M.jpg","gender":"male","age":40,"locations":[{"title":"current","country":"US","adminarea":"wisconsin","locality":"portage","text":"Portage, Wisconsin"}],"bio_field":"about_me","fields":{"geocoded_lat_long":[43.547184000000001,-89.465057999999999],"member_since":{"day":15,"month":8,"year":2006},"has_profile_image":true,"last_name":"SMITH","tatata_id":"363812639","estimated_date_of_birth":{"max_date":{"day":26,"month":2,"year":1970},"mean_date":{"day":26,"month":2,"year":1970},"min_date":{"day":26,"month":2,"year":1970}},"url_list":["http://www.42theanswer.com/displayProfile.do?uid=363812677"],"zip":"53901","city":"Portage","site":["42theanswer.com","www.42theanswer.com"],"state":"WI","source":"tatata","country":"US","user_id":"363812639","publication_timestamp":"1262614598","first_name":"JAMES","tatata_image_id":"15867624","source_info":{"id":"tatata","url":"http://www.42theanswer.com/"},"date_of_birth":{"year":1970,"month":2,"day":26}}} 2 | -------------------------------------------------------------------------------- /bench/buf.ml: -------------------------------------------------------------------------------- 1 | let main () = 2 | let buf = Buffer.create 4096 in 3 | let data = Mocks.large_int_assoc in 4 | Benchmark.throughputN ~repeat:3 8 5 | [ 6 | ( "JSON writing with internal buffer", 7 | (fun () -> 8 | Out_channel.with_open_bin "/dev/null" (fun oc -> 9 | ignore (Yojson.Safe.to_channel oc data))), 10 | () ); 11 | ( "JSON writing with provided buffer", 12 | (fun () -> 13 | Out_channel.with_open_bin "/dev/null" (fun oc -> 14 | ignore (Yojson.Safe.to_channel ~buf oc data))), 15 | () ); 16 | ] 17 | |> Benchmark.tabulate 18 | 19 | let () = main () 20 | -------------------------------------------------------------------------------- /bench/dune: -------------------------------------------------------------------------------- 1 | (executables 2 | (names generic buf) 3 | (package yojson-bench) 4 | (public_names yojson-bench -) 5 | (libraries yojson benchmark)) 6 | 7 | (rule 8 | (alias bench-generic) 9 | (deps bench.json) 10 | (action 11 | (run ./generic.exe))) 12 | 13 | (rule 14 | (alias bench-buffer) 15 | (deps bench.json) 16 | (action 17 | (run ./buf.exe))) 18 | 19 | (alias 20 | (name bench) 21 | (deps 22 | (alias bench-generic) 23 | (alias bench-buffer))) 24 | -------------------------------------------------------------------------------- /bench/generic.ml: -------------------------------------------------------------------------------- 1 | let main () = 2 | Benchmark.throughputN ~repeat:3 8 3 | [ 4 | ( "JSON reading", 5 | (fun () -> ignore (Yojson.Safe.from_string Mocks.data)), 6 | () ); 7 | ( "JSON writing", 8 | (fun () -> ignore (Yojson.Safe.to_string Mocks.yojson_data)), 9 | () ); 10 | ( "JSON writing assoc", 11 | (fun () -> ignore (Yojson.Safe.to_string Mocks.large_int_assoc)), 12 | () ); 13 | ( "JSON writing int list", 14 | (fun () -> ignore (Yojson.Safe.to_string Mocks.large_int_list)), 15 | () ); 16 | ( "JSON writing string list", 17 | (fun () -> ignore (Yojson.Safe.to_string Mocks.large_string_list)), 18 | () ); 19 | ( "JSON writing int list to channel", 20 | (fun () -> 21 | Out_channel.with_open_bin "/dev/null" @@ fun oc -> 22 | ignore (Yojson.Safe.to_channel oc Mocks.large_int_list)), 23 | () ); 24 | ( "JSON writing string list to channel", 25 | (fun () -> 26 | Out_channel.with_open_bin "/dev/null" @@ fun oc -> 27 | ignore (Yojson.Safe.to_channel oc Mocks.large_string_list)), 28 | () ); 29 | ( "JSON writing assoc to channel", 30 | (fun () -> 31 | Out_channel.with_open_bin "/dev/null" @@ fun oc -> 32 | ignore (Yojson.Safe.to_channel oc Mocks.large_int_assoc)), 33 | () ); 34 | (let buf = Buffer.create 1000 in 35 | ( "JSON seq roundtrip", 36 | (fun () -> 37 | let stream = 38 | Yojson.Safe.seq_from_string ~buf Mocks.streamable_string 39 | in 40 | ignore (Yojson.Safe.seq_to_string ~buf stream)), 41 | () )); 42 | ] 43 | |> Benchmark.tabulate 44 | 45 | let () = main () 46 | -------------------------------------------------------------------------------- /bench/mocks.ml: -------------------------------------------------------------------------------- 1 | (* chosen by fair dice roll, guaranteed to be large *) 2 | let large = 10_000 3 | 4 | let large_int_assoc = 5 | let ints = List.init large (fun n -> (string_of_int n, `Int n)) in 6 | `Assoc ints 7 | 8 | let data = 9 | In_channel.with_open_text "bench.json" @@ fun ic -> In_channel.input_all ic 10 | 11 | let yojson_data = Yojson.Safe.from_string data 12 | 13 | let large_int_list = 14 | let ints = List.init large (fun n -> `Int n) in 15 | `List ints 16 | 17 | let large_string_list = 18 | let strings = List.init large (fun n -> `String (string_of_int n)) in 19 | `List strings 20 | 21 | let streamable_string = 22 | let buf = Buffer.create (large * 100) in 23 | for i = 1 to large do 24 | Printf.bprintf buf "%d\n" i 25 | done; 26 | Buffer.contents buf 27 | -------------------------------------------------------------------------------- /bin/dune: -------------------------------------------------------------------------------- 1 | (executable 2 | (name ydump) 3 | (public_name ydump) 4 | (package yojson) 5 | (libraries yojson)) 6 | -------------------------------------------------------------------------------- /bin/ydump.ml: -------------------------------------------------------------------------------- 1 | let license = 2 | "Copyright (c) 2010-2012 Martin Jambon\n\ 3 | All rights reserved.\n\n\ 4 | Redistribution and use in source and binary forms, with or without\n\ 5 | modification, are permitted provided that the following conditions\n\ 6 | are met:\n\ 7 | 1. Redistributions of source code must retain the above copyright\n\ 8 | \ notice, this list of conditions and the following disclaimer.\n\ 9 | 2. Redistributions in binary form must reproduce the above copyright\n\ 10 | \ notice, this list of conditions and the following disclaimer in the\n\ 11 | \ documentation and/or other materials provided with the distribution.\n\ 12 | 3. The name of the author may not be used to endorse or promote products\n\ 13 | \ derived from this software without specific prior written permission.\n\n\ 14 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n\ 15 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n\ 16 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n\ 17 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n\ 18 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n\ 19 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n\ 20 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n\ 21 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n\ 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\n\ 23 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" 24 | 25 | let polycat write_one streaming in_file out_file = 26 | let ic, fname = 27 | match in_file with `Stdin -> (stdin, "") | `File s -> (open_in s, s) 28 | in 29 | let oc = match out_file with `Stdout -> stdout | `File s -> open_out s in 30 | let finally () = 31 | if oc != stdout then close_out_noerr oc; 32 | if ic != stdin then close_in_noerr ic 33 | in 34 | try 35 | if streaming then 36 | Seq.iter (write_one oc) (Yojson.Safe.seq_from_channel ~fname ic) 37 | else write_one oc (Yojson.Safe.from_channel ~fname ic); 38 | finally (); 39 | true 40 | with e -> 41 | finally (); 42 | Printf.eprintf "Error:\n"; 43 | (match e with 44 | | Yojson.Json_error s -> Printf.eprintf "%s\n%!" s 45 | | e -> Printf.eprintf "%s\n%!" (Printexc.to_string e)); 46 | false 47 | 48 | let cat sort output_biniou std compact streaming in_file out_file = 49 | if not output_biniou then 50 | let write_one oc x = 51 | let x = if sort then Yojson.Safe.sort x else x in 52 | if compact then Yojson.Safe.to_channel ~std oc x 53 | else Yojson.Safe.pretty_to_channel ~std oc x; 54 | output_char oc '\n' 55 | in 56 | polycat write_one streaming in_file out_file 57 | else 58 | let write_one oc x = output_string oc (Yojson.Safe.to_string x) in 59 | polycat write_one streaming in_file out_file 60 | 61 | let parse_cmdline () = 62 | let out = ref None in 63 | let std = ref false in 64 | let compact = ref false in 65 | let streaming = ref true in 66 | let sort = ref false in 67 | let output_biniou = ref false in 68 | let options = 69 | [ 70 | ( "-o", 71 | Arg.String (fun s -> out := Some s), 72 | "\n Output file" ); 73 | ( "-std", 74 | Arg.Set std, 75 | "\n\ 76 | \ Refuse to print NaN and infinities,\n\ 77 | \ require the root node to be either an object or an array." ); 78 | ( "-c", 79 | Arg.Set compact, 80 | "\n Compact output (default: pretty-printed)" ); 81 | ( "-s", 82 | Arg.Set streaming, 83 | "\n\ 84 | \ Streaming mode: read and write a sequence of JSON values \ 85 | instead of\n\ 86 | \ just one (default)." ); 87 | ( "-u", 88 | Arg.Clear streaming, 89 | "\n\ 90 | \ A single JSON record is expected.\n\ 91 | \ (no longer the default since 1.1.1)" ); 92 | ( "-sort", 93 | Arg.Set sort, 94 | "\n Sort object fields (default: preserve field order)" ); 95 | ("-ob", Arg.Set output_biniou, "Experimental"); 96 | ( "-version", 97 | Arg.Unit 98 | (fun () -> 99 | print_endline Yojson.version; 100 | exit 0), 101 | "Print version of yojson and ydump and exit." ); 102 | ] 103 | in 104 | let files = ref [] in 105 | let anon_fun s = files := s :: !files in 106 | let msg = 107 | Printf.sprintf 108 | "JSON pretty-printer based on the Yojson library for OCaml\n\n\ 109 | %s\n\n\ 110 | JSON pretty-printer based on the Yojson library for OCaml\n\n\ 111 | Usage: %s [input file]" license Sys.argv.(0) 112 | in 113 | Arg.parse options anon_fun msg; 114 | let in_file = 115 | match List.rev !files with 116 | | [] -> `Stdin 117 | | [ x ] -> `File x 118 | | _ -> 119 | Printf.eprintf "Too many input files\n%!"; 120 | exit 1 121 | in 122 | let out_file = match !out with None -> `Stdout | Some x -> `File x in 123 | (!sort, !output_biniou, !std, !compact, !streaming, in_file, out_file) 124 | 125 | let () = 126 | let sort, output_biniou, std, compact, streaming, in_file, out_file = 127 | parse_cmdline () 128 | in 129 | let success = cat sort output_biniou std compact streaming in_file out_file in 130 | if success then exit 0 else exit 1 131 | -------------------------------------------------------------------------------- /doc/dune: -------------------------------------------------------------------------------- 1 | (documentation 2 | (package yojson)) 3 | -------------------------------------------------------------------------------- /doc/index.mld: -------------------------------------------------------------------------------- 1 | {0 The [yojson] library} 2 | 3 | The Yojson library provides runtime functions for reading and writing JSON 4 | data from OCaml. 5 | The design goals of Yojson are the following: 6 | - Allowing type-aware serializers/deserializers 7 | to read and write directly without going through a generic JSON tree, 8 | for efficiency purposes. 9 | - Distinguishing between ints and floats. 10 | - Providing optional extensions of the JSON syntax. 11 | These extensions include comments, arbitrary strings, 12 | optional quotes around field names. 13 | 14 | See {{:http://json.org}JSON specification}. 15 | 16 | Author: Martin Jambon 17 | 18 | {1 Entry point} 19 | 20 | The entry point of this library is the module {!yojson}. 21 | 22 | -------------------------------------------------------------------------------- /dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 2.7) 2 | (name yojson) 3 | 4 | (generate_opam_files true) 5 | (source (github ocaml-community/yojson)) 6 | (license BSD-3-Clause) 7 | 8 | (maintainers "paul-elliot@tarides.com" "nathan@tarides.com" "marek@tarides.com") 9 | (authors "Martin Jambon") 10 | 11 | (package 12 | (name yojson) 13 | (synopsis "Yojson is an optimized parsing and printing library for the JSON format") 14 | (description "Yojson is an optimized parsing and printing library for the JSON format. 15 | 16 | ydump is a pretty-printing command-line program provided with the 17 | yojson package.") 18 | (documentation "https://ocaml.org/p/yojson/latest") 19 | (depends 20 | (ocaml (>= 4.08)) 21 | (alcotest (and :with-test (>= 0.8.5))))) 22 | 23 | (package 24 | (name yojson-five) 25 | (synopsis "Yojson-five is a parsing and printing library for the JSON5 format") 26 | (description "Yojson-five is a parsing and printing library for the JSON5 format. 27 | It supports parsing JSON5 to Yojson.Basic.t and Yojson.Safe.t types.") 28 | (documentation "https://ocaml.org/p/yojson-five/latest") 29 | (depends 30 | (ocaml (>= 4.08)) 31 | (sedlex (>= 2.5)) 32 | (yojson (= :version)) 33 | (alcotest (and :with-test (>= 0.8.5))))) 34 | 35 | (package 36 | (name yojson-bench) 37 | (synopsis "Run Yojson benchmarks") 38 | (description "Yojson benchmarks require `Core_bench` which is not a dependency of Yojson, 39 | because it is not part of the regular installation/testing flow. This is solely 40 | meant for developers that are worried about performance changes in Yojson.") 41 | (depends 42 | (ocaml (>= 4.14)) 43 | (yojson (= :version)) 44 | benchmark)) 45 | -------------------------------------------------------------------------------- /examples/constructing.ml: -------------------------------------------------------------------------------- 1 | (* 2 | dune exec examples/constructing.exe < filter_member "pages" |> flatten |> filter_member "title" 32 | |> filter_string 33 | 34 | let main () = 35 | let json = Yojson.Basic.from_channel stdin in 36 | List.iter print_endline (extract_titles json) 37 | 38 | let () = main () 39 | -------------------------------------------------------------------------------- /lib/basic.cppo.ml: -------------------------------------------------------------------------------- 1 | #define INT 2 | #define FLOAT 3 | #define STRING 4 | 5 | #include "type.ml" 6 | 7 | #include "write.ml" 8 | 9 | module Pretty = struct 10 | #include "prettyprint.ml" 11 | end 12 | 13 | #include "monomorphic.ml" 14 | 15 | #include "write2.ml" 16 | 17 | #include "read.ml" 18 | 19 | module Util = struct 20 | #include "util.ml" 21 | end 22 | 23 | #undef INT 24 | #undef FLOAT 25 | #undef STRING 26 | -------------------------------------------------------------------------------- /lib/basic.cppo.mli: -------------------------------------------------------------------------------- 1 | (** 2 | This module supports standard JSON nodes only. 3 | Arbitrary integers are not supported as they must all fit within the 4 | standard OCaml int type (31 or 63 bits depending on the platform). 5 | 6 | The main advantage of this module is its simplicity. 7 | *) 8 | 9 | #define INT 10 | #define FLOAT 11 | #define STRING 12 | 13 | #include "type.ml" 14 | 15 | #include "write.mli" 16 | 17 | #include "monomorphic.mli" 18 | 19 | #include "write2.mli" 20 | 21 | #include "read.mli" 22 | 23 | (** This module provides combinators for extracting fields from JSON values. *) 24 | module Util : sig 25 | #include "util.mli" 26 | end 27 | 28 | #undef INT 29 | #undef FLOAT 30 | #undef STRING 31 | -------------------------------------------------------------------------------- /lib/codec.ml: -------------------------------------------------------------------------------- 1 | let utf8_of_code buf x = 2 | let add = Buffer.add_char in 3 | 4 | (* Straight <= doesn't work with signed 31-bit ints *) 5 | let maxbits n x = x lsr n = 0 in 6 | 7 | if maxbits 7 x then (* 7 *) 8 | add buf (Char.chr x) 9 | else if maxbits 11 x then ( 10 | (* 5 + 6 *) 11 | add buf (Char.chr (0b11000000 lor ((x lsr 6) land 0b00011111))); 12 | add buf (Char.chr (0b10000000 lor (x land 0b00111111)))) 13 | else if maxbits 16 x then ( 14 | (* 4 + 6 + 6 *) 15 | add buf (Char.chr (0b11100000 lor ((x lsr 12) land 0b00001111))); 16 | add buf (Char.chr (0b10000000 lor ((x lsr 6) land 0b00111111))); 17 | add buf (Char.chr (0b10000000 lor (x land 0b00111111)))) 18 | else if maxbits 21 x then ( 19 | (* 3 + 6 + 6 + 6 *) 20 | add buf (Char.chr (0b11110000 lor ((x lsr 18) land 0b00000111))); 21 | add buf (Char.chr (0b10000000 lor ((x lsr 12) land 0b00111111))); 22 | add buf (Char.chr (0b10000000 lor ((x lsr 6) land 0b00111111))); 23 | add buf (Char.chr (0b10000000 lor (x land 0b00111111)))) 24 | else if maxbits 26 x then ( 25 | (* 2 + 6 + 6 + 6 + 6 *) 26 | add buf (Char.chr (0b11111000 lor ((x lsr 24) land 0b00000011))); 27 | add buf (Char.chr (0b10000000 lor ((x lsr 18) land 0b00111111))); 28 | add buf (Char.chr (0b10000000 lor ((x lsr 12) land 0b00111111))); 29 | add buf (Char.chr (0b10000000 lor ((x lsr 6) land 0b00111111))); 30 | add buf (Char.chr (0b10000000 lor (x land 0b00111111)))) 31 | else ( 32 | assert (maxbits 31 x); 33 | (* 1 + 6 + 6 + 6 + 6 + 6 *) 34 | add buf (Char.chr (0b11111100 lor ((x lsr 30) land 0b00000001))); 35 | add buf (Char.chr (0b10000000 lor ((x lsr 24) land 0b00111111))); 36 | add buf (Char.chr (0b10000000 lor ((x lsr 18) land 0b00111111))); 37 | add buf (Char.chr (0b10000000 lor ((x lsr 12) land 0b00111111))); 38 | add buf (Char.chr (0b10000000 lor ((x lsr 6) land 0b00111111))); 39 | add buf (Char.chr (0b10000000 lor (x land 0b00111111)))) 40 | 41 | let code_of_surrogate_pair i j = 42 | let high10 = i - 0xD800 in 43 | let low10 = j - 0xDC00 in 44 | 0x10000 + ((high10 lsl 10) lor low10) 45 | 46 | let utf8_of_surrogate_pair buf i j = 47 | utf8_of_code buf (code_of_surrogate_pair i j) 48 | -------------------------------------------------------------------------------- /lib/codec.mli: -------------------------------------------------------------------------------- 1 | val utf8_of_code : Buffer.t -> int -> unit 2 | val utf8_of_surrogate_pair : Buffer.t -> int -> int -> unit 3 | -------------------------------------------------------------------------------- /lib/common.ml: -------------------------------------------------------------------------------- 1 | let version = "%%VERSION%%" 2 | 3 | exception Json_error of string 4 | 5 | let json_error s = raise (Json_error s) 6 | 7 | exception End_of_array 8 | exception End_of_object 9 | exception End_of_input 10 | 11 | type lexer_state = { 12 | buf : Buffer.t; (* Buffer used to accumulate substrings *) 13 | mutable lnum : int; (* Current line number (starting from 1) *) 14 | mutable bol : int; 15 | (* Absolute position of the first character of the current line 16 | (starting from 0) *) 17 | mutable fname : string option; (* Name describing the input file *) 18 | } 19 | 20 | module Lexer_state = struct 21 | type t = lexer_state = { 22 | buf : Buffer.t; 23 | mutable lnum : int; 24 | mutable bol : int; 25 | mutable fname : string option; 26 | } 27 | end 28 | 29 | let init_lexer ?buf ?fname ?(lnum = 1) () = 30 | let buf = match buf with None -> Buffer.create 256 | Some buf -> buf in 31 | { buf; lnum; bol = 0; fname } 32 | -------------------------------------------------------------------------------- /lib/common.mli: -------------------------------------------------------------------------------- 1 | val version : string 2 | 3 | exception Json_error of string 4 | (** Exception used: 5 | - in JSON readers, if parsing fails; 6 | - in JSON writers and pretty printing, if [float] value is not allowed in standard JSON. *) 7 | 8 | val json_error : string -> 'a 9 | (** @raise Json_error *) 10 | 11 | type lexer_state = { 12 | buf : Buffer.t; (** Buffer used to accumulate substrings *) 13 | mutable lnum : int; (** Current line number (counting from 1) *) 14 | mutable bol : int; 15 | (** Absolute position of the first character of the current line 16 | (counting from 0) *) 17 | mutable fname : string option; 18 | (** Name referencing the input file in error messages *) 19 | } 20 | 21 | module Lexer_state : sig 22 | type t = lexer_state = { 23 | buf : Buffer.t; 24 | mutable lnum : int; 25 | mutable bol : int; 26 | mutable fname : string option; 27 | } 28 | end 29 | 30 | val init_lexer : 31 | ?buf:Buffer.t -> ?fname:string -> ?lnum:int -> unit -> lexer_state 32 | (** Create a fresh lexer_state record. *) 33 | 34 | (**/**) 35 | (* begin undocumented section *) 36 | 37 | exception End_of_array 38 | exception End_of_object 39 | exception End_of_input 40 | 41 | (* end undocumented section *) 42 | (**/**) 43 | -------------------------------------------------------------------------------- /lib/dune: -------------------------------------------------------------------------------- 1 | (ocamllex read lexer_utils) 2 | 3 | (vendored_dirs mucppo) 4 | 5 | (rule 6 | (targets t.ml) 7 | (deps 8 | (:out t.cppo.ml) 9 | type.ml 10 | monomorphic.ml 11 | prettyprint.ml 12 | write.ml 13 | write2.ml) 14 | (action 15 | (run mucppo/mucppo.exe %{out} -o %{targets}))) 16 | 17 | (rule 18 | (targets t.mli) 19 | (deps 20 | (:out t.cppo.mli) 21 | type.ml 22 | monomorphic.mli 23 | write.mli 24 | write2.mli) 25 | (action 26 | (run mucppo/mucppo.exe %{out} -o %{targets}))) 27 | 28 | (rule 29 | (targets basic.ml) 30 | (deps 31 | (:out basic.cppo.ml) 32 | type.ml 33 | write.ml 34 | prettyprint.ml 35 | monomorphic.ml 36 | write2.ml 37 | read.ml 38 | util.ml) 39 | (action 40 | (run mucppo/mucppo.exe %{out} -o %{targets}))) 41 | 42 | (rule 43 | (targets basic.mli) 44 | (deps 45 | (:out basic.cppo.mli) 46 | type.ml 47 | write.mli 48 | monomorphic.mli 49 | write2.mli 50 | read.mli 51 | util.mli) 52 | (action 53 | (run mucppo/mucppo.exe %{out} -o %{targets}))) 54 | 55 | (rule 56 | (targets safe.ml) 57 | (deps 58 | basic.ml 59 | (:out safe.cppo.ml) 60 | type.ml 61 | safe_to_basic.ml 62 | write.ml 63 | prettyprint.ml 64 | monomorphic.ml 65 | write2.ml 66 | read.ml 67 | util.ml) 68 | (action 69 | (run mucppo/mucppo.exe %{out} -o %{targets}))) 70 | 71 | (rule 72 | (targets safe.mli) 73 | (deps 74 | basic.mli 75 | (:out safe.cppo.mli) 76 | type.ml 77 | monomorphic.mli 78 | safe_to_basic.mli 79 | write.mli 80 | write2.mli 81 | read.mli 82 | util.mli) 83 | (action 84 | (run mucppo/mucppo.exe %{out} -o %{targets}))) 85 | 86 | (rule 87 | (targets raw.ml) 88 | (deps 89 | (:out raw.cppo.ml) 90 | type.ml 91 | write.ml 92 | prettyprint.ml 93 | monomorphic.ml 94 | write2.ml 95 | read.ml 96 | util.ml) 97 | (action 98 | (run mucppo/mucppo.exe %{out} -o %{targets}))) 99 | 100 | (rule 101 | (targets raw.mli) 102 | (deps 103 | (:out raw.cppo.mli) 104 | type.ml 105 | monomorphic.mli 106 | write.mli 107 | write2.mli 108 | read.mli 109 | util.mli) 110 | (action 111 | (run mucppo/mucppo.exe %{out} -o %{targets}))) 112 | 113 | (library 114 | (name yojson) 115 | (public_name yojson) 116 | (modules yojson t basic safe raw common codec lexer_utils) 117 | (synopsis "JSON parsing and printing") 118 | (flags 119 | (:standard -w -27-32))) 120 | -------------------------------------------------------------------------------- /lib/json5/ast.ml: -------------------------------------------------------------------------------- 1 | type t = 2 | | Assoc of (string * t) list 3 | | List of t list 4 | | StringLit of string 5 | | IntLit of string 6 | | FloatLit of string 7 | | Bool of bool 8 | | Null 9 | 10 | let rec to_basic = function 11 | | Assoc l -> `Assoc (List.map (fun (name, obj) -> (name, to_basic obj)) l) 12 | | List l -> `List (List.map to_basic l) 13 | | StringLit s -> `String s 14 | | FloatLit s -> `Float (float_of_string s) 15 | | IntLit s -> `Int (int_of_string s) 16 | | Bool b -> `Bool b 17 | | Null -> `Null 18 | 19 | let rec to_safe = function 20 | | Assoc l -> `Assoc (List.map (fun (name, obj) -> (name, to_safe obj)) l) 21 | | List l -> `List (List.map to_safe l) 22 | | StringLit s -> `String s 23 | | FloatLit s -> `Float (float_of_string s) 24 | | IntLit s -> ( 25 | match int_of_string_opt s with Some i -> `Int i | None -> `Intlit s) 26 | | Bool b -> `Bool b 27 | | Null -> `Null 28 | -------------------------------------------------------------------------------- /lib/json5/basic.ml: -------------------------------------------------------------------------------- 1 | include Yojson.Basic 2 | 3 | include Read.Make (struct 4 | type t = Yojson.Basic.t 5 | 6 | let convert = Ast.to_basic 7 | end) 8 | -------------------------------------------------------------------------------- /lib/json5/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name yojson_five) 3 | (public_name yojson-five) 4 | (libraries yojson sedlex) 5 | (preprocess 6 | (pps sedlex.ppx))) 7 | -------------------------------------------------------------------------------- /lib/json5/errors.ml: -------------------------------------------------------------------------------- 1 | let string_of_position { Lexing.pos_lnum; pos_fname; _ } = 2 | match pos_fname with 3 | | "" -> Printf.sprintf "Line %d" pos_lnum 4 | | fname -> Printf.sprintf "File %s, line %d" fname pos_lnum 5 | -------------------------------------------------------------------------------- /lib/json5/errors.mli: -------------------------------------------------------------------------------- 1 | val string_of_position : Lexing.position -> string 2 | (** [string_of_position pos] returns a string that contains the line and, if 3 | supplied, the filename of the position in a way that's appropriate to include 4 | in an error message *) 5 | -------------------------------------------------------------------------------- /lib/json5/let_syntax.ml: -------------------------------------------------------------------------------- 1 | module Result = struct 2 | let ( let* ) = Result.bind 3 | let ( let+ ) v f = Result.map f v 4 | end 5 | -------------------------------------------------------------------------------- /lib/json5/lexer.ml: -------------------------------------------------------------------------------- 1 | open Let_syntax.Result 2 | 3 | type token = 4 | | OPEN_PAREN 5 | | CLOSE_PAREN 6 | | OPEN_BRACE 7 | | CLOSE_BRACE 8 | | OPEN_BRACKET 9 | | CLOSE_BRACKET 10 | | COLON 11 | | COMMA 12 | | TRUE 13 | | FALSE 14 | | NULL 15 | | FLOAT of string 16 | | INT_OR_FLOAT of string 17 | | INT of string 18 | | STRING of string 19 | | IDENTIFIER_NAME of string 20 | | EOF 21 | 22 | let pp_token ppf = 23 | let ps = Format.pp_print_string ppf in 24 | let pf = Format.fprintf ppf in 25 | function 26 | | OPEN_PAREN -> ps "'('" 27 | | CLOSE_PAREN -> ps "')'" 28 | | OPEN_BRACE -> ps "'{'" 29 | | CLOSE_BRACE -> ps "'}'" 30 | | OPEN_BRACKET -> ps "'['" 31 | | CLOSE_BRACKET -> ps "']'" 32 | | COLON -> ps "':'" 33 | | COMMA -> ps "','" 34 | | TRUE -> ps "'true'" 35 | | FALSE -> ps "'false'" 36 | | NULL -> ps "'null'" 37 | | FLOAT s -> pf "FLOAT %S" s 38 | | INT_OR_FLOAT s -> pf "INT_OR_STRING %S" s 39 | | INT s -> pf "INT %S" s 40 | | STRING s -> pf "%S" s 41 | | IDENTIFIER_NAME s -> pf "IDENTIFIER_NAME %S" s 42 | | EOF -> ps "EOF" 43 | 44 | let lexer_error lexbuf = 45 | let pos_start, _pos_end = Sedlexing.lexing_positions lexbuf in 46 | let location = Errors.string_of_position pos_start in 47 | let msg = 48 | Printf.sprintf "%s: Unexpected character '%s'" location 49 | (Sedlexing.Utf8.lexeme lexbuf) 50 | in 51 | Error msg 52 | 53 | let source_character = [%sedlex.regexp? any] 54 | let line_terminator = [%sedlex.regexp? 0x000A | 0x000D | 0x2028 | 0x2029] 55 | 56 | let line_terminator_sequence = 57 | [%sedlex.regexp? 0x000A | 0x000D, Opt 0x000A | 0x2028 | 0x2029] 58 | 59 | (* NUMBERS, 7.8.3 *) 60 | let non_zero_digit = [%sedlex.regexp? '1' .. '9'] 61 | let decimal_digit = [%sedlex.regexp? '0' .. '9'] 62 | let decimal_digits = [%sedlex.regexp? Plus decimal_digit] 63 | let hex_digit = [%sedlex.regexp? '0' .. '9' | 'a' .. 'f' | 'A' .. 'F'] 64 | let exponent_indicator = [%sedlex.regexp? 'e' | 'E'] 65 | 66 | let signed_integer = 67 | [%sedlex.regexp? decimal_digits | '+', decimal_digits | '-', decimal_digits] 68 | 69 | let exponent_part = [%sedlex.regexp? exponent_indicator, signed_integer] 70 | 71 | let decimal_integer_literal = 72 | [%sedlex.regexp? '0' | non_zero_digit, Opt decimal_digits] 73 | 74 | let hex_integer_literal = 75 | [%sedlex.regexp? "0x", Plus hex_digit | "0X", Plus hex_digit] 76 | 77 | (* float *) 78 | let float_literal = 79 | [%sedlex.regexp? 80 | ( decimal_integer_literal, '.', Opt decimal_digits, Opt exponent_part 81 | | '.', decimal_digits, Opt exponent_part )] 82 | 83 | let json5_float = 84 | [%sedlex.regexp? float_literal | '+', float_literal | '-', float_literal] 85 | 86 | (* int_or_float *) 87 | let int_or_float_literal = 88 | [%sedlex.regexp? decimal_integer_literal, Opt exponent_part] 89 | 90 | let json5_int_or_float = 91 | [%sedlex.regexp? 92 | int_or_float_literal | '+', int_or_float_literal | '-', int_or_float_literal] 93 | 94 | (* int/hex *) 95 | let int_literal = 96 | [%sedlex.regexp? decimal_digits | '+', decimal_digits | '-', decimal_digits] 97 | 98 | let json5_int = 99 | [%sedlex.regexp? 100 | ( hex_integer_literal 101 | | '+', hex_integer_literal 102 | | '-', hex_integer_literal 103 | | int_literal )] 104 | 105 | (* STRING LITERALS, 7.8.4 *) 106 | let unicode_escape_sequence = 107 | [%sedlex.regexp? 'u', hex_digit, hex_digit, hex_digit, hex_digit] 108 | 109 | let single_escape_character = [%sedlex.regexp? Chars {|'"\\bfnrtv|}] 110 | 111 | let escape_character = 112 | [%sedlex.regexp? single_escape_character | decimal_digit | 'x' | 'u'] 113 | 114 | let non_escape_character = 115 | [%sedlex.regexp? Sub (source_character, (escape_character | line_terminator))] 116 | 117 | let character_escape_sequence = 118 | [%sedlex.regexp? single_escape_character | non_escape_character] 119 | 120 | let line_continuation = [%sedlex.regexp? '\\', line_terminator_sequence] 121 | let hex_escape_sequence = [%sedlex.regexp? 'x', hex_digit, hex_digit] 122 | 123 | let escape_sequence = 124 | [%sedlex.regexp? 125 | ( character_escape_sequence 126 | | '0', Opt (decimal_digit, decimal_digit) 127 | | hex_escape_sequence | unicode_escape_sequence )] 128 | 129 | let single_string_character = 130 | [%sedlex.regexp? 131 | ( Sub (source_character, ('\'' | '\\' | line_terminator)) 132 | | '\\', escape_sequence 133 | | line_continuation )] 134 | 135 | let double_string_character = 136 | [%sedlex.regexp? 137 | ( Sub (source_character, ('"' | '\\' | line_terminator)) 138 | | '\\', escape_sequence 139 | | line_continuation )] 140 | 141 | let string_literal = 142 | [%sedlex.regexp? 143 | ( '"', Star double_string_character, '"' 144 | | '\'', Star single_string_character, '\'' )] 145 | 146 | (* IDENTIFIER_NAME (keys in objects) *) 147 | let unicode_combining_mark = [%sedlex.regexp? mn | mc] 148 | let unicode_digit = [%sedlex.regexp? nd] 149 | let unicode_connector_punctuation = [%sedlex.regexp? pc] 150 | let unicode_letter = [%sedlex.regexp? lu | ll | lt | lm | lo | nl] 151 | let zwnj = [%sedlex.regexp? 0x200C] 152 | let zwj = [%sedlex.regexp? 0x200D] 153 | 154 | let identifier_start = 155 | [%sedlex.regexp? unicode_letter | '$' | '_' | '\\', unicode_escape_sequence] 156 | 157 | let identifier_part = 158 | [%sedlex.regexp? 159 | ( identifier_start | unicode_combining_mark | unicode_digit 160 | | unicode_connector_punctuation | zwnj | zwj )] 161 | 162 | let identifier_name = [%sedlex.regexp? identifier_start, Star identifier_part] 163 | 164 | (* COMMENTS 7.4 *) 165 | let single_line_comment_char = 166 | [%sedlex.regexp? Sub (source_character, line_terminator)] 167 | 168 | let single_line_comment = [%sedlex.regexp? "//", Star single_line_comment_char] 169 | let multi_line_not_asterisk_char = [%sedlex.regexp? Sub (source_character, '*')] 170 | let multi_line_not_slash_char = [%sedlex.regexp? Sub (source_character, '/')] 171 | 172 | let multi_line_comment_char = 173 | [%sedlex.regexp? 174 | multi_line_not_asterisk_char | '*', Plus multi_line_not_slash_char] 175 | 176 | let multi_line_comment = 177 | [%sedlex.regexp? "/*", Star multi_line_comment_char, Opt '*', "*/"] 178 | 179 | let comment = [%sedlex.regexp? multi_line_comment | single_line_comment] 180 | 181 | let white_space = 182 | [%sedlex.regexp? 0x0009 | 0x000B | 0x000C | 0x0020 | 0x00A0 | 0xFEFF | zs] 183 | 184 | let string_lex_single lexbuf strbuf = 185 | let lexeme = Sedlexing.Utf8.lexeme in 186 | let rec lex lexbuf strbuf = 187 | match%sedlex lexbuf with 188 | | '\'' -> Ok (Buffer.contents strbuf) 189 | | '\\', escape_sequence -> 190 | let* s = Unescape.unescape (lexeme lexbuf) in 191 | Buffer.add_string strbuf s; 192 | lex lexbuf strbuf 193 | | line_continuation -> lex lexbuf strbuf 194 | | Sub (source_character, ('\'' | line_terminator)) -> 195 | Buffer.add_string strbuf (lexeme lexbuf); 196 | lex lexbuf strbuf 197 | | _ -> lexer_error lexbuf 198 | in 199 | lex lexbuf strbuf 200 | 201 | let string_lex_double lexbuf strbuf = 202 | let lexeme = Sedlexing.Utf8.lexeme in 203 | let rec lex lexbuf strbuf = 204 | match%sedlex lexbuf with 205 | | '"' -> Ok (Buffer.contents strbuf) 206 | | '\\', escape_sequence -> 207 | let* s = Unescape.unescape (lexeme lexbuf) in 208 | Buffer.add_string strbuf s; 209 | lex lexbuf strbuf 210 | | line_continuation -> lex lexbuf strbuf 211 | | Sub (source_character, ('"' | line_terminator)) -> 212 | Buffer.add_string strbuf (lexeme lexbuf); 213 | lex lexbuf strbuf 214 | | _ -> lexer_error lexbuf 215 | in 216 | lex lexbuf strbuf 217 | 218 | let string_lex lexbuf quote = 219 | let strbuf = Buffer.create 200 in 220 | match quote with 221 | | "'" -> string_lex_single lexbuf strbuf 222 | | {|"|} -> string_lex_double lexbuf strbuf 223 | | _ -> Error (Printf.sprintf "Invalid string quote %S" quote) 224 | 225 | let rec lex tokens buf = 226 | let lexeme = Sedlexing.Utf8.lexeme in 227 | let pos, _ = Sedlexing.lexing_positions buf in 228 | match%sedlex buf with 229 | | '(' -> lex ((OPEN_PAREN, pos) :: tokens) buf 230 | | ')' -> lex ((CLOSE_PAREN, pos) :: tokens) buf 231 | | '{' -> lex ((OPEN_BRACE, pos) :: tokens) buf 232 | | '}' -> lex ((CLOSE_BRACE, pos) :: tokens) buf 233 | | '[' -> lex ((OPEN_BRACKET, pos) :: tokens) buf 234 | | ']' -> lex ((CLOSE_BRACKET, pos) :: tokens) buf 235 | | ':' -> lex ((COLON, pos) :: tokens) buf 236 | | ',' -> lex ((COMMA, pos) :: tokens) buf 237 | | Chars {|"'|} -> 238 | let* s = string_lex buf (lexeme buf) in 239 | lex ((STRING s, pos) :: tokens) buf 240 | | multi_line_comment | single_line_comment | white_space | line_terminator -> 241 | lex tokens buf 242 | | "true" -> lex ((TRUE, pos) :: tokens) buf 243 | | "false" -> lex ((FALSE, pos) :: tokens) buf 244 | | "null" -> lex ((NULL, pos) :: tokens) buf 245 | | json5_float -> 246 | let s = lexeme buf in 247 | lex ((FLOAT s, pos) :: tokens) buf 248 | | json5_int -> 249 | let s = lexeme buf in 250 | lex ((INT s, pos) :: tokens) buf 251 | | json5_int_or_float -> 252 | let s = lexeme buf in 253 | lex ((INT_OR_FLOAT s, pos) :: tokens) buf 254 | | identifier_name -> 255 | let s = lexeme buf in 256 | lex ((IDENTIFIER_NAME s, pos) :: tokens) buf 257 | | eof -> Ok (List.rev ((EOF, pos) :: tokens)) 258 | | _ -> lexer_error buf 259 | -------------------------------------------------------------------------------- /lib/json5/parser.ml: -------------------------------------------------------------------------------- 1 | open Let_syntax.Result 2 | 3 | let parser_error pos error = 4 | let location = Errors.string_of_position pos in 5 | let msg = Printf.sprintf "%s: %s" location error in 6 | Error msg 7 | 8 | let rec parse_list acc = function 9 | | [] -> Error "Unexpected end of input" 10 | | [ (Lexer.EOF, pos) ] -> parser_error pos "Unexpected end of input" 11 | | (Lexer.CLOSE_BRACKET, _) :: xs -> Ok (acc, xs) 12 | | xs -> ( 13 | let* v, xs = parse xs in 14 | match xs with 15 | | [] -> Error "Unexpected end of input" 16 | | [ (Lexer.EOF, pos) ] -> parser_error pos "Unexpected end of input" 17 | | (Lexer.CLOSE_BRACKET, _) :: xs | (COMMA, _) :: (CLOSE_BRACKET, _) :: xs 18 | -> 19 | Ok (v :: acc, xs) 20 | | (COMMA, _) :: xs -> parse_list (v :: acc) xs 21 | | (x, pos) :: _ -> 22 | let s = 23 | Format.asprintf "Unexpected list token: %a" Lexer.pp_token x 24 | in 25 | parser_error pos s) 26 | 27 | and parse_assoc acc = function 28 | | [] -> Error "Unexpected end of input" 29 | | [ (Lexer.EOF, pos) ] -> parser_error pos "Unexpected end of input" 30 | | (CLOSE_BRACE, _) :: xs -> Ok (acc, xs) 31 | | (STRING k, _) :: xs | (IDENTIFIER_NAME k, _) :: xs -> ( 32 | match xs with 33 | | [] -> Error "Unexpected end of input" 34 | | [ (Lexer.EOF, pos) ] -> parser_error pos "Unexpected end of input" 35 | | (Lexer.COLON, _) :: xs -> ( 36 | let* v, xs = parse xs in 37 | let item = (k, v) in 38 | match xs with 39 | | [] -> Error "Unexpected end of input" 40 | | [ (Lexer.EOF, pos) ] -> parser_error pos "Unexpected end of input" 41 | | (CLOSE_BRACE, _) :: xs | (COMMA, _) :: (CLOSE_BRACE, _) :: xs -> 42 | Ok (item :: acc, xs) 43 | | (COMMA, _) :: xs -> parse_assoc (item :: acc) xs 44 | | (x, pos) :: _ -> 45 | let s = 46 | Format.asprintf "Unexpected assoc list token: %a" Lexer.pp_token 47 | x 48 | in 49 | parser_error pos s) 50 | | (x, pos) :: _ -> 51 | let s = 52 | Format.asprintf "Expected %a but found %a" Lexer.pp_token 53 | Lexer.COLON Lexer.pp_token x 54 | in 55 | parser_error pos s) 56 | | (x, pos) :: _ -> 57 | let s = 58 | Format.asprintf "Expected string or identifier but found %a" 59 | Lexer.pp_token x 60 | in 61 | parser_error pos s 62 | 63 | and parse = function 64 | | [] -> Error "Unexpected end of input" 65 | | [ (Lexer.EOF, pos) ] -> parser_error pos "Unexpected end of input" 66 | | (token, pos) :: xs -> ( 67 | match token with 68 | | TRUE -> Ok (Ast.Bool true, xs) 69 | | FALSE -> Ok (Bool false, xs) 70 | | NULL -> Ok (Null, xs) 71 | | INT v -> Ok (IntLit v, xs) 72 | | FLOAT v -> Ok (FloatLit v, xs) 73 | | INT_OR_FLOAT v -> Ok (FloatLit v, xs) 74 | | STRING s -> Ok (StringLit s, xs) 75 | | OPEN_BRACKET -> 76 | let+ l, xs = parse_list [] xs in 77 | (Ast.List (List.rev l), xs) 78 | | OPEN_BRACE -> 79 | let+ a, xs = parse_assoc [] xs in 80 | (Ast.Assoc (List.rev a), xs) 81 | | x -> 82 | let s = Format.asprintf "Unexpected token: %a" Lexer.pp_token x in 83 | parser_error pos s) 84 | 85 | let parse_from_lexbuf ?(fname = "") ?(lnum = 1) lexbuffer = 86 | Sedlexing.set_filename lexbuffer fname; 87 | let pos = 88 | { Lexing.pos_fname = fname; pos_lnum = lnum; pos_bol = 0; pos_cnum = 0 } 89 | in 90 | Sedlexing.set_position lexbuffer pos; 91 | let* tokens = Lexer.lex [] lexbuffer in 92 | let+ ast, _unparsed = parse tokens in 93 | ast 94 | 95 | let parse_from_string ?fname ?lnum input = 96 | parse_from_lexbuf (Sedlexing.Utf8.from_string input) ?fname ?lnum 97 | 98 | let parse_from_channel ?fname ?lnum ic = 99 | parse_from_lexbuf (Sedlexing.Utf8.from_channel ic) ?fname ?lnum 100 | 101 | let parse_from_file ?fname ?lnum filename = 102 | let ic = open_in filename in 103 | let out = parse_from_channel ?fname ?lnum ic in 104 | close_in ic; 105 | out 106 | -------------------------------------------------------------------------------- /lib/json5/read.ml: -------------------------------------------------------------------------------- 1 | open Let_syntax.Result 2 | 3 | module type S = sig 4 | type t 5 | 6 | val convert : Ast.t -> t 7 | end 8 | 9 | module type Out = sig 10 | type t 11 | 12 | val from_string : ?fname:string -> ?lnum:int -> string -> (t, string) result 13 | 14 | val from_channel : 15 | ?fname:string -> ?lnum:int -> in_channel -> (t, string) result 16 | 17 | val from_file : ?fname:string -> ?lnum:int -> string -> (t, string) result 18 | end 19 | 20 | module Make (F : S) : Out with type t = F.t = struct 21 | type t = F.t 22 | 23 | let from_string ?fname ?lnum input = 24 | let+ ast = Parser.parse_from_string ?fname ?lnum input in 25 | F.convert ast 26 | 27 | let from_channel ?fname ?lnum ic = 28 | let+ ast = Parser.parse_from_channel ?fname ?lnum ic in 29 | F.convert ast 30 | 31 | let from_file ?fname ?lnum file = 32 | let+ ast = Parser.parse_from_file ?fname ?lnum file in 33 | F.convert ast 34 | end 35 | -------------------------------------------------------------------------------- /lib/json5/safe.ml: -------------------------------------------------------------------------------- 1 | include Yojson.Safe 2 | 3 | include Read.Make (struct 4 | type t = Yojson.Safe.t 5 | 6 | let convert = Ast.to_safe 7 | end) 8 | -------------------------------------------------------------------------------- /lib/json5/unescape.ml: -------------------------------------------------------------------------------- 1 | open Let_syntax.Result 2 | 3 | let ( % ) = Int.logor 4 | let ( << ) = Int.shift_left 5 | let ( >> ) = Int.shift_right 6 | let ( & ) = Int.logand 7 | 8 | let utf_8_string_of_unicode i = 9 | if i <= 0x007F then ( 10 | let b = Bytes.create 1 in 11 | Bytes.set_int8 b 0 i; 12 | Ok (Bytes.to_string b)) 13 | else if i <= 0x07FF then ( 14 | let five_high_bits = i >> 6 & 0b11111 in 15 | let six_low_bits = i & 0b111111 in 16 | let high = 0b11000000 % five_high_bits << 8 in 17 | let low = 0b10000000 % six_low_bits in 18 | let n = high % low in 19 | let b = Bytes.create 2 in 20 | Bytes.set_int16_be b 0 n; 21 | Ok (Bytes.to_string b)) 22 | else if i <= 0xFFFF then ( 23 | let four_high_bits = i >> 12 & 0b1111 in 24 | let six_mid_bits = i >> 6 & 0b111111 in 25 | let six_low_bits = i & 0b111111 in 26 | let high = 0b11100000 % four_high_bits << 16 in 27 | let mid = 0b10000000 % six_mid_bits << 8 in 28 | let low = 0b10000000 % six_low_bits in 29 | let n = high % mid % low in 30 | let b = Bytes.create 3 in 31 | Bytes.set_int32_be b 0 (Int32.of_int n); 32 | Ok (Bytes.to_string b)) 33 | else if i <= 0x10FFFF then ( 34 | let three_hh_bits = i >> 18 & 0b111 in 35 | let six_hl_bits = i >> 12 & 0b111111 in 36 | let six_lh_bits = i >> 6 & 0b111111 in 37 | let six_ll_bits = i & 0b111111 in 38 | let hh = 0b11110000 % three_hh_bits << 24 in 39 | let hl = 0b10000000 % six_hl_bits << 16 in 40 | let lh = 0b10000000 % six_lh_bits << 8 in 41 | let ll = 0b10000000 % six_ll_bits in 42 | let n = hh % hl % lh % ll in 43 | let b = Bytes.create 4 in 44 | Bytes.set_int32_be b 0 (Int32.of_int n); 45 | Ok (Bytes.to_string b)) 46 | else Error (Format.sprintf "invalid code point %X" i) 47 | 48 | let unescape str = 49 | if String.length str < 2 then 50 | Error (Format.sprintf "too small escape sequence %s" str) 51 | else 52 | match str.[1] with 53 | | 'u' -> 54 | let escape_chars = String.sub str 2 4 in 55 | let* as_int = 56 | Format.sprintf "0x%s" escape_chars |> int_of_string_opt |> function 57 | | Some x -> Ok x 58 | | None -> Error (Format.sprintf "bad escape sequence %s" escape_chars) 59 | in 60 | utf_8_string_of_unicode as_int 61 | | 'x' -> 62 | let escape_chars = String.sub str 2 2 in 63 | let* as_int = 64 | Format.sprintf "0x%s" escape_chars |> int_of_string_opt |> function 65 | | Some x -> Ok x 66 | | None -> Error (Format.sprintf "bad escape sequence %s" escape_chars) 67 | in 68 | utf_8_string_of_unicode as_int 69 | (* https://spec.json5.org/#escapes table 1 *) 70 | | 'b' -> Ok "\b" 71 | | 'n' -> Ok "\n" 72 | | 'r' -> Ok "\r" 73 | | 't' -> Ok "\t" 74 | | 'f' -> Ok "\x0C" 75 | | 'v' -> Ok "\x0B" 76 | | '0' -> 77 | if String.length str = 2 then Ok "\x00" 78 | else if String.length str = 4 then 79 | let octal_str = String.(sub str 2 2) in 80 | let* as_int = 81 | Format.sprintf "0o%s" octal_str |> int_of_string_opt |> function 82 | | Some x -> Ok x 83 | | None -> Error (Format.sprintf "bad escape sequence %s" octal_str) 84 | in 85 | utf_8_string_of_unicode as_int 86 | else Error (Format.sprintf "invalid octal sequence %s" str) 87 | | '1' .. '9' -> Error (Format.sprintf "invalid escape sequence %c" str.[1]) 88 | | c -> 89 | (* According to https://spec.json5.org/#escapes : "If any other 90 | character follows a reverse solidus, except for the decimal 91 | digits 1 through 9, that character will be included in the 92 | string, but the reverse solidus will not.". 93 | Alternatively, apostrophe, quotation mark and reverse solidus 94 | should also be included in the string without the leading 95 | reverse solidus. *) 96 | Ok (String.make 1 c) 97 | -------------------------------------------------------------------------------- /lib/json5/yojson_five.ml: -------------------------------------------------------------------------------- 1 | module Safe = Safe 2 | module Basic = Basic 3 | -------------------------------------------------------------------------------- /lib/json5/yojson_five.mli: -------------------------------------------------------------------------------- 1 | module Safe : sig 2 | type t = Yojson.Safe.t 3 | 4 | val from_string : ?fname:string -> ?lnum:int -> string -> (t, string) result 5 | 6 | val from_channel : 7 | ?fname:string -> ?lnum:int -> in_channel -> (t, string) result 8 | 9 | val from_file : ?fname:string -> ?lnum:int -> string -> (t, string) result 10 | 11 | val to_string : 12 | ?buf:Buffer.t -> ?len:int -> ?suf:string -> ?std:bool -> t -> string 13 | 14 | val to_channel : 15 | ?buf:Stdlib.Buffer.t -> 16 | ?len:int -> 17 | ?suf:string -> 18 | ?std:bool -> 19 | Stdlib.out_channel -> 20 | t -> 21 | unit 22 | 23 | val to_output : 24 | ?buf:Stdlib.Buffer.t -> 25 | ?len:int -> 26 | ?suf:string -> 27 | ?std:bool -> 28 | < output : string -> int -> int -> int > -> 29 | t -> 30 | unit 31 | 32 | val to_file : ?len:int -> ?std:bool -> ?suf:string -> string -> t -> unit 33 | val pp : Format.formatter -> t -> unit 34 | val equal : t -> t -> bool 35 | end 36 | 37 | module Basic : sig 38 | type t = Yojson.Basic.t 39 | 40 | val from_string : ?fname:string -> ?lnum:int -> string -> (t, string) result 41 | 42 | val from_channel : 43 | ?fname:string -> ?lnum:int -> in_channel -> (t, string) result 44 | 45 | val from_file : ?fname:string -> ?lnum:int -> string -> (t, string) result 46 | 47 | val to_string : 48 | ?buf:Buffer.t -> ?len:int -> ?suf:string -> ?std:bool -> t -> string 49 | 50 | val to_channel : 51 | ?buf:Stdlib.Buffer.t -> 52 | ?len:int -> 53 | ?suf:string -> 54 | ?std:bool -> 55 | Stdlib.out_channel -> 56 | t -> 57 | unit 58 | 59 | val to_output : 60 | ?buf:Stdlib.Buffer.t -> 61 | ?len:int -> 62 | ?suf:string -> 63 | ?std:bool -> 64 | < output : string -> int -> int -> int > -> 65 | t -> 66 | unit 67 | 68 | val to_file : ?len:int -> ?std:bool -> ?suf:string -> string -> t -> unit 69 | val pp : Format.formatter -> t -> unit 70 | val equal : t -> t -> bool 71 | end 72 | -------------------------------------------------------------------------------- /lib/lexer_utils.mll: -------------------------------------------------------------------------------- 1 | rule read_junk buf n = parse 2 | | eof { () } 3 | | _ { 4 | if n <= 0 then () 5 | else begin 6 | Buffer.add_char buf (Lexing.lexeme_char lexbuf 0); 7 | read_junk buf (n - 1) lexbuf 8 | end 9 | } 10 | 11 | { 12 | let read_junk_without_positions buf n (lexbuf : Lexing.lexbuf) = 13 | let lex_abs_pos = lexbuf.lex_abs_pos in 14 | let lex_start_pos = lexbuf.lex_start_pos in 15 | read_junk buf n lexbuf; 16 | lexbuf.lex_start_pos <- lex_start_pos + 1; 17 | lexbuf.lex_abs_pos <- lex_abs_pos 18 | } 19 | -------------------------------------------------------------------------------- /lib/monomorphic.ml: -------------------------------------------------------------------------------- 1 | let rec pp fmt = 2 | function 3 | | `Null -> Format.pp_print_string fmt "`Null" 4 | | `Bool x -> 5 | Format.fprintf fmt "`Bool (@["; 6 | Format.fprintf fmt "%B" x; 7 | Format.fprintf fmt "@])" 8 | #ifdef INT 9 | | `Int x -> 10 | Format.fprintf fmt "`Int (@["; 11 | Format.fprintf fmt "%d" x; 12 | Format.fprintf fmt "@])" 13 | #endif 14 | #ifdef INTLIT 15 | | `Intlit x -> 16 | Format.fprintf fmt "`Intlit (@["; 17 | Format.fprintf fmt "%S" x; 18 | Format.fprintf fmt "@])" 19 | #endif 20 | #ifdef FLOAT 21 | | `Float x -> 22 | Format.fprintf fmt "`Float (@["; 23 | Format.fprintf fmt "%F" x; 24 | Format.fprintf fmt "@])" 25 | #endif 26 | #ifdef FLOATLIT 27 | | `Floatlit x -> 28 | Format.fprintf fmt "`Floatlit (@["; 29 | Format.fprintf fmt "%S" x; 30 | Format.fprintf fmt "@])" 31 | #endif 32 | #ifdef STRING 33 | | `String x -> 34 | Format.fprintf fmt "`String (@["; 35 | Format.fprintf fmt "%S" x; 36 | Format.fprintf fmt "@])" 37 | #endif 38 | #ifdef STRINGLIT 39 | | `Stringlit x -> 40 | Format.fprintf fmt "`Stringlit (@["; 41 | Format.fprintf fmt "%S" x; 42 | Format.fprintf fmt "@])" 43 | #endif 44 | | `Assoc xs -> 45 | Format.fprintf fmt "`Assoc (@["; 46 | Format.fprintf fmt "@[<2>["; 47 | ignore (List.fold_left 48 | (fun sep (key, value) -> 49 | if sep then 50 | Format.fprintf fmt ";@ "; 51 | Format.fprintf fmt "(@["; 52 | Format.fprintf fmt "%S" key; 53 | Format.fprintf fmt ",@ "; 54 | pp fmt value; 55 | Format.fprintf fmt "@])"; 56 | true) false xs); 57 | Format.fprintf fmt "@,]@]"; 58 | Format.fprintf fmt "@])" 59 | | `List xs -> 60 | Format.fprintf fmt "`List (@["; 61 | Format.fprintf fmt "@[<2>["; 62 | ignore (List.fold_left 63 | (fun sep x -> 64 | if sep then 65 | Format.fprintf fmt ";@ "; 66 | pp fmt x; 67 | true) false xs); 68 | Format.fprintf fmt "@,]@]"; 69 | Format.fprintf fmt "@])" 70 | 71 | let show x = 72 | Format.asprintf "%a" pp x 73 | 74 | let rec equal a b = 75 | match a, b with 76 | | `Null, `Null -> true 77 | | `Bool a, `Bool b -> a = b 78 | #ifdef INT 79 | | `Int a, `Int b -> a = b 80 | #endif 81 | #ifdef INTLIT 82 | | `Intlit a, `Intlit b -> a = b 83 | #endif 84 | #ifdef FLOAT 85 | | `Float a, `Float b -> a = b 86 | #endif 87 | #ifdef FLOATLIT 88 | | `Floatlit a, `Floatlit b -> a = b 89 | #endif 90 | #ifdef STRING 91 | | `String a, `String b -> a = b 92 | #endif 93 | #ifdef STRINGLIT 94 | | `Stringlit a, `Stringlit b -> a = b 95 | #endif 96 | | `Assoc xs, `Assoc ys -> 97 | let compare_keys = fun (key, _) (key', _) -> String.compare key key' in 98 | let xs = List.stable_sort compare_keys xs in 99 | let ys = List.stable_sort compare_keys ys in 100 | (match List.for_all2 (fun (key, value) (key', value') -> 101 | match key = key' with 102 | | false -> false 103 | | true -> equal value value') xs ys with 104 | | result -> result 105 | | exception Invalid_argument _ -> 106 | (* the lists were of different lengths, thus unequal *) 107 | false) 108 | | `List xs, `List ys -> 109 | (match List.for_all2 equal xs ys with 110 | | result -> result 111 | | exception Invalid_argument _ -> 112 | (* the lists were of different lengths, thus unequal *) 113 | false) 114 | | _ -> false 115 | -------------------------------------------------------------------------------- /lib/monomorphic.mli: -------------------------------------------------------------------------------- 1 | val pp : Format.formatter -> t -> unit 2 | (** Pretty printer, useful for debugging *) 3 | 4 | val show : t -> string 5 | (** Convert value to string, useful for debugging *) 6 | 7 | val equal : t -> t -> bool 8 | (** [equal a b] is the monomorphic equality. 9 | Determines whether two JSON values are considered equal. In the case of 10 | JSON objects, the order of the keys does not matter, except for 11 | duplicate keys which will be considered equal as long as they are in the 12 | same input order. 13 | *) 14 | -------------------------------------------------------------------------------- /lib/mucppo/dune: -------------------------------------------------------------------------------- 1 | (executable 2 | (name mucppo)) 3 | -------------------------------------------------------------------------------- /lib/mucppo/mucppo.ml: -------------------------------------------------------------------------------- 1 | (* mucppo Copyright 2023-2024 Marek Kubica 2 | * Released under CC0 license, freely available to all. 3 | * 4 | * Simple, no dependency cppo replacement to be embedded into builds. 5 | * 6 | * Contains a bare subset of cppo features to eliminate it as a dependency. 7 | * For more info check the project page at 8 | * https://github.com/Leonidas-from-XIV/mucppo 9 | *) 10 | 11 | let version_triple major minor patch = (major, minor, patch) 12 | let current_version = Scanf.sscanf Sys.ocaml_version "%u.%u.%u" version_triple 13 | let greater_or_equal (v : int * int * int) = current_version >= v 14 | 15 | module PrintingState : sig 16 | type t 17 | 18 | val empty : t 19 | val is_empty : t -> bool 20 | val flip_top : t -> t 21 | val latest_was_triggered : t -> bool 22 | val pop : t -> t 23 | val push : bool -> t -> t 24 | val should_print : t -> bool 25 | end = struct 26 | type state = { state : bool; was_true : bool } 27 | type t = state list 28 | 29 | let empty = [ { state = true; was_true = false } ] 30 | let is_empty (x : t) = x = empty 31 | 32 | let flip_top = function 33 | | [] -> failwith "Output stack empty, invalid state" 34 | | x :: xs -> { state = not x.state; was_true = true } :: xs 35 | 36 | let latest_was_triggered l = (List.hd l).was_true 37 | let pop = List.tl 38 | let push state l = { state; was_true = state } :: l 39 | 40 | let should_print = 41 | List.fold_left (fun acc { state; was_true = _ } -> state && acc) true 42 | end 43 | 44 | module Variables = struct 45 | module Map = Map.Make (String) 46 | 47 | let is_defined name = Map.mem name 48 | let define name = Map.add name () 49 | let undefine name = Map.remove name 50 | let empty = Map.empty 51 | end 52 | 53 | (* for OCaml 4.02 *) 54 | let string_equal = (=) 55 | 56 | let starts_with ~prefix s = 57 | let len = String.length prefix in 58 | String.length s >= len && string_equal (String.sub s 0 len) prefix 59 | 60 | let is_if_statement = starts_with ~prefix:"#if" 61 | let is_elif_defined_statement = starts_with ~prefix:"#elif defined" 62 | let is_include_statement = starts_with ~prefix:"#include" 63 | let is_define_statement = starts_with ~prefix:"#define" 64 | let is_undef_statement = starts_with ~prefix:"#undef" 65 | let is_ifdef = starts_with ~prefix:"#ifdef" 66 | let filename_of_include s = Scanf.sscanf s "#include %S" (fun x -> x) 67 | let variable_of_define s = Scanf.sscanf s "#define %s" (fun x -> x) 68 | let variable_of_undef s = Scanf.sscanf s "#undef %s" (fun x -> x) 69 | let variable_of_ifdef s = Scanf.sscanf s "#ifdef %s" (fun x -> x) 70 | let variable_of_elif_defined s = Scanf.sscanf s "#elif defined %s" (fun x -> x) 71 | 72 | let is_ocaml_version s = 73 | (* Sscanf.sscanf_opt exists but only since 5.0 *) 74 | match Scanf.sscanf s "#if OCAML_VERSION >= (%u, %u, %u)" version_triple with 75 | | v -> Some v 76 | | exception _ -> None 77 | 78 | module State = struct 79 | type t = { 80 | (* print state *) 81 | ps : PrintingState.t; 82 | vars : unit Variables.Map.t; 83 | } 84 | 85 | let conditional_triggered s = { s with ps = PrintingState.flip_top s.ps } 86 | let end_conditional s = { s with ps = PrintingState.pop s.ps } 87 | let triggered_before s = PrintingState.latest_was_triggered s.ps 88 | let start_conditional v s = { s with ps = PrintingState.push v s.ps } 89 | let should_output { ps; vars = _ } = PrintingState.should_print ps 90 | 91 | let don't_print s = 92 | match should_output s with 93 | | false -> s 94 | | true -> { s with ps = PrintingState.flip_top s.ps } 95 | 96 | let finished { ps; vars = _ } = PrintingState.is_empty ps 97 | let empty = { ps = PrintingState.empty; vars = Variables.empty } 98 | let define v s = { s with vars = Variables.define v s.vars } 99 | let undefine v s = { s with vars = Variables.undefine v s.vars } 100 | let is_defined v { ps = _; vars } = Variables.is_defined v vars 101 | end 102 | 103 | let output_endline oc s = 104 | output_string oc s; 105 | output_char oc '\n' 106 | 107 | let rec loop ic oc ~lineno ~filename st = 108 | match input_line ic with 109 | | line -> ( 110 | let next = loop ic oc ~lineno:(succ lineno) ~filename in 111 | match String.trim line with 112 | | "#else" -> ( 113 | match State.triggered_before st with 114 | | true -> next (State.don't_print st) 115 | | false -> next (State.conditional_triggered st)) 116 | | "#endif" -> next (State.end_conditional st) 117 | | trimmed_line when is_define_statement trimmed_line -> 118 | let var = variable_of_define trimmed_line in 119 | let st = State.define var st in 120 | next st 121 | | trimmed_line when is_undef_statement trimmed_line -> 122 | let var = variable_of_undef trimmed_line in 123 | let st = State.undefine var st in 124 | next st 125 | | trimmed_line when is_include_statement trimmed_line -> 126 | let filename = filename_of_include trimmed_line in 127 | let included_ic = open_in filename in 128 | loop included_ic oc ~lineno:1 ~filename st; 129 | next st 130 | | trimmed_line when is_ifdef trimmed_line -> 131 | let var = variable_of_ifdef trimmed_line in 132 | let is_defined = State.is_defined var st in 133 | let st = State.start_conditional is_defined st in 134 | next st 135 | | trimmed_line when is_if_statement trimmed_line -> ( 136 | match is_ocaml_version line with 137 | | None -> 138 | failwith 139 | (Printf.sprintf "Parsing #if in file %s line %d failed, exiting" 140 | filename lineno) 141 | | Some (major, minor, patch) -> 142 | next 143 | (State.start_conditional 144 | (greater_or_equal (major, minor, patch)) 145 | st)) 146 | | trimmed_line when is_elif_defined_statement trimmed_line -> ( 147 | match State.triggered_before st with 148 | | true -> next (State.don't_print st) 149 | | false -> ( 150 | let var = variable_of_elif_defined trimmed_line in 151 | match State.is_defined var st with 152 | | true -> next (State.conditional_triggered st) 153 | | false -> next st)) 154 | | _trimmed_line -> 155 | if State.should_output st then output_endline oc line; 156 | next st) 157 | | exception End_of_file -> 158 | if not (State.finished st) then 159 | failwith "Output stack messed up, missing #endif?" 160 | 161 | let () = 162 | let output_file = ref None in 163 | let input_file = ref None in 164 | let speclist = 165 | [ 166 | ( "-o", 167 | Arg.String (fun filename -> output_file := Some filename), 168 | "Set output file name" ); 169 | ] 170 | in 171 | let anonymous filename = input_file := Some filename in 172 | let usage = "mucppo -o " in 173 | Arg.parse speclist anonymous usage; 174 | let ic, filename = 175 | match !input_file with 176 | | Some filename -> (open_in filename, filename) 177 | | None -> (stdin, "") 178 | in 179 | let oc = 180 | match !output_file with 181 | | Some filename -> open_out filename 182 | | None -> stdout 183 | in 184 | loop ic oc ~lineno:1 ~filename State.empty; 185 | close_in ic; 186 | close_out oc 187 | -------------------------------------------------------------------------------- /lib/prettyprint.ml: -------------------------------------------------------------------------------- 1 | (* 2 | Pretty-print JSON data in an attempt to maximize readability. 3 | 4 | 1. What fits on one line stays on one line. 5 | 2. What doesn't fit on one line gets printed more vertically so as to not 6 | exceed a reasonable page width, if possible. 7 | 8 | Arrays containing only simple elements ("atoms") are pretty-printed with 9 | end-of-line wrapping like ordinary text: 10 | 11 | [ 12 | "hello", "hello", "hello", "hello", "hello", "hello", "hello", "hello", 13 | "hello", "hello", "hello", "hello", "hello", "hello", "hello", "hello" 14 | ] 15 | 16 | Other arrays are printed either horizontally or vertically depending 17 | on whether they fit on a single line: 18 | 19 | [ { "hello": "world" }, { "hello": "world" }, { "hello": "world" } ] 20 | 21 | or 22 | 23 | [ 24 | { "hello": "world" }, 25 | { "hello": "world" }, 26 | { "hello": "world" }, 27 | { "hello": "world" } 28 | ] 29 | *) 30 | 31 | let pp_list sep ppx out l = 32 | let pp_sep out () = Format.fprintf out "%s@ " sep in 33 | Format.pp_print_list ~pp_sep ppx out l 34 | 35 | let is_atom (x: [> t]) = 36 | match x with 37 | | `Null 38 | | `Bool _ 39 | | `Int _ 40 | | `Float _ 41 | | `String _ 42 | | `Intlit _ 43 | | `Floatlit _ 44 | | `Stringlit _ 45 | | `List [] 46 | | `Assoc [] -> true 47 | | `List _ 48 | | `Assoc _ -> false 49 | 50 | let is_atom_list l = 51 | List.for_all is_atom l 52 | 53 | (* 54 | inside_box: indicates that we're already within a box that imposes 55 | a certain style and we shouldn't create a new one. This is used for 56 | printing field values like this: 57 | 58 | foo: [ 59 | bar 60 | ] 61 | 62 | rather than something else like 63 | 64 | foo: 65 | [ 66 | bar 67 | ] 68 | *) 69 | let rec format ~inside_box (out : Format.formatter) (x : t) : unit = 70 | match x with 71 | | `Null -> Format.pp_print_string out "null" 72 | | `Bool x -> Format.pp_print_bool out x 73 | #ifdef INT 74 | | `Int x -> Format.pp_print_string out (json_string_of_int x) 75 | #endif 76 | #ifdef FLOAT 77 | | `Float x -> 78 | Format.pp_print_string out (json_string_of_float x) 79 | #endif 80 | #ifdef STRING 81 | | `String s -> Format.pp_print_string out (json_string_of_string s) 82 | #endif 83 | #ifdef INTLIT 84 | | `Intlit s -> Format.pp_print_string out s 85 | #endif 86 | #ifdef FLOATLIT 87 | | `Floatlit s -> Format.pp_print_string out s 88 | #endif 89 | #ifdef STRINGLIT 90 | | `Stringlit s -> Format.pp_print_string out s 91 | #endif 92 | | `List [] -> Format.pp_print_string out "[]" 93 | | `List l -> 94 | if not inside_box then Format.fprintf out "@["; 95 | if is_atom_list l then 96 | (* use line wrapping like we would do for a paragraph of text *) 97 | Format.fprintf out "[@;<1 0>@[%a@]@;<1 -2>]" 98 | (pp_list "," (format ~inside_box:false)) l 99 | else 100 | (* print the elements horizontally if they fit on the line, 101 | otherwise print them in a column *) 102 | Format.fprintf out "[@;<1 0>@[%a@]@;<1 -2>]" 103 | (pp_list "," (format ~inside_box:false)) l; 104 | if not inside_box then Format.fprintf out "@]"; 105 | | `Assoc [] -> Format.pp_print_string out "{}" 106 | | `Assoc l -> 107 | if not inside_box then Format.fprintf out "@["; 108 | Format.fprintf out "{@;<1 0>%a@;<1 -2>}" (pp_list "," (format_field)) l; 109 | if not inside_box then Format.fprintf out "@]"; 110 | 111 | and format_field out (name, x) = 112 | Format.fprintf out "@[%s: %a@]" (json_string_of_string name) (format ~inside_box:true) x 113 | 114 | (* [std] argument to be deprecated *) 115 | let pp ?(std = true) out x = 116 | Format.fprintf out "@[%a@]" (format ~inside_box:true) (x :> t) 117 | 118 | let to_string ?std x = 119 | Format.asprintf "%a" (pp ?std) x 120 | 121 | let to_channel ?std oc x = 122 | let fmt = Format.formatter_of_out_channel oc in 123 | Format.fprintf fmt "%a@?" (pp ?std) x 124 | -------------------------------------------------------------------------------- /lib/raw.cppo.ml: -------------------------------------------------------------------------------- 1 | #define INTLIT 2 | #define FLOATLIT 3 | #define STRINGLIT 4 | 5 | #include "type.ml" 6 | 7 | #include "write.ml" 8 | 9 | module Pretty = struct 10 | #include "prettyprint.ml" 11 | end 12 | 13 | #include "monomorphic.ml" 14 | 15 | #include "write2.ml" 16 | 17 | #include "read.ml" 18 | 19 | module Util = struct 20 | #include "util.ml" 21 | end 22 | 23 | #undef INTLIT 24 | #undef FLOATLIT 25 | #undef STRINGLIT 26 | -------------------------------------------------------------------------------- /lib/raw.cppo.mli: -------------------------------------------------------------------------------- 1 | (** 2 | Ints, floats and strings literals are systematically preserved using 3 | [`Intlit], [`Floatlit] and [`Stringlit]. 4 | *) 5 | 6 | #define INTLIT 7 | #define FLOATLIT 8 | #define STRINGLIT 9 | 10 | #include "type.ml" 11 | 12 | #include "monomorphic.mli" 13 | 14 | #include "write.mli" 15 | 16 | #include "write2.mli" 17 | 18 | #include "read.mli" 19 | 20 | (** This module provides combinators for extracting fields from JSON values. *) 21 | module Util : sig 22 | #include "util.mli" 23 | end 24 | 25 | #undef INTLIT 26 | #undef FLOATLIT 27 | #undef STRINGLIT 28 | -------------------------------------------------------------------------------- /lib/read.mli: -------------------------------------------------------------------------------- 1 | val prettify : ?std:bool -> string -> string 2 | (** Combined parser and pretty-printer. 3 | See [to_string] for the role of the optional [std] argument and raised exceptions. *) 4 | 5 | val compact : ?std:bool -> string -> string 6 | (** Combined parser and printer. 7 | See [to_string] for the role of the optional [std] argument and raised exceptions. *) 8 | 9 | (** {2 JSON readers} *) 10 | 11 | exception Finally of exn * exn 12 | (** Exception describing a failure in both finalizer and parsing. *) 13 | 14 | val from_string : ?buf:Buffer.t -> ?fname:string -> ?lnum:int -> string -> t 15 | (** Read a JSON value from a string. 16 | @param buf use this buffer at will during parsing instead of creating 17 | a new one. 18 | @param fname data file name to be used in error messages. It does 19 | not have to be a real file. 20 | @param lnum number of the first line of input. Default is 1. 21 | @raise Json_error if parsing fails. 22 | *) 23 | 24 | val from_channel : 25 | ?buf:Buffer.t -> ?fname:string -> ?lnum:int -> in_channel -> t 26 | (** Read a JSON value from a channel. 27 | See [from_string] for the meaning of the optional arguments and raised exceptions. *) 28 | 29 | val from_file : ?buf:Buffer.t -> ?fname:string -> ?lnum:int -> string -> t 30 | (** Read a JSON value from a file. 31 | See [from_string] for the meaning of the optional arguments and raised exceptions. *) 32 | 33 | type lexer_state = Common.Lexer_state.t = { 34 | buf : Buffer.t; 35 | mutable lnum : int; 36 | mutable bol : int; 37 | mutable fname : string option; 38 | } 39 | (** This alias is provided for backward compatibility. 40 | New code should refer to {!Yojson.lexer_state} directly. 41 | *) 42 | 43 | val init_lexer : 44 | ?buf:Buffer.t -> ?fname:string -> ?lnum:int -> unit -> lexer_state 45 | (** This alias is provided for backward compatibility. 46 | New code should use {!Yojson.init_lexer} directly. *) 47 | 48 | val from_lexbuf : lexer_state -> ?stream:bool -> Lexing.lexbuf -> t 49 | (** Read a JSON value from a lexbuf. 50 | A valid initial [lexer_state] can be created with [init_lexer]. 51 | See [from_string] for the meaning of the optional arguments and raised exceptions. 52 | 53 | @param stream indicates whether more data may follow. The default value 54 | is false and indicates that only JSON whitespace can be found between 55 | the end of the JSON value and the end of the input. *) 56 | 57 | val seq_from_string : 58 | ?buf:Buffer.t -> ?fname:string -> ?lnum:int -> string -> t Seq.t 59 | (** Input a sequence of JSON values from a string. 60 | Whitespace between JSON values is fine but not required. 61 | See [from_string] for the meaning of the optional arguments and raised exceptions. *) 62 | 63 | val seq_from_channel : 64 | ?buf:Buffer.t -> 65 | ?fin:(unit -> unit) -> 66 | ?fname:string -> 67 | ?lnum:int -> 68 | in_channel -> 69 | t Seq.t 70 | (** Input a sequence of JSON values from a channel. 71 | Whitespace between JSON values is fine but not required. 72 | @param fin finalization function executed once when the end of the 73 | sequence is reached either because there is no more input or because 74 | the input could not be parsed, raising an exception. 75 | @raise Finally When the parsing and the finalizer both raised, [Finally (exn, fin_exn)] 76 | is raised, [exn] being the parsing exception and [fin_exn] the finalizer one. 77 | 78 | See [from_string] for the meaning of the other optional arguments and other raised exceptions. *) 79 | 80 | val seq_from_file : 81 | ?buf:Buffer.t -> ?fname:string -> ?lnum:int -> string -> t Seq.t 82 | (** Input a sequence of JSON values from a file. 83 | Whitespace between JSON values is fine but not required. 84 | 85 | See [from_string] for the meaning of the optional arguments and raised exceptions. *) 86 | 87 | val seq_from_lexbuf : 88 | lexer_state -> ?fin:(unit -> unit) -> Lexing.lexbuf -> t Seq.t 89 | (** Input a sequence of JSON values from a lexbuf. 90 | A valid initial [lexer_state] can be created with [init_lexer]. 91 | Whitespace between JSON values is fine but not required. 92 | @raise Finally When the parsing and the finalizer both raised, [Finally (exn, fin_exn)] 93 | is raised, [exn] being the parsing exception and [fin_exn] the finalizer one. 94 | 95 | See [seq_from_channel] for the meaning of the optional [fin] 96 | argument and other raised exceptions. *) 97 | 98 | type json_line = [ `Json of t | `Exn of exn ] 99 | (** The type of values resulting from a parsing attempt of a JSON value. *) 100 | 101 | val lineseq_from_channel : 102 | ?buf:Buffer.t -> 103 | ?fin:(unit -> unit) -> 104 | ?fname:string -> 105 | ?lnum:int -> 106 | in_channel -> 107 | json_line Seq.t 108 | (** Input a sequence of JSON values, one per line, from a channel. 109 | Exceptions raised when reading malformed lines are caught 110 | and represented using [`Exn]. 111 | 112 | See [seq_from_channel] for the meaning of the optional [fin] 113 | argument. 114 | See [from_string] for the meaning of the other optional arguments and raised exceptions. *) 115 | 116 | val lineseq_from_file : 117 | ?buf:Buffer.t -> ?fname:string -> ?lnum:int -> string -> json_line Seq.t 118 | (** Input a sequence of JSON values, one per line, from a file. 119 | Exceptions raised when reading malformed lines are caught 120 | and represented using [`Exn]. 121 | 122 | See [seq_from_channel] for the meaning of the optional [fin] 123 | argument. 124 | See [from_string] for the meaning of the other optional arguments and raised exceptions. *) 125 | 126 | val read_t : lexer_state -> Lexing.lexbuf -> t 127 | (** Read a JSON value from the given lexer_state and lexing buffer and return it. 128 | Provided as a reader function for atdgen. 129 | *) 130 | 131 | (**/**) 132 | (* begin undocumented section *) 133 | 134 | val finish_string : lexer_state -> Lexing.lexbuf -> string 135 | val read_string : lexer_state -> Lexing.lexbuf -> string 136 | val read_ident : lexer_state -> Lexing.lexbuf -> string 137 | 138 | val map_ident : 139 | lexer_state -> (string -> int -> int -> 'a) -> Lexing.lexbuf -> 'a 140 | (* equivalent to read_ident *) 141 | 142 | val read_lt : lexer_state -> Lexing.lexbuf -> unit 143 | val read_gt : lexer_state -> Lexing.lexbuf -> unit 144 | val read_comma : lexer_state -> Lexing.lexbuf -> unit 145 | val finish_stringlit : lexer_state -> Lexing.lexbuf -> string 146 | val finish_skip_stringlit : lexer_state -> Lexing.lexbuf -> unit 147 | val finish_escaped_char : lexer_state -> Lexing.lexbuf -> unit 148 | val finish_comment : lexer_state -> Lexing.lexbuf -> unit 149 | val read_space : lexer_state -> Lexing.lexbuf -> unit 150 | val read_eof : Lexing.lexbuf -> bool 151 | val read_null : lexer_state -> Lexing.lexbuf -> unit 152 | val read_null_if_possible : lexer_state -> Lexing.lexbuf -> bool 153 | val read_bool : lexer_state -> Lexing.lexbuf -> bool 154 | val read_int : lexer_state -> Lexing.lexbuf -> int 155 | val read_int8 : lexer_state -> Lexing.lexbuf -> char 156 | val read_int32 : lexer_state -> Lexing.lexbuf -> int32 157 | val read_int64 : lexer_state -> Lexing.lexbuf -> int64 158 | val read_number : lexer_state -> Lexing.lexbuf -> float 159 | val skip_ident : lexer_state -> Lexing.lexbuf -> unit 160 | 161 | val read_sequence : 162 | ('a -> lexer_state -> Lexing.lexbuf -> 'a) -> 163 | 'a -> 164 | lexer_state -> 165 | Lexing.lexbuf -> 166 | 'a 167 | 168 | val read_list : 169 | (lexer_state -> Lexing.lexbuf -> 'a) -> 170 | lexer_state -> 171 | Lexing.lexbuf -> 172 | 'a list 173 | 174 | val read_list_rev : 175 | (lexer_state -> Lexing.lexbuf -> 'a) -> 176 | lexer_state -> 177 | Lexing.lexbuf -> 178 | 'a list 179 | 180 | val read_array_end : Lexing.lexbuf -> unit 181 | val read_array_sep : lexer_state -> Lexing.lexbuf -> unit 182 | 183 | val read_array : 184 | (lexer_state -> Lexing.lexbuf -> 'a) -> 185 | lexer_state -> 186 | Lexing.lexbuf -> 187 | 'a array 188 | 189 | val read_lpar : lexer_state -> Lexing.lexbuf -> unit 190 | val read_rpar : lexer_state -> Lexing.lexbuf -> unit 191 | val read_lbr : lexer_state -> Lexing.lexbuf -> unit 192 | val read_rbr : lexer_state -> Lexing.lexbuf -> unit 193 | 194 | val read_fields : 195 | ('acc -> string -> lexer_state -> Lexing.lexbuf -> 'acc) -> 196 | 'acc -> 197 | lexer_state -> 198 | Lexing.lexbuf -> 199 | 'acc 200 | 201 | val read_abstract_fields : 202 | (lexer_state -> Lexing.lexbuf -> 'key) -> 203 | ('acc -> 'key -> lexer_state -> Lexing.lexbuf -> 'acc) -> 204 | 'acc -> 205 | lexer_state -> 206 | Lexing.lexbuf -> 207 | 'acc 208 | 209 | val read_lcurl : lexer_state -> Lexing.lexbuf -> unit 210 | val read_object_end : Lexing.lexbuf -> unit 211 | val read_object_sep : lexer_state -> Lexing.lexbuf -> unit 212 | val read_colon : lexer_state -> Lexing.lexbuf -> unit 213 | val read_json : lexer_state -> Lexing.lexbuf -> t 214 | val skip_json : lexer_state -> Lexing.lexbuf -> unit 215 | val buffer_json : lexer_state -> Lexing.lexbuf -> unit 216 | 217 | (* end undocumented section *) 218 | (**/**) 219 | -------------------------------------------------------------------------------- /lib/safe.cppo.ml: -------------------------------------------------------------------------------- 1 | #define INT 2 | #define INTLIT 3 | #define FLOAT 4 | #define STRING 5 | 6 | #include "type.ml" 7 | 8 | #include "safe_to_basic.ml" 9 | 10 | #include "write.ml" 11 | 12 | module Pretty = struct 13 | #include "prettyprint.ml" 14 | end 15 | 16 | #include "monomorphic.ml" 17 | 18 | #include "write2.ml" 19 | 20 | #include "read.ml" 21 | 22 | module Util = struct 23 | #include "util.ml" 24 | end 25 | 26 | #undef INT 27 | #undef INTLIT 28 | #undef FLOAT 29 | #undef STRING 30 | -------------------------------------------------------------------------------- /lib/safe.cppo.mli: -------------------------------------------------------------------------------- 1 | (** 2 | This module supports a slight superset of the standard JSON nodes. 3 | Arbitrary integers are supported and represented as a decimal string 4 | using [`Intlit] when they cannot be represented using OCaml's int type 5 | (31 or 63 bits depending on the platform). 6 | 7 | This module is recommended for intensive use or OCaml-friendly use of 8 | JSON. 9 | *) 10 | 11 | #define INT 12 | #define INTLIT 13 | #define FLOAT 14 | #define STRING 15 | 16 | #include "type.ml" 17 | 18 | #include "monomorphic.mli" 19 | 20 | #include "safe_to_basic.mli" 21 | 22 | #include "write.mli" 23 | 24 | #include "write2.mli" 25 | 26 | #include "read.mli" 27 | 28 | (** This module provides combinators for extracting fields from JSON values. *) 29 | module Util : sig 30 | #include "util.mli" 31 | end 32 | 33 | #undef INT 34 | #undef INTLIT 35 | #undef FLOAT 36 | #undef STRING 37 | -------------------------------------------------------------------------------- /lib/safe_to_basic.ml: -------------------------------------------------------------------------------- 1 | let rec to_basic : t -> Basic.t = function 2 | | (`Null | `Bool _ | `Int _ | `Float _ | `String _) as x -> x 3 | | `Intlit s -> `String s 4 | | `List l -> `List (List.rev (List.rev_map to_basic l)) 5 | | `Assoc l -> 6 | `Assoc (List.rev (List.rev_map (fun (k, v) -> (k, to_basic v)) l)) 7 | -------------------------------------------------------------------------------- /lib/safe_to_basic.mli: -------------------------------------------------------------------------------- 1 | val to_basic : t -> Basic.t 2 | (** 3 | Long integers are converted to JSON strings. 4 | 5 | Examples: 6 | {v 7 | `Intlit "12345678901234567890" -> `String "12345678901234567890" 8 | v} 9 | *) 10 | -------------------------------------------------------------------------------- /lib/t.cppo.ml: -------------------------------------------------------------------------------- 1 | #define INT 2 | #define INTLIT 3 | #define FLOAT 4 | #define FLOATLIT 5 | #define STRING 6 | #define STRINGLIT 7 | 8 | #include "type.ml" 9 | 10 | #include "write.ml" 11 | 12 | #include "monomorphic.ml" 13 | 14 | module Pretty = struct 15 | #include "prettyprint.ml" 16 | end 17 | 18 | #include "write2.ml" 19 | 20 | #undef INT 21 | #undef INTLIT 22 | #undef FLOAT 23 | #undef FLOATLIT 24 | #undef STRING 25 | #undef STRINGLIT 26 | -------------------------------------------------------------------------------- /lib/t.cppo.mli: -------------------------------------------------------------------------------- 1 | #define INT 2 | #define INTLIT 3 | #define FLOAT 4 | #define FLOATLIT 5 | #define STRING 6 | #define STRINGLIT 7 | 8 | #include "type.ml" 9 | 10 | #include "monomorphic.mli" 11 | 12 | #include "write.mli" 13 | 14 | #include "write2.mli" 15 | 16 | #undef INT 17 | #undef INTLIT 18 | #undef FLOAT 19 | #undef FLOATLIT 20 | #undef STRING 21 | #undef STRINGLIT 22 | -------------------------------------------------------------------------------- /lib/type.ml: -------------------------------------------------------------------------------- 1 | (** {3 Type of the JSON tree} *) 2 | 3 | type t = 4 | [ 5 | | `Null 6 | | `Bool of bool 7 | #ifdef INT 8 | | `Int of int 9 | #endif 10 | #ifdef INTLIT 11 | | `Intlit of string 12 | #endif 13 | #ifdef FLOAT 14 | | `Float of float 15 | #endif 16 | #ifdef FLOATLIT 17 | | `Floatlit of string 18 | #endif 19 | #ifdef STRING 20 | | `String of string 21 | #endif 22 | #ifdef STRINGLIT 23 | | `Stringlit of string 24 | #endif 25 | | `Assoc of (string * t) list 26 | | `List of t list 27 | ] 28 | (** 29 | All possible cases defined in Yojson: 30 | - `Null: JSON null 31 | - `Bool of bool: JSON boolean 32 | - `Int of int: JSON number without decimal point or exponent. 33 | - `Intlit of string: JSON number without decimal point or exponent, 34 | preserved as a string. 35 | - `Float of float: JSON number, Infinity, -Infinity or NaN. 36 | - `Floatlit of string: JSON number, Infinity, -Infinity or NaN, 37 | preserved as a string. 38 | - `String of string: JSON string. Bytes in the range 128-255 are preserved 39 | as-is without encoding validation for both reading 40 | and writing. 41 | - `Stringlit of string: JSON string literal including the double quotes. 42 | - `Assoc of (string * json) list: JSON object. 43 | - `List of json list: JSON array. 44 | *) 45 | 46 | (* 47 | Note to adventurers: ocamldoc does not support inline comments 48 | on each polymorphic variant, and cppo doesn't allow to concatenate 49 | comments, so it would be complicated to document only the 50 | cases that are preserved by cppo in the type definition. 51 | *) 52 | -------------------------------------------------------------------------------- /lib/util.ml: -------------------------------------------------------------------------------- 1 | exception Type_error of string * t 2 | 3 | let typeof = function 4 | | `Assoc _ -> "object" 5 | | `Bool _ -> "bool" 6 | | `Float _ -> "float" 7 | #ifdef INT 8 | | `Int _ -> "int" 9 | #endif 10 | | `List _ -> "array" 11 | | `Null -> "null" 12 | | `String _ -> "string" 13 | | `Intlit _ -> "intlit" 14 | | `Floatlit _ -> "floatlit" 15 | #ifdef STRINGLIT 16 | | `Stringlit _ -> "stringlit" 17 | #endif 18 | 19 | let typerr msg js = raise (Type_error (msg ^ typeof js, js)) 20 | 21 | exception Undefined of string * t 22 | 23 | let assoc name obj = try List.assoc name obj with Not_found -> `Null 24 | 25 | let member name = function 26 | | `Assoc obj -> assoc name obj 27 | | js -> typerr ("Can't get member '" ^ name ^ "' of non-object type ") js 28 | 29 | let rec path l obj = 30 | match l with 31 | | [] -> Some obj 32 | | key :: l -> ( 33 | match obj with 34 | | `Assoc assoc -> ( 35 | match List.assoc key assoc with 36 | | obj -> path l obj 37 | | exception Not_found -> None) 38 | | _ -> None) 39 | 40 | let index i = function 41 | | `List l as js -> 42 | let len = List.length l in 43 | let wrapped_index = if i < 0 then len + i else i in 44 | if wrapped_index < 0 || wrapped_index >= len then 45 | raise (Undefined ("Index " ^ string_of_int i ^ " out of bounds", js)) 46 | else List.nth l wrapped_index 47 | | js -> 48 | typerr ("Can't get index " ^ string_of_int i ^ " of non-array type ") js 49 | 50 | let map f = function 51 | | `List l -> `List (List.map f l) 52 | | js -> typerr "Can't map function over non-array type " js 53 | 54 | let to_assoc = function 55 | | `Assoc obj -> obj 56 | | js -> typerr "Expected object, got " js 57 | 58 | let to_option f = function `Null -> None | x -> Some (f x) 59 | let to_bool = function `Bool b -> b | js -> typerr "Expected bool, got " js 60 | 61 | let to_bool_option = function 62 | | `Bool b -> Some b 63 | | `Null -> None 64 | | js -> typerr "Expected bool or null, got " js 65 | 66 | let to_number = function 67 | #ifdef INT 68 | | `Int i -> float i 69 | #endif 70 | #ifdef FLOAT 71 | | `Float f -> f 72 | #endif 73 | | js -> typerr "Expected number, got " js 74 | 75 | let to_number_option = function 76 | #ifdef INT 77 | | `Int i -> Some (float i) 78 | #endif 79 | #ifdef FLOAT 80 | | `Float f -> Some f 81 | #endif 82 | | `Null -> None 83 | | js -> typerr "Expected number or null, got " js 84 | 85 | let to_float = function 86 | #ifdef FLOAT 87 | | `Float f -> f 88 | #endif 89 | | js -> typerr "Expected float, got " js 90 | 91 | let to_float_option = function 92 | #ifdef FLOAT 93 | | `Float f -> Some f 94 | #endif 95 | | `Null -> None 96 | | js -> typerr "Expected float or null, got " js 97 | 98 | let to_int = function 99 | #ifdef INT 100 | | `Int i -> i 101 | #endif 102 | | js -> typerr "Expected int, got " js 103 | 104 | let to_int_option = function 105 | #ifdef INT 106 | | `Int i -> Some i 107 | #endif 108 | | `Null -> None 109 | | js -> typerr "Expected int or null, got " js 110 | 111 | let to_list = function `List l -> l | js -> typerr "Expected array, got " js 112 | 113 | let to_string = function 114 | #ifdef STRING 115 | | `String s -> s 116 | #endif 117 | | js -> typerr "Expected string, got " js 118 | 119 | let to_string_option = function 120 | #ifdef STRING 121 | | `String s -> Some s 122 | #endif 123 | | `Null -> None 124 | | js -> typerr "Expected string or null, got " js 125 | 126 | let convert_each f = function 127 | | `List l -> List.map f l 128 | | js -> typerr "Can't convert each element of non-array type " js 129 | 130 | let rec rev_filter_map f acc l = 131 | match l with 132 | | [] -> acc 133 | | x :: tl -> ( 134 | match f x with 135 | | None -> rev_filter_map f acc tl 136 | | Some y -> rev_filter_map f (y :: acc) tl) 137 | 138 | let filter_map f l = List.rev (rev_filter_map f [] l) 139 | 140 | let rec rev_flatten acc l = 141 | match l with 142 | | [] -> acc 143 | | x :: tl -> ( 144 | match x with 145 | | `List l2 -> rev_flatten (List.rev_append l2 acc) tl 146 | | _ -> rev_flatten acc tl) 147 | 148 | let flatten l = List.rev (rev_flatten [] l) 149 | 150 | let filter_index i l = 151 | filter_map 152 | (function 153 | | `List l -> ( try Some (List.nth l i) with _ -> None) | _ -> None) 154 | l 155 | 156 | let filter_list l = filter_map (function `List l -> Some l | _ -> None) l 157 | 158 | let filter_member k l = 159 | filter_map 160 | (function 161 | | `Assoc l -> ( try Some (List.assoc k l) with _ -> None) | _ -> None) 162 | l 163 | 164 | let filter_assoc l = filter_map (function `Assoc l -> Some l | _ -> None) l 165 | let filter_bool l = filter_map (function `Bool x -> Some x | _ -> None) l 166 | let filter_int l = 167 | filter_map ( 168 | function 169 | #ifdef INT 170 | | `Int x -> Some x 171 | #endif 172 | | _ -> None 173 | ) l 174 | 175 | let filter_float l = 176 | filter_map ( 177 | function 178 | #ifdef FLOAT 179 | `Float x -> Some x 180 | #endif 181 | | _ -> None 182 | ) l 183 | 184 | let filter_number l = 185 | filter_map ( 186 | function 187 | #ifdef INT 188 | `Int x -> Some (float x) 189 | #endif 190 | #ifdef FLOAT 191 | | `Float x -> Some x 192 | #endif 193 | | _ -> None 194 | ) l 195 | 196 | let filter_string l = 197 | filter_map ( 198 | function 199 | #ifdef STRING 200 | `String x -> Some x 201 | #endif 202 | | _ -> None 203 | ) l 204 | 205 | let keys o = 206 | to_assoc o |> List.map (fun (key, _) -> key) 207 | 208 | let values o = 209 | to_assoc o |> List.map (fun (_, value) -> value) 210 | 211 | let combine (first : t) (second : t) = 212 | match (first, second) with 213 | | `Assoc a, `Assoc b -> (`Assoc (a @ b) : t) 214 | | a, b -> raise (Invalid_argument "Expected two objects, check inputs") 215 | -------------------------------------------------------------------------------- /lib/util.mli: -------------------------------------------------------------------------------- 1 | (** 2 | This module provides combinators for extracting fields from JSON 3 | values. This approach is recommended for reading a few fields 4 | from data returned by public APIs. However for more complex applications 5 | we recommend {{:https://github.com/ahrefs/atd}Atdgen}. 6 | 7 | Here is some sample JSON data: 8 | {v 9 | { 10 | "id": "398eb027", 11 | "name": "John Doe", 12 | "pages": [ 13 | { 14 | "id": 1, 15 | "title": "The Art of Flipping Coins", 16 | "url": "http://example.com/398eb027/1" 17 | }, 18 | { 19 | "id": 2, 20 | "deleted": true 21 | }, 22 | { 23 | "id": 3, 24 | "title": "Artichoke Salad", 25 | "url": "http://example.com/398eb027/3" 26 | }, 27 | { 28 | "id": 4, 29 | "title": "Flying Bananas", 30 | "url": "http://example.com/398eb027/4" 31 | } 32 | ] 33 | } 34 | v} 35 | 36 | In order to extract the "id" field, assuming it is mandatory, 37 | we would use the following OCaml code that operates on single JSON 38 | nodes: 39 | {v 40 | open Yojson.Basic.Util 41 | ... 42 | let id = json |> member "id" |> to_string in 43 | ... 44 | v} 45 | 46 | In order to extract all the "title" fields, we would write the following 47 | OCaml code that operates on lists of JSON nodes, skipping 48 | undefined nodes and nodes of unexpected type: 49 | {v 50 | open Yojson.Basic.Util 51 | 52 | let extract_titles (json : Yojson.Basic.t) : string list = 53 | [json] 54 | |> filter_member "pages" 55 | |> flatten 56 | |> filter_member "title" 57 | |> filter_string 58 | v} 59 | *) 60 | 61 | exception Type_error of string * t 62 | (** Raised when the JSON value is not of the correct type to support an 63 | operation, e.g. [member] on an [`Int]. The string message explains the 64 | mismatch. *) 65 | 66 | exception Undefined of string * t 67 | (** Raised when the equivalent JavaScript operation on the JSON value would 68 | return undefined. Currently this only happens when an array index is out 69 | of bounds. *) 70 | 71 | val keys : t -> string list 72 | (** Returns all the key names in the given JSON object. 73 | @raise Type_error if argument is not a JSON object. *) 74 | 75 | val values : t -> t list 76 | (** Return all the value in the given JSON object. 77 | @raise Type_error if argument is not a JSON object. *) 78 | 79 | val combine : t -> t -> t 80 | (** Combine two JSON objects together. 81 | @raise Invalid_argument if either argument is not a JSON object. *) 82 | 83 | val member : string -> t -> t 84 | (** [member k obj] returns the value associated with the key [k] in the JSON 85 | object [obj], or [`Null] if [k] is not present in [obj]. 86 | @raise Type_error if [obj] is not a JSON object. *) 87 | 88 | val path : string list -> t -> t option 89 | (* [path l obj] recurses the JSON object [obj] for each key in the path 90 | [l] until the path is empty or there is no such key in the chain. *) 91 | 92 | val index : int -> t -> t 93 | (** [index i arr] returns the value at index [i] in the JSON array [arr]. 94 | Negative indices count from the end of the list (so -1 is the last 95 | element). 96 | @raise Type_error if [arr] is not a JSON array. 97 | @raise Undefined if index is out of bounds. *) 98 | 99 | val map : (t -> t) -> t -> t 100 | (** [map f arr] calls the function [f] on each element of the JSON array 101 | [arr], and returns a JSON array containing the results. 102 | @raise Type_error if [arr] is not an JSON array. *) 103 | 104 | val to_assoc : t -> (string * t) list 105 | (** Extract the items of a JSON object. 106 | @raise Type_error if argument is not a JSON object. *) 107 | 108 | val to_option : (t -> 'a) -> t -> 'a option 109 | (** Return [None] if the JSON value is null or map the JSON value 110 | to [Some] value using the provided function. *) 111 | 112 | val to_bool : t -> bool 113 | (** Extract a boolean value. 114 | @raise Type_error if argument is not a JSON boolean. *) 115 | 116 | val to_bool_option : t -> bool option 117 | (** Extract [Some] boolean value, 118 | return [None] if the value is null. 119 | @raise Type_error if argument is neither. *) 120 | 121 | val to_number : t -> float 122 | (** Extract a number. 123 | @raise Type_error if argument is not a JSON number. *) 124 | 125 | val to_number_option : t -> float option 126 | (** Extract [Some] number, 127 | return [None] if the value is null. 128 | @raise Type_error if argument is neither. *) 129 | 130 | val to_float : t -> float 131 | (** Extract a float value. 132 | [to_number] is generally preferred as it also works with int literals. 133 | @raise Type_error if argument is not a JSON float. *) 134 | 135 | val to_float_option : t -> float option 136 | (** Extract [Some] float value, 137 | return [None] if the value is null. 138 | [to_number_option] is generally preferred as it also works 139 | with int literals. 140 | @raise Type_error if argument is neither. *) 141 | 142 | val to_int : t -> int 143 | (** Extract an int from a JSON int. 144 | @raise Type_error if argument is not a JSON int. *) 145 | 146 | val to_int_option : t -> int option 147 | (** Extract [Some] int from a JSON int, 148 | return [None] if the value is null. 149 | @raise Type_error if argument is neither. *) 150 | 151 | val to_list : t -> t list 152 | (** Extract a list from JSON array. 153 | @raise Type_error if argument is not a JSON array. *) 154 | 155 | val to_string : t -> string 156 | (** Extract a string from a JSON string. 157 | @raise Type_error if argument is not a JSON string. *) 158 | 159 | val to_string_option : t -> string option 160 | (** Extract [Some] string from a JSON string, 161 | return [None] if the value is null. 162 | @raise Type_error if argument is neither. *) 163 | 164 | val convert_each : (t -> 'a) -> t -> 'a list 165 | (** The conversion functions above cannot be used with [map], because they do 166 | not return JSON values. This convenience function [convert_each to_f arr] 167 | is equivalent to [List.map to_f (to_list arr)]. 168 | @raise Type_error if [arr] is not a JSON array. *) 169 | 170 | (** {3 Exception-free filters} *) 171 | 172 | (** 173 | The following functions operate on lists of JSON nodes. 174 | None of them raises an exception when a certain kind of node is expected 175 | but no node or the wrong kind of node is found. 176 | Instead of raising an exception, nodes that are not as expected 177 | are simply ignored. 178 | *) 179 | 180 | val filter_map : ('a -> 'b option) -> 'a list -> 'b list 181 | (** [filter_map f l] maps each element of the list [l] to an optional value 182 | using function [f] and unwraps the resulting values. *) 183 | 184 | val flatten : t list -> t list 185 | (** Expects JSON arrays and returns all their elements as a single 186 | list. [flatten l] is equivalent to [List.flatten (filter_list l)]. *) 187 | 188 | val filter_index : int -> t list -> t list 189 | (** Expects JSON arrays and returns all their elements existing at the given 190 | position. *) 191 | 192 | val filter_list : t list -> t list list 193 | (** Expects JSON arrays and unwraps them. *) 194 | 195 | val filter_member : string -> t list -> t list 196 | (** Expects JSON objects and returns all the fields of the given name 197 | (at most one field per object). *) 198 | 199 | val filter_assoc : t list -> (string * t) list list 200 | (** Expects JSON objects and unwraps them. *) 201 | 202 | val filter_bool : t list -> bool list 203 | (** Expects JSON booleans and unwraps them. *) 204 | 205 | val filter_int : t list -> int list 206 | (** Expects JSON integers ([`Int] nodes) and unwraps them. *) 207 | 208 | val filter_float : t list -> float list 209 | (** Expects JSON floats ([`Float] nodes) and unwraps them. *) 210 | 211 | val filter_number : t list -> float list 212 | (** Expects JSON numbers ([`Int] or [`Float]) and unwraps them. 213 | Ints are converted to floats. *) 214 | 215 | val filter_string : t list -> string list 216 | (** Expects JSON strings and unwraps them. *) 217 | -------------------------------------------------------------------------------- /lib/write.mli: -------------------------------------------------------------------------------- 1 | (** {2 JSON writers} *) 2 | 3 | val to_string : 4 | ?buf:Buffer.t -> 5 | ?len:int -> 6 | ?suf:string -> 7 | ?std:bool -> 8 | t -> string 9 | (** Write a compact JSON value to a string. 10 | @param buf allows to reuse an existing buffer created with 11 | [Buffer.create]. The buffer is cleared of all contents 12 | before starting and right before returning. 13 | @param len initial length of the output buffer. 14 | @param suf appended to the output as a suffix, 15 | defaults to empty string. 16 | @param std use only standard JSON syntax, 17 | refuse to print NaN and infinities, 18 | require the root node to be either an object or an array. 19 | Default is [false]. 20 | @raise Json_error if [float] value is not allowed in standard JSON. 21 | *) 22 | 23 | val to_channel : 24 | ?buf:Buffer.t -> 25 | ?len:int -> 26 | ?suf:string -> 27 | ?std:bool -> 28 | out_channel -> t -> unit 29 | (** Write a compact JSON value to a channel. 30 | Note: the [out_channel] is not flushed by this function. 31 | 32 | See [to_string] for the role of the optional arguments and raised exceptions. *) 33 | 34 | val to_output : 35 | ?buf:Buffer.t -> 36 | ?len:int -> 37 | ?suf:string -> 38 | ?std:bool -> 39 | < output : string -> int -> int -> int; .. > -> t -> unit 40 | (** Write a compact JSON value to an OO channel. 41 | 42 | See [to_string] for the role of the optional arguments and raised exceptions. *) 43 | 44 | val to_file : 45 | ?len:int -> 46 | ?std:bool -> 47 | ?suf:string -> 48 | string -> t -> unit 49 | (** Write a compact JSON value to a file. 50 | See [to_string] for the role of the optional arguments and raised exceptions. 51 | @param suf is a suffix appended to the output Newline by default 52 | for POSIX compliance. *) 53 | 54 | val to_buffer : 55 | ?suf:string -> 56 | ?std:bool -> 57 | Buffer.t -> t -> unit 58 | (** Write a compact JSON value to an existing buffer. 59 | See [to_string] for the role of the optional argument and raised exceptions. *) 60 | 61 | val seq_to_string : 62 | ?buf:Buffer.t -> 63 | ?len:int -> 64 | ?suf:string -> 65 | ?std:bool -> 66 | t Seq.t -> string 67 | (** Write a sequence of [suf]-suffixed compact one-line JSON values to 68 | a string. 69 | @param suf is the suffix ouf each value written. Newline by default. 70 | See [to_string] for the role of the optional arguments and raised exceptions. *) 71 | 72 | val seq_to_channel : 73 | ?buf:Buffer.t -> 74 | ?len:int -> 75 | ?suf:string -> 76 | ?std:bool -> 77 | out_channel -> t Seq.t -> unit 78 | (** Write a sequence of [suf]-suffixed compact one-line JSON values to 79 | a channel. 80 | @param suf is the suffix of each value written. Newline by default. 81 | See [to_channel] for the role of the optional arguments and raised exceptions. *) 82 | 83 | val seq_to_file : 84 | ?len:int -> 85 | ?suf:string -> 86 | ?std:bool -> 87 | string -> t Seq.t -> unit 88 | (** Write a sequence of [suf]-suffixed compact one-line JSON values to 89 | a file. 90 | @param suf is the suffix of each value written. Newline by default. 91 | See [to_string] for the role of the optional arguments and raised exceptions. *) 92 | 93 | val seq_to_buffer : 94 | ?suf:string -> 95 | ?std:bool -> 96 | Buffer.t -> 97 | t Seq.t -> unit 98 | (** Write a sequence of [suf]-suffixed compact one-line JSON values to 99 | an existing buffer. 100 | @param suf is the suffix of each value written. Newline by default. 101 | See [to_string] for the role of the optional arguments and raised exceptions. *) 102 | 103 | val write_t : Buffer.t -> t -> unit 104 | (** Write the given JSON value to the given buffer. 105 | Provided as a writer function for atdgen. 106 | *) 107 | 108 | (** {2 Miscellaneous} *) 109 | 110 | val sort : t -> t 111 | (** Sort object fields (stable sort, comparing field names 112 | and treating them as byte sequences) *) 113 | 114 | 115 | (**/**) 116 | (* begin undocumented section *) 117 | 118 | val write_null : Buffer.t -> unit -> unit 119 | val write_bool : Buffer.t -> bool -> unit 120 | #ifdef INT 121 | val write_int : Buffer.t -> int -> unit 122 | #endif 123 | #ifdef FLOAT 124 | val write_float : Buffer.t -> float -> unit 125 | val write_std_float : Buffer.t -> float -> unit 126 | val write_float_prec : int -> Buffer.t -> float -> unit 127 | val write_std_float_prec : int -> Buffer.t -> float -> unit 128 | #endif 129 | #ifdef STRING 130 | val write_string : Buffer.t -> string -> unit 131 | #endif 132 | 133 | #ifdef INTLIT 134 | val write_intlit : Buffer.t -> string -> unit 135 | #endif 136 | #ifdef FLOATLIT 137 | val write_floatlit : Buffer.t -> string -> unit 138 | #endif 139 | #ifdef STRINGLIT 140 | val write_stringlit : Buffer.t -> string -> unit 141 | #endif 142 | 143 | val write_assoc : Buffer.t -> (string * t) list -> unit 144 | val write_list : Buffer.t -> t list -> unit 145 | 146 | val write_json : Buffer.t -> t -> unit 147 | val write_std_json : Buffer.t -> t -> unit 148 | 149 | (* end undocumented section *) 150 | (**/**) 151 | -------------------------------------------------------------------------------- /lib/write2.ml: -------------------------------------------------------------------------------- 1 | let pretty_print ?std out x = Pretty.pp ?std out x 2 | let pretty_to_string ?std x = Pretty.to_string ?std x 3 | let pretty_to_channel ?std oc x = Pretty.to_channel ?std oc x 4 | -------------------------------------------------------------------------------- /lib/write2.mli: -------------------------------------------------------------------------------- 1 | (** {2 JSON pretty-printing} *) 2 | 3 | val pretty_print : ?std:bool -> Format.formatter -> t -> unit 4 | (** Pretty-print into a {!Format.formatter}. 5 | See [to_string] for the role of the optional [std] argument. 6 | @raise Json_error if [float] value is not allowed in standard JSON. 7 | 8 | @since 1.3.1 *) 9 | 10 | val pretty_to_string : ?std:bool -> t -> string 11 | (** Pretty-print into a string. 12 | See [to_string] for the role of the optional [std] argument. 13 | See [pretty_print] for raised exceptions. 14 | *) 15 | 16 | val pretty_to_channel : ?std:bool -> out_channel -> t -> unit 17 | (** Pretty-print to a channel. 18 | See [to_string] for the role of the optional [std] argument. 19 | See [pretty_print] for raised exceptions. 20 | *) 21 | -------------------------------------------------------------------------------- /lib/yojson.ml: -------------------------------------------------------------------------------- 1 | include Common 2 | include T 3 | module Basic = Basic 4 | module Safe = Safe 5 | module Raw = Raw 6 | -------------------------------------------------------------------------------- /lib/yojson.mli: -------------------------------------------------------------------------------- 1 | (** 2 | The Yojson library provides several types for representing JSON values, with different use cases. 3 | 4 | - The {{!basic}Basic} JSON type, 5 | - The {{!safe}Safe} JSON type, a superset of JSON with safer support for integers, 6 | - The {{!raw}Raw} JSON type, a superset of JSON, safer but less integrated with OCaml types. 7 | 8 | Each of these different types have their own module. 9 | 10 | *) 11 | 12 | (** {1 Shared types and functions} *) 13 | 14 | include module type of Common 15 | include module type of T 16 | 17 | (** {1:basic Basic JSON tree type} *) 18 | 19 | module Basic = Basic 20 | 21 | (** {1:safe Multipurpose JSON tree type} *) 22 | 23 | module Safe = Safe 24 | 25 | (** {1 JSON tree type with literal int/float/string leaves} *) 26 | 27 | module Raw = Raw 28 | 29 | (** {1:raw Supertype of all JSON tree types} *) 30 | -------------------------------------------------------------------------------- /test/compliance/dune: -------------------------------------------------------------------------------- 1 | (executable 2 | (name test) 3 | (libraries alcotest yojson)) 4 | 5 | (rule 6 | (alias compliance-tests) 7 | (deps 8 | (:test test.exe) 9 | (glob_files test_cases/*)) 10 | (action 11 | (run %{test} --show-errors))) 12 | -------------------------------------------------------------------------------- /test/compliance/test.ml: -------------------------------------------------------------------------------- 1 | let test_cases_dir = "test_cases" 2 | 3 | let test ~accepted ~rejected ~dir_name file_name = 4 | try 5 | let json = Yojson.Basic.from_file @@ Filename.concat dir_name file_name in 6 | let pped = Yojson.Basic.show json in 7 | accepted file_name pped 8 | with Yojson.Json_error msg -> rejected file_name msg 9 | 10 | let pass _ _ = () 11 | let fail fmt filename v = Alcotest.failf fmt filename v 12 | 13 | let test_parses = 14 | test ~accepted:pass 15 | ~rejected:(fail "%s is valid JSON but failed with Json_error %s") 16 | 17 | let test_rejects = 18 | test ~accepted:(fail "%s is invalid JSON but parsed to %s") ~rejected:pass 19 | 20 | let test_any = test ~accepted:pass ~rejected:pass 21 | 22 | let test_file ~dir_name file_name = 23 | match file_name.[0] with 24 | | 'y' -> test_parses ~dir_name file_name 25 | | 'n' -> test_rejects ~dir_name file_name 26 | | 'i' -> test_any ~dir_name file_name 27 | | _ -> assert false 28 | 29 | let basic = 30 | let test_files = Array.to_list @@ Sys.readdir test_cases_dir in 31 | let sorted = List.sort String.compare test_files in 32 | List.rev_map 33 | (fun base_name -> 34 | (base_name, `Quick, fun () -> test_file ~dir_name:test_cases_dir base_name)) 35 | sorted 36 | 37 | let () = Alcotest.run "RFC 8259 Compliance" [ ("Yojson.Basic", basic) ] 38 | -------------------------------------------------------------------------------- /test/compliance/test_cases/i_number_double_huge_neg_exp.json: -------------------------------------------------------------------------------- 1 | [123.456e-789] -------------------------------------------------------------------------------- /test/compliance/test_cases/i_number_huge_exp.json: -------------------------------------------------------------------------------- 1 | [0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006] -------------------------------------------------------------------------------- /test/compliance/test_cases/i_number_neg_int_huge_exp.json: -------------------------------------------------------------------------------- 1 | [-1e+9999] -------------------------------------------------------------------------------- /test/compliance/test_cases/i_number_pos_double_huge_exp.json: -------------------------------------------------------------------------------- 1 | [1.5e+9999] -------------------------------------------------------------------------------- /test/compliance/test_cases/i_number_real_neg_overflow.json: -------------------------------------------------------------------------------- 1 | [-123123e100000] -------------------------------------------------------------------------------- /test/compliance/test_cases/i_number_real_pos_overflow.json: -------------------------------------------------------------------------------- 1 | [123123e100000] -------------------------------------------------------------------------------- /test/compliance/test_cases/i_number_real_underflow.json: -------------------------------------------------------------------------------- 1 | [123e-10000000] -------------------------------------------------------------------------------- /test/compliance/test_cases/i_number_too_big_neg_int.json: -------------------------------------------------------------------------------- 1 | [-123123123123123123123123123123] -------------------------------------------------------------------------------- /test/compliance/test_cases/i_number_too_big_pos_int.json: -------------------------------------------------------------------------------- 1 | [100000000000000000000] -------------------------------------------------------------------------------- /test/compliance/test_cases/i_number_very_big_negative_int.json: -------------------------------------------------------------------------------- 1 | [-237462374673276894279832749832423479823246327846] -------------------------------------------------------------------------------- /test/compliance/test_cases/i_object_key_lone_2nd_surrogate.json: -------------------------------------------------------------------------------- 1 | {"\uDFAA":0} -------------------------------------------------------------------------------- /test/compliance/test_cases/i_string_1st_surrogate_but_2nd_missing.json: -------------------------------------------------------------------------------- 1 | ["\uDADA"] -------------------------------------------------------------------------------- /test/compliance/test_cases/i_string_1st_valid_surrogate_2nd_invalid.json: -------------------------------------------------------------------------------- 1 | ["\uD888\u1234"] -------------------------------------------------------------------------------- /test/compliance/test_cases/i_string_UTF-16LE_with_BOM.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocaml-community/yojson/6a0395f8af9cf096c9c29546620931d6c2e5a9b5/test/compliance/test_cases/i_string_UTF-16LE_with_BOM.json -------------------------------------------------------------------------------- /test/compliance/test_cases/i_string_UTF-8_invalid_sequence.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocaml-community/yojson/6a0395f8af9cf096c9c29546620931d6c2e5a9b5/test/compliance/test_cases/i_string_UTF-8_invalid_sequence.json -------------------------------------------------------------------------------- /test/compliance/test_cases/i_string_UTF8_surrogate_U+D800.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocaml-community/yojson/6a0395f8af9cf096c9c29546620931d6c2e5a9b5/test/compliance/test_cases/i_string_UTF8_surrogate_U+D800.json -------------------------------------------------------------------------------- /test/compliance/test_cases/i_string_incomplete_surrogate_and_escape_valid.json: -------------------------------------------------------------------------------- 1 | ["\uD800\n"] -------------------------------------------------------------------------------- /test/compliance/test_cases/i_string_incomplete_surrogate_pair.json: -------------------------------------------------------------------------------- 1 | ["\uDd1ea"] -------------------------------------------------------------------------------- /test/compliance/test_cases/i_string_incomplete_surrogates_escape_valid.json: -------------------------------------------------------------------------------- 1 | ["\uD800\uD800\n"] -------------------------------------------------------------------------------- /test/compliance/test_cases/i_string_invalid_lonely_surrogate.json: -------------------------------------------------------------------------------- 1 | ["\ud800"] -------------------------------------------------------------------------------- /test/compliance/test_cases/i_string_invalid_surrogate.json: -------------------------------------------------------------------------------- 1 | ["\ud800abc"] -------------------------------------------------------------------------------- /test/compliance/test_cases/i_string_invalid_utf-8.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocaml-community/yojson/6a0395f8af9cf096c9c29546620931d6c2e5a9b5/test/compliance/test_cases/i_string_invalid_utf-8.json -------------------------------------------------------------------------------- /test/compliance/test_cases/i_string_inverted_surrogates_U+1D11E.json: -------------------------------------------------------------------------------- 1 | ["\uDd1e\uD834"] -------------------------------------------------------------------------------- /test/compliance/test_cases/i_string_iso_latin_1.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocaml-community/yojson/6a0395f8af9cf096c9c29546620931d6c2e5a9b5/test/compliance/test_cases/i_string_iso_latin_1.json -------------------------------------------------------------------------------- /test/compliance/test_cases/i_string_lone_second_surrogate.json: -------------------------------------------------------------------------------- 1 | ["\uDFAA"] -------------------------------------------------------------------------------- /test/compliance/test_cases/i_string_lone_utf8_continuation_byte.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocaml-community/yojson/6a0395f8af9cf096c9c29546620931d6c2e5a9b5/test/compliance/test_cases/i_string_lone_utf8_continuation_byte.json -------------------------------------------------------------------------------- /test/compliance/test_cases/i_string_not_in_unicode_range.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocaml-community/yojson/6a0395f8af9cf096c9c29546620931d6c2e5a9b5/test/compliance/test_cases/i_string_not_in_unicode_range.json -------------------------------------------------------------------------------- /test/compliance/test_cases/i_string_overlong_sequence_2_bytes.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocaml-community/yojson/6a0395f8af9cf096c9c29546620931d6c2e5a9b5/test/compliance/test_cases/i_string_overlong_sequence_2_bytes.json -------------------------------------------------------------------------------- /test/compliance/test_cases/i_string_overlong_sequence_6_bytes.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocaml-community/yojson/6a0395f8af9cf096c9c29546620931d6c2e5a9b5/test/compliance/test_cases/i_string_overlong_sequence_6_bytes.json -------------------------------------------------------------------------------- /test/compliance/test_cases/i_string_overlong_sequence_6_bytes_null.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocaml-community/yojson/6a0395f8af9cf096c9c29546620931d6c2e5a9b5/test/compliance/test_cases/i_string_overlong_sequence_6_bytes_null.json -------------------------------------------------------------------------------- /test/compliance/test_cases/i_string_truncated-utf-8.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocaml-community/yojson/6a0395f8af9cf096c9c29546620931d6c2e5a9b5/test/compliance/test_cases/i_string_truncated-utf-8.json -------------------------------------------------------------------------------- /test/compliance/test_cases/i_string_utf16BE_no_BOM.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocaml-community/yojson/6a0395f8af9cf096c9c29546620931d6c2e5a9b5/test/compliance/test_cases/i_string_utf16BE_no_BOM.json -------------------------------------------------------------------------------- /test/compliance/test_cases/i_string_utf16LE_no_BOM.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocaml-community/yojson/6a0395f8af9cf096c9c29546620931d6c2e5a9b5/test/compliance/test_cases/i_string_utf16LE_no_BOM.json -------------------------------------------------------------------------------- /test/compliance/test_cases/i_structure_500_nested_arrays.json: -------------------------------------------------------------------------------- 1 | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] -------------------------------------------------------------------------------- /test/compliance/test_cases/i_structure_UTF-8_BOM_empty_object.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/compliance/test_cases/n_array_1_true_without_comma.json: -------------------------------------------------------------------------------- 1 | [1 true] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_array_a_invalid_utf8.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocaml-community/yojson/6a0395f8af9cf096c9c29546620931d6c2e5a9b5/test/compliance/test_cases/n_array_a_invalid_utf8.json -------------------------------------------------------------------------------- /test/compliance/test_cases/n_array_colon_instead_of_comma.json: -------------------------------------------------------------------------------- 1 | ["": 1] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_array_comma_after_close.json: -------------------------------------------------------------------------------- 1 | [""], -------------------------------------------------------------------------------- /test/compliance/test_cases/n_array_comma_and_number.json: -------------------------------------------------------------------------------- 1 | [,1] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_array_double_comma.json: -------------------------------------------------------------------------------- 1 | [1,,2] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_array_double_extra_comma.json: -------------------------------------------------------------------------------- 1 | ["x",,] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_array_extra_close.json: -------------------------------------------------------------------------------- 1 | ["x"]] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_array_extra_comma.json: -------------------------------------------------------------------------------- 1 | ["",] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_array_incomplete.json: -------------------------------------------------------------------------------- 1 | ["x" -------------------------------------------------------------------------------- /test/compliance/test_cases/n_array_incomplete_invalid_value.json: -------------------------------------------------------------------------------- 1 | [x -------------------------------------------------------------------------------- /test/compliance/test_cases/n_array_inner_array_no_comma.json: -------------------------------------------------------------------------------- 1 | [3[4]] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_array_invalid_utf8.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocaml-community/yojson/6a0395f8af9cf096c9c29546620931d6c2e5a9b5/test/compliance/test_cases/n_array_invalid_utf8.json -------------------------------------------------------------------------------- /test/compliance/test_cases/n_array_items_separated_by_semicolon.json: -------------------------------------------------------------------------------- 1 | [1:2] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_array_just_comma.json: -------------------------------------------------------------------------------- 1 | [,] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_array_just_minus.json: -------------------------------------------------------------------------------- 1 | [-] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_array_missing_value.json: -------------------------------------------------------------------------------- 1 | [ , ""] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_array_newlines_unclosed.json: -------------------------------------------------------------------------------- 1 | ["a", 2 | 4 3 | ,1, -------------------------------------------------------------------------------- /test/compliance/test_cases/n_array_number_and_comma.json: -------------------------------------------------------------------------------- 1 | [1,] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_array_number_and_several_commas.json: -------------------------------------------------------------------------------- 1 | [1,,] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_array_spaces_vertical_tab_formfeed.json: -------------------------------------------------------------------------------- 1 | [" a"\f] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_array_star_inside.json: -------------------------------------------------------------------------------- 1 | [*] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_array_unclosed.json: -------------------------------------------------------------------------------- 1 | ["" -------------------------------------------------------------------------------- /test/compliance/test_cases/n_array_unclosed_trailing_comma.json: -------------------------------------------------------------------------------- 1 | [1, -------------------------------------------------------------------------------- /test/compliance/test_cases/n_array_unclosed_with_new_lines.json: -------------------------------------------------------------------------------- 1 | [1, 2 | 1 3 | ,1 -------------------------------------------------------------------------------- /test/compliance/test_cases/n_array_unclosed_with_object_inside.json: -------------------------------------------------------------------------------- 1 | [{} -------------------------------------------------------------------------------- /test/compliance/test_cases/n_incomplete_false.json: -------------------------------------------------------------------------------- 1 | [fals] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_incomplete_null.json: -------------------------------------------------------------------------------- 1 | [nul] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_incomplete_true.json: -------------------------------------------------------------------------------- 1 | [tru] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_multidigit_number_then_00.json: -------------------------------------------------------------------------------- 1 | 123 -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_++.json: -------------------------------------------------------------------------------- 1 | [++1234] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_+1.json: -------------------------------------------------------------------------------- 1 | [+1] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_+Inf.json: -------------------------------------------------------------------------------- 1 | [+Inf] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_-01.json: -------------------------------------------------------------------------------- 1 | [-01] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_-1.0..json: -------------------------------------------------------------------------------- 1 | [-1.0.] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_-2..json: -------------------------------------------------------------------------------- 1 | [-2.] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_-NaN.json: -------------------------------------------------------------------------------- 1 | [-NaN] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_.-1.json: -------------------------------------------------------------------------------- 1 | [.-1] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_.2e-3.json: -------------------------------------------------------------------------------- 1 | [.2e-3] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_0.1.2.json: -------------------------------------------------------------------------------- 1 | [0.1.2] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_0.3e+.json: -------------------------------------------------------------------------------- 1 | [0.3e+] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_0.3e.json: -------------------------------------------------------------------------------- 1 | [0.3e] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_0.e1.json: -------------------------------------------------------------------------------- 1 | [0.e1] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_0_capital_E+.json: -------------------------------------------------------------------------------- 1 | [0E+] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_0_capital_E.json: -------------------------------------------------------------------------------- 1 | [0E] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_0e+.json: -------------------------------------------------------------------------------- 1 | [0e+] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_0e.json: -------------------------------------------------------------------------------- 1 | [0e] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_1.0e+.json: -------------------------------------------------------------------------------- 1 | [1.0e+] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_1.0e-.json: -------------------------------------------------------------------------------- 1 | [1.0e-] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_1.0e.json: -------------------------------------------------------------------------------- 1 | [1.0e] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_1_000.json: -------------------------------------------------------------------------------- 1 | [1 000.0] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_1eE2.json: -------------------------------------------------------------------------------- 1 | [1eE2] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_2.e+3.json: -------------------------------------------------------------------------------- 1 | [2.e+3] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_2.e-3.json: -------------------------------------------------------------------------------- 1 | [2.e-3] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_2.e3.json: -------------------------------------------------------------------------------- 1 | [2.e3] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_9.e+.json: -------------------------------------------------------------------------------- 1 | [9.e+] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_Inf.json: -------------------------------------------------------------------------------- 1 | [Inf] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_NaN.json: -------------------------------------------------------------------------------- 1 | [NaN] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_U+FF11_fullwidth_digit_one.json: -------------------------------------------------------------------------------- 1 | [1] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_expression.json: -------------------------------------------------------------------------------- 1 | [1+2] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_hex_1_digit.json: -------------------------------------------------------------------------------- 1 | [0x1] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_hex_2_digits.json: -------------------------------------------------------------------------------- 1 | [0x42] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_infinity.json: -------------------------------------------------------------------------------- 1 | [Infinity] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_invalid+-.json: -------------------------------------------------------------------------------- 1 | [0e+-1] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_invalid-negative-real.json: -------------------------------------------------------------------------------- 1 | [-123.123foo] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_invalid-utf-8-in-bigger-int.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocaml-community/yojson/6a0395f8af9cf096c9c29546620931d6c2e5a9b5/test/compliance/test_cases/n_number_invalid-utf-8-in-bigger-int.json -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_invalid-utf-8-in-exponent.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocaml-community/yojson/6a0395f8af9cf096c9c29546620931d6c2e5a9b5/test/compliance/test_cases/n_number_invalid-utf-8-in-exponent.json -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_invalid-utf-8-in-int.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocaml-community/yojson/6a0395f8af9cf096c9c29546620931d6c2e5a9b5/test/compliance/test_cases/n_number_invalid-utf-8-in-int.json -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_minus_infinity.json: -------------------------------------------------------------------------------- 1 | [-Infinity] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_minus_sign_with_trailing_garbage.json: -------------------------------------------------------------------------------- 1 | [-foo] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_minus_space_1.json: -------------------------------------------------------------------------------- 1 | [- 1] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_neg_int_starting_with_zero.json: -------------------------------------------------------------------------------- 1 | [-012] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_neg_real_without_int_part.json: -------------------------------------------------------------------------------- 1 | [-.123] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_neg_with_garbage_at_end.json: -------------------------------------------------------------------------------- 1 | [-1x] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_real_garbage_after_e.json: -------------------------------------------------------------------------------- 1 | [1ea] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_real_with_invalid_utf8_after_e.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocaml-community/yojson/6a0395f8af9cf096c9c29546620931d6c2e5a9b5/test/compliance/test_cases/n_number_real_with_invalid_utf8_after_e.json -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_real_without_fractional_part.json: -------------------------------------------------------------------------------- 1 | [1.] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_starting_with_dot.json: -------------------------------------------------------------------------------- 1 | [.123] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_with_alpha.json: -------------------------------------------------------------------------------- 1 | [1.2a-3] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_with_alpha_char.json: -------------------------------------------------------------------------------- 1 | [1.8011670033376514H-308] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_number_with_leading_zero.json: -------------------------------------------------------------------------------- 1 | [012] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_object_bad_value.json: -------------------------------------------------------------------------------- 1 | ["x", truth] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_object_bracket_key.json: -------------------------------------------------------------------------------- 1 | {[: "x"} 2 | -------------------------------------------------------------------------------- /test/compliance/test_cases/n_object_comma_instead_of_colon.json: -------------------------------------------------------------------------------- 1 | {"x", null} -------------------------------------------------------------------------------- /test/compliance/test_cases/n_object_double_colon.json: -------------------------------------------------------------------------------- 1 | {"x"::"b"} -------------------------------------------------------------------------------- /test/compliance/test_cases/n_object_emoji.json: -------------------------------------------------------------------------------- 1 | {🇨🇭} -------------------------------------------------------------------------------- /test/compliance/test_cases/n_object_garbage_at_end.json: -------------------------------------------------------------------------------- 1 | {"a":"a" 123} -------------------------------------------------------------------------------- /test/compliance/test_cases/n_object_key_with_single_quotes.json: -------------------------------------------------------------------------------- 1 | {key: 'value'} -------------------------------------------------------------------------------- /test/compliance/test_cases/n_object_lone_continuation_byte_in_key_and_trailing_comma.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocaml-community/yojson/6a0395f8af9cf096c9c29546620931d6c2e5a9b5/test/compliance/test_cases/n_object_lone_continuation_byte_in_key_and_trailing_comma.json -------------------------------------------------------------------------------- /test/compliance/test_cases/n_object_missing_colon.json: -------------------------------------------------------------------------------- 1 | {"a" b} -------------------------------------------------------------------------------- /test/compliance/test_cases/n_object_missing_key.json: -------------------------------------------------------------------------------- 1 | {:"b"} -------------------------------------------------------------------------------- /test/compliance/test_cases/n_object_missing_semicolon.json: -------------------------------------------------------------------------------- 1 | {"a" "b"} -------------------------------------------------------------------------------- /test/compliance/test_cases/n_object_missing_value.json: -------------------------------------------------------------------------------- 1 | {"a": -------------------------------------------------------------------------------- /test/compliance/test_cases/n_object_no-colon.json: -------------------------------------------------------------------------------- 1 | {"a" -------------------------------------------------------------------------------- /test/compliance/test_cases/n_object_non_string_key.json: -------------------------------------------------------------------------------- 1 | {1:1} -------------------------------------------------------------------------------- /test/compliance/test_cases/n_object_non_string_key_but_huge_number_instead.json: -------------------------------------------------------------------------------- 1 | {9999E9999:1} -------------------------------------------------------------------------------- /test/compliance/test_cases/n_object_repeated_null_null.json: -------------------------------------------------------------------------------- 1 | {null:null,null:null} -------------------------------------------------------------------------------- /test/compliance/test_cases/n_object_several_trailing_commas.json: -------------------------------------------------------------------------------- 1 | {"id":0,,,,,} -------------------------------------------------------------------------------- /test/compliance/test_cases/n_object_single_quote.json: -------------------------------------------------------------------------------- 1 | {'a':0} -------------------------------------------------------------------------------- /test/compliance/test_cases/n_object_trailing_comma.json: -------------------------------------------------------------------------------- 1 | {"id":0,} -------------------------------------------------------------------------------- /test/compliance/test_cases/n_object_trailing_comment.json: -------------------------------------------------------------------------------- 1 | {"a":"b"}/**/ -------------------------------------------------------------------------------- /test/compliance/test_cases/n_object_trailing_comment_open.json: -------------------------------------------------------------------------------- 1 | {"a":"b"}/**// -------------------------------------------------------------------------------- /test/compliance/test_cases/n_object_trailing_comment_slash_open.json: -------------------------------------------------------------------------------- 1 | {"a":"b"}// -------------------------------------------------------------------------------- /test/compliance/test_cases/n_object_trailing_comment_slash_open_incomplete.json: -------------------------------------------------------------------------------- 1 | {"a":"b"}/ -------------------------------------------------------------------------------- /test/compliance/test_cases/n_object_two_commas_in_a_row.json: -------------------------------------------------------------------------------- 1 | {"a":"b",,"c":"d"} -------------------------------------------------------------------------------- /test/compliance/test_cases/n_object_unquoted_key.json: -------------------------------------------------------------------------------- 1 | {a: "b"} -------------------------------------------------------------------------------- /test/compliance/test_cases/n_object_unterminated-value.json: -------------------------------------------------------------------------------- 1 | {"a":"a -------------------------------------------------------------------------------- /test/compliance/test_cases/n_object_with_single_string.json: -------------------------------------------------------------------------------- 1 | { "foo" : "bar", "a" } -------------------------------------------------------------------------------- /test/compliance/test_cases/n_object_with_trailing_garbage.json: -------------------------------------------------------------------------------- 1 | {"a":"b"}# -------------------------------------------------------------------------------- /test/compliance/test_cases/n_single_space.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_1_surrogate_then_escape.json: -------------------------------------------------------------------------------- 1 | ["\uD800\"] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_1_surrogate_then_escape_u.json: -------------------------------------------------------------------------------- 1 | ["\uD800\u"] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_1_surrogate_then_escape_u1.json: -------------------------------------------------------------------------------- 1 | ["\uD800\u1"] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_1_surrogate_then_escape_u1x.json: -------------------------------------------------------------------------------- 1 | ["\uD800\u1x"] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_accentuated_char_no_quotes.json: -------------------------------------------------------------------------------- 1 | [é] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_backslash_00.json: -------------------------------------------------------------------------------- 1 | ["\"] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_escape_x.json: -------------------------------------------------------------------------------- 1 | ["\x00"] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_escaped_backslash_bad.json: -------------------------------------------------------------------------------- 1 | ["\\\"] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_escaped_ctrl_char_tab.json: -------------------------------------------------------------------------------- 1 | ["\ "] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_escaped_emoji.json: -------------------------------------------------------------------------------- 1 | ["\🌀"] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_incomplete_escape.json: -------------------------------------------------------------------------------- 1 | ["\"] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_incomplete_escaped_character.json: -------------------------------------------------------------------------------- 1 | ["\u00A"] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_incomplete_surrogate.json: -------------------------------------------------------------------------------- 1 | ["\uD834\uDd"] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_incomplete_surrogate_escape_invalid.json: -------------------------------------------------------------------------------- 1 | ["\uD800\uD800\x"] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_invalid-utf-8-in-escape.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocaml-community/yojson/6a0395f8af9cf096c9c29546620931d6c2e5a9b5/test/compliance/test_cases/n_string_invalid-utf-8-in-escape.json -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_invalid_backslash_esc.json: -------------------------------------------------------------------------------- 1 | ["\a"] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_invalid_unicode_escape.json: -------------------------------------------------------------------------------- 1 | ["\uqqqq"] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_invalid_utf8_after_escape.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocaml-community/yojson/6a0395f8af9cf096c9c29546620931d6c2e5a9b5/test/compliance/test_cases/n_string_invalid_utf8_after_escape.json -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_leading_uescaped_thinspace.json: -------------------------------------------------------------------------------- 1 | [\u0020"asd"] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_no_quotes_with_bad_escape.json: -------------------------------------------------------------------------------- 1 | [\n] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_single_doublequote.json: -------------------------------------------------------------------------------- 1 | " -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_single_quote.json: -------------------------------------------------------------------------------- 1 | ['single quote'] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_single_string_no_double_quotes.json: -------------------------------------------------------------------------------- 1 | abc -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_start_escape_unclosed.json: -------------------------------------------------------------------------------- 1 | ["\ -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_unescaped_crtl_char.json: -------------------------------------------------------------------------------- 1 | ["aa"] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_unescaped_newline.json: -------------------------------------------------------------------------------- 1 | ["new 2 | line"] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_unescaped_tab.json: -------------------------------------------------------------------------------- 1 | [" "] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_unicode_CapitalU.json: -------------------------------------------------------------------------------- 1 | "\UA66D" -------------------------------------------------------------------------------- /test/compliance/test_cases/n_string_with_trailing_garbage.json: -------------------------------------------------------------------------------- 1 | ""x -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_U+2060_word_joined.json: -------------------------------------------------------------------------------- 1 | [⁠] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_UTF8_BOM_no_data.json: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_angle_bracket_..json: -------------------------------------------------------------------------------- 1 | <.> -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_angle_bracket_null.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_array_trailing_garbage.json: -------------------------------------------------------------------------------- 1 | [1]x -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_array_with_extra_array_close.json: -------------------------------------------------------------------------------- 1 | [1]] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_array_with_unclosed_string.json: -------------------------------------------------------------------------------- 1 | ["asd] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_ascii-unicode-identifier.json: -------------------------------------------------------------------------------- 1 | aå -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_capitalized_True.json: -------------------------------------------------------------------------------- 1 | [True] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_close_unopened_array.json: -------------------------------------------------------------------------------- 1 | 1] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_comma_instead_of_closing_brace.json: -------------------------------------------------------------------------------- 1 | {"x": true, -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_double_array.json: -------------------------------------------------------------------------------- 1 | [][] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_end_array.json: -------------------------------------------------------------------------------- 1 | ] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_incomplete_UTF8_BOM.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocaml-community/yojson/6a0395f8af9cf096c9c29546620931d6c2e5a9b5/test/compliance/test_cases/n_structure_incomplete_UTF8_BOM.json -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_lone-invalid-utf-8.json: -------------------------------------------------------------------------------- 1 | � -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_lone-open-bracket.json: -------------------------------------------------------------------------------- 1 | [ -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_no_data.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ocaml-community/yojson/6a0395f8af9cf096c9c29546620931d6c2e5a9b5/test/compliance/test_cases/n_structure_no_data.json -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_null-byte-outside-string.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_number_with_trailing_garbage.json: -------------------------------------------------------------------------------- 1 | 2@ -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_object_followed_by_closing_object.json: -------------------------------------------------------------------------------- 1 | {}} -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_object_unclosed_no_value.json: -------------------------------------------------------------------------------- 1 | {"": -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_object_with_comment.json: -------------------------------------------------------------------------------- 1 | {"a":/*comment*/"b"} -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_object_with_trailing_garbage.json: -------------------------------------------------------------------------------- 1 | {"a": true} "x" -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_open_array_apostrophe.json: -------------------------------------------------------------------------------- 1 | [' -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_open_array_comma.json: -------------------------------------------------------------------------------- 1 | [, -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_open_array_open_object.json: -------------------------------------------------------------------------------- 1 | [{ -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_open_array_open_string.json: -------------------------------------------------------------------------------- 1 | ["a -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_open_array_string.json: -------------------------------------------------------------------------------- 1 | ["a" -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_open_object.json: -------------------------------------------------------------------------------- 1 | { -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_open_object_close_array.json: -------------------------------------------------------------------------------- 1 | {] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_open_object_comma.json: -------------------------------------------------------------------------------- 1 | {, -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_open_object_open_array.json: -------------------------------------------------------------------------------- 1 | {[ -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_open_object_open_string.json: -------------------------------------------------------------------------------- 1 | {"a -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_open_object_string_with_apostrophes.json: -------------------------------------------------------------------------------- 1 | {'a' -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_open_open.json: -------------------------------------------------------------------------------- 1 | ["\{["\{["\{["\{ -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_single_eacute.json: -------------------------------------------------------------------------------- 1 | � -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_single_star.json: -------------------------------------------------------------------------------- 1 | * -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_trailing_#.json: -------------------------------------------------------------------------------- 1 | {"a":"b"}#{} -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_uescaped_LF_before_string.json: -------------------------------------------------------------------------------- 1 | [\u000A""] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_unclosed_array.json: -------------------------------------------------------------------------------- 1 | [1 -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_unclosed_array_partial_null.json: -------------------------------------------------------------------------------- 1 | [ false, nul -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_unclosed_array_unfinished_false.json: -------------------------------------------------------------------------------- 1 | [ true, fals -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_unclosed_array_unfinished_true.json: -------------------------------------------------------------------------------- 1 | [ false, tru -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_unclosed_object.json: -------------------------------------------------------------------------------- 1 | {"asd":"asd" -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_unicode-identifier.json: -------------------------------------------------------------------------------- 1 | å -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_whitespace_U+2060_word_joiner.json: -------------------------------------------------------------------------------- 1 | [⁠] -------------------------------------------------------------------------------- /test/compliance/test_cases/n_structure_whitespace_formfeed.json: -------------------------------------------------------------------------------- 1 | [ ] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_array_arraysWithSpaces.json: -------------------------------------------------------------------------------- 1 | [[] ] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_array_empty-string.json: -------------------------------------------------------------------------------- 1 | [""] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_array_empty.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_array_ending_with_newline.json: -------------------------------------------------------------------------------- 1 | ["a"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_array_false.json: -------------------------------------------------------------------------------- 1 | [false] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_array_heterogeneous.json: -------------------------------------------------------------------------------- 1 | [null, 1, "1", {}] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_array_null.json: -------------------------------------------------------------------------------- 1 | [null] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_array_with_1_and_newline.json: -------------------------------------------------------------------------------- 1 | [1 2 | ] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_array_with_leading_space.json: -------------------------------------------------------------------------------- 1 | [1] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_array_with_several_null.json: -------------------------------------------------------------------------------- 1 | [1,null,null,null,2] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_array_with_trailing_space.json: -------------------------------------------------------------------------------- 1 | [2] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_number.json: -------------------------------------------------------------------------------- 1 | [123e65] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_number_0e+1.json: -------------------------------------------------------------------------------- 1 | [0e+1] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_number_0e1.json: -------------------------------------------------------------------------------- 1 | [0e1] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_number_after_space.json: -------------------------------------------------------------------------------- 1 | [ 4] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_number_double_close_to_zero.json: -------------------------------------------------------------------------------- 1 | [-0.000000000000000000000000000000000000000000000000000000000000000000000000000001] 2 | -------------------------------------------------------------------------------- /test/compliance/test_cases/y_number_int_with_exp.json: -------------------------------------------------------------------------------- 1 | [20e1] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_number_minus_zero.json: -------------------------------------------------------------------------------- 1 | [-0] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_number_negative_int.json: -------------------------------------------------------------------------------- 1 | [-123] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_number_negative_one.json: -------------------------------------------------------------------------------- 1 | [-1] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_number_negative_zero.json: -------------------------------------------------------------------------------- 1 | [-0] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_number_real_capital_e.json: -------------------------------------------------------------------------------- 1 | [1E22] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_number_real_capital_e_neg_exp.json: -------------------------------------------------------------------------------- 1 | [1E-2] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_number_real_capital_e_pos_exp.json: -------------------------------------------------------------------------------- 1 | [1E+2] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_number_real_exponent.json: -------------------------------------------------------------------------------- 1 | [123e45] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_number_real_fraction_exponent.json: -------------------------------------------------------------------------------- 1 | [123.456e78] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_number_real_neg_exp.json: -------------------------------------------------------------------------------- 1 | [1e-2] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_number_real_pos_exponent.json: -------------------------------------------------------------------------------- 1 | [1e+2] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_number_simple_int.json: -------------------------------------------------------------------------------- 1 | [123] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_number_simple_real.json: -------------------------------------------------------------------------------- 1 | [123.456789] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_object.json: -------------------------------------------------------------------------------- 1 | {"asd":"sdf", "dfg":"fgh"} -------------------------------------------------------------------------------- /test/compliance/test_cases/y_object_basic.json: -------------------------------------------------------------------------------- 1 | {"asd":"sdf"} -------------------------------------------------------------------------------- /test/compliance/test_cases/y_object_duplicated_key.json: -------------------------------------------------------------------------------- 1 | {"a":"b","a":"c"} -------------------------------------------------------------------------------- /test/compliance/test_cases/y_object_duplicated_key_and_value.json: -------------------------------------------------------------------------------- 1 | {"a":"b","a":"b"} -------------------------------------------------------------------------------- /test/compliance/test_cases/y_object_empty.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/compliance/test_cases/y_object_empty_key.json: -------------------------------------------------------------------------------- 1 | {"":0} -------------------------------------------------------------------------------- /test/compliance/test_cases/y_object_escaped_null_in_key.json: -------------------------------------------------------------------------------- 1 | {"foo\u0000bar": 42} -------------------------------------------------------------------------------- /test/compliance/test_cases/y_object_extreme_numbers.json: -------------------------------------------------------------------------------- 1 | { "min": -1.0e+28, "max": 1.0e+28 } -------------------------------------------------------------------------------- /test/compliance/test_cases/y_object_long_strings.json: -------------------------------------------------------------------------------- 1 | {"x":[{"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}], "id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"} -------------------------------------------------------------------------------- /test/compliance/test_cases/y_object_simple.json: -------------------------------------------------------------------------------- 1 | {"a":[]} -------------------------------------------------------------------------------- /test/compliance/test_cases/y_object_string_unicode.json: -------------------------------------------------------------------------------- 1 | {"title":"\u041f\u043e\u043b\u0442\u043e\u0440\u0430 \u0417\u0435\u043c\u043b\u0435\u043a\u043e\u043f\u0430" } -------------------------------------------------------------------------------- /test/compliance/test_cases/y_object_with_newlines.json: -------------------------------------------------------------------------------- 1 | { 2 | "a": "b" 3 | } -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_1_2_3_bytes_UTF-8_sequences.json: -------------------------------------------------------------------------------- 1 | ["\u0060\u012a\u12AB"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_accepted_surrogate_pair.json: -------------------------------------------------------------------------------- 1 | ["\uD801\udc37"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_accepted_surrogate_pairs.json: -------------------------------------------------------------------------------- 1 | ["\ud83d\ude39\ud83d\udc8d"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_allowed_escapes.json: -------------------------------------------------------------------------------- 1 | ["\"\\\/\b\f\n\r\t"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_backslash_and_u_escaped_zero.json: -------------------------------------------------------------------------------- 1 | ["\\u0000"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_backslash_doublequotes.json: -------------------------------------------------------------------------------- 1 | ["\""] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_comments.json: -------------------------------------------------------------------------------- 1 | ["a/*b*/c/*d//e"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_double_escape_a.json: -------------------------------------------------------------------------------- 1 | ["\\a"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_double_escape_n.json: -------------------------------------------------------------------------------- 1 | ["\\n"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_escaped_control_character.json: -------------------------------------------------------------------------------- 1 | ["\u0012"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_escaped_noncharacter.json: -------------------------------------------------------------------------------- 1 | ["\uFFFF"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_in_array.json: -------------------------------------------------------------------------------- 1 | ["asd"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_in_array_with_leading_space.json: -------------------------------------------------------------------------------- 1 | [ "asd"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_last_surrogates_1_and_2.json: -------------------------------------------------------------------------------- 1 | ["\uDBFF\uDFFF"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_nbsp_uescaped.json: -------------------------------------------------------------------------------- 1 | ["new\u00A0line"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_nonCharacterInUTF-8_U+10FFFF.json: -------------------------------------------------------------------------------- 1 | ["􏿿"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_nonCharacterInUTF-8_U+FFFF.json: -------------------------------------------------------------------------------- 1 | ["￿"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_null_escape.json: -------------------------------------------------------------------------------- 1 | ["\u0000"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_one-byte-utf-8.json: -------------------------------------------------------------------------------- 1 | ["\u002c"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_pi.json: -------------------------------------------------------------------------------- 1 | ["π"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_reservedCharacterInUTF-8_U+1BFFF.json: -------------------------------------------------------------------------------- 1 | ["𛿿"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_simple_ascii.json: -------------------------------------------------------------------------------- 1 | ["asd "] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_space.json: -------------------------------------------------------------------------------- 1 | " " -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json: -------------------------------------------------------------------------------- 1 | ["\uD834\uDd1e"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_three-byte-utf-8.json: -------------------------------------------------------------------------------- 1 | ["\u0821"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_two-byte-utf-8.json: -------------------------------------------------------------------------------- 1 | ["\u0123"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_u+2028_line_sep.json: -------------------------------------------------------------------------------- 1 | ["
"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_u+2029_par_sep.json: -------------------------------------------------------------------------------- 1 | ["
"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_uEscape.json: -------------------------------------------------------------------------------- 1 | ["\u0061\u30af\u30EA\u30b9"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_uescaped_newline.json: -------------------------------------------------------------------------------- 1 | ["new\u000Aline"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_unescaped_char_delete.json: -------------------------------------------------------------------------------- 1 | [""] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_unicode.json: -------------------------------------------------------------------------------- 1 | ["\uA66D"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_unicodeEscapedBackslash.json: -------------------------------------------------------------------------------- 1 | ["\u005C"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_unicode_2.json: -------------------------------------------------------------------------------- 1 | ["⍂㈴⍂"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_unicode_U+10FFFE_nonchar.json: -------------------------------------------------------------------------------- 1 | ["\uDBFF\uDFFE"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_unicode_U+1FFFE_nonchar.json: -------------------------------------------------------------------------------- 1 | ["\uD83F\uDFFE"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json: -------------------------------------------------------------------------------- 1 | ["\u200B"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_unicode_U+2064_invisible_plus.json: -------------------------------------------------------------------------------- 1 | ["\u2064"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_unicode_U+FDD0_nonchar.json: -------------------------------------------------------------------------------- 1 | ["\uFDD0"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_unicode_U+FFFE_nonchar.json: -------------------------------------------------------------------------------- 1 | ["\uFFFE"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_unicode_escaped_double_quote.json: -------------------------------------------------------------------------------- 1 | ["\u0022"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_utf8.json: -------------------------------------------------------------------------------- 1 | ["€𝄞"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_string_with_del_character.json: -------------------------------------------------------------------------------- 1 | ["aa"] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_structure_lonely_false.json: -------------------------------------------------------------------------------- 1 | false -------------------------------------------------------------------------------- /test/compliance/test_cases/y_structure_lonely_int.json: -------------------------------------------------------------------------------- 1 | 42 -------------------------------------------------------------------------------- /test/compliance/test_cases/y_structure_lonely_negative_real.json: -------------------------------------------------------------------------------- 1 | -0.1 -------------------------------------------------------------------------------- /test/compliance/test_cases/y_structure_lonely_null.json: -------------------------------------------------------------------------------- 1 | null -------------------------------------------------------------------------------- /test/compliance/test_cases/y_structure_lonely_string.json: -------------------------------------------------------------------------------- 1 | "asd" -------------------------------------------------------------------------------- /test/compliance/test_cases/y_structure_lonely_true.json: -------------------------------------------------------------------------------- 1 | true -------------------------------------------------------------------------------- /test/compliance/test_cases/y_structure_string_empty.json: -------------------------------------------------------------------------------- 1 | "" -------------------------------------------------------------------------------- /test/compliance/test_cases/y_structure_trailing_newline.json: -------------------------------------------------------------------------------- 1 | ["a"] 2 | -------------------------------------------------------------------------------- /test/compliance/test_cases/y_structure_true_in_array.json: -------------------------------------------------------------------------------- 1 | [true] -------------------------------------------------------------------------------- /test/compliance/test_cases/y_structure_whitespace_array.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /test/dune: -------------------------------------------------------------------------------- 1 | (test 2 | (name test) 3 | (package yojson) 4 | (libraries yojson alcotest)) 5 | -------------------------------------------------------------------------------- /test/fixtures.ml: -------------------------------------------------------------------------------- 1 | let json_value = 2 | `Assoc 3 | [ 4 | ("null", `Null); 5 | ("bool", `Bool true); 6 | ("int", `Int 0); 7 | ("intlit", `Intlit "10000000000000000000"); 8 | ("float", `Float 0.); 9 | ("string", `String "string"); 10 | ("list", `List [ `Int 0; `Int 1; `Int 2 ]); 11 | ("assoc", `Assoc [ ("value", `Int 42) ]); 12 | ] 13 | 14 | let crlf = "\r\n" 15 | 16 | let snippets = 17 | [ 18 | "{"; 19 | {|"null":null,|}; 20 | {|"bool":true,|}; 21 | {|"int":0,|}; 22 | {|"intlit":10000000000000000000,|}; 23 | {|"float":0.0,|}; 24 | {|"string":"string",|}; 25 | {|"list":[0,1,2],|}; 26 | {|"assoc":{"value":42}|}; 27 | "}"; 28 | ] 29 | 30 | let json_string = String.concat "" snippets 31 | let json_string_crlf = String.concat crlf snippets 32 | let unquoted_json = {|{foo: null}|} 33 | let unquoted_value = `Assoc [ ("foo", `Null) ] 34 | let json_string_newline = json_string ^ "\n" 35 | -------------------------------------------------------------------------------- /test/fixtures.mli: -------------------------------------------------------------------------------- 1 | (** Shared test fixtures *) 2 | 3 | val json_value : Yojson.Safe.t 4 | (** A json value to use for testing *) 5 | 6 | val json_string : string 7 | (** A JSON string that must parse to [json_value] *) 8 | 9 | val json_string_crlf : string 10 | (** A JSON string separated by [\r\n] that must parse to [json_value] *) 11 | 12 | val json_string_newline : string 13 | (** The same JSON string terminated with a newline *) 14 | 15 | val unquoted_json : string 16 | val unquoted_value : Yojson.Safe.t 17 | -------------------------------------------------------------------------------- /test/pretty/atd.expected.json: -------------------------------------------------------------------------------- 1 | [ 2 | { "r1": "testing" }, 3 | { "r2": [ "Some", 2 ] }, 4 | { "r2": "None" }, 5 | { "r3": [ "Some", 3 ] }, 6 | {}, 7 | { "r4": true }, 8 | { "r5": [ "Some", 5 ] }, 9 | {}, 10 | { "r6": 6 }, 11 | {}, 12 | { "r7": -1000 }, 13 | { "r8": [ 1, 2, 3 ] }, 14 | [ "foo", "bar" ], 15 | [], 16 | null, 17 | [ 1, 2, 3 ], 18 | 99, 19 | { "foo": 7, "bar": 8, "baz": 43 }, 20 | { "foo2": 5, "bar2": 6, "baz2": 41, "42": 42 }, 21 | [ 100, "foo" ], 22 | [ 100, 200, 42 ], 23 | [ 100, 200, -1 ], 24 | [ 25 | "V1", 26 | "v22", 27 | [ "V3", "testing" ], 28 | [ "V44", 255 ], 29 | [ "V5", "None" ], 30 | [ "V5", [ "Some", true ] ] 31 | ], 32 | { "v2": "A" }, 33 | { "v2": [ "B", 100 ] }, 34 | [ "C1", [ "C2", true ], [ "C2", false ] ], 35 | [ 50, 30, -1, 400 ] 36 | ] 37 | -------------------------------------------------------------------------------- /test/pretty/atd.ml: -------------------------------------------------------------------------------- 1 | let massive_json = 2 | `List 3 | [ 4 | `Assoc [ ("r1", `String "testing") ]; 5 | `Assoc [ ("r2", `List [ `String "Some"; `Int 2 ]) ]; 6 | `Assoc [ ("r2", `String "None") ]; 7 | `Assoc [ ("r3", `List [ `String "Some"; `Int 3 ]) ]; 8 | `Assoc []; 9 | `Assoc [ ("r4", `Bool true) ]; 10 | `Assoc [ ("r5", `List [ `String "Some"; `Int 5 ]) ]; 11 | `Assoc []; 12 | `Assoc [ ("r6", `Int 6) ]; 13 | `Assoc []; 14 | `Assoc [ ("r7", `Int (-1_000)) ]; 15 | `Assoc [ ("r8", `List [ `Int 1; `Int 2; `Int 3 ]) ]; 16 | `List [ `String "foo"; `String "bar" ]; 17 | `List []; 18 | `Null; 19 | `List [ `Int 1; `Int 2; `Int 3 ]; 20 | `Int 99; 21 | `Assoc [ ("foo", `Int 7); ("bar", `Int 8); ("baz", `Int 43) ]; 22 | `Assoc 23 | [ 24 | ("foo2", `Int 5); ("bar2", `Int 6); ("baz2", `Int 41); ("42", `Int 42); 25 | ]; 26 | `List [ `Int 100; `String "foo" ]; 27 | `List [ `Int 100; `Int 200; `Int 42 ]; 28 | `List [ `Int 100; `Int 200; `Int (-1) ]; 29 | `List 30 | [ 31 | `String "V1"; 32 | `String "v22"; 33 | `List [ `String "V3"; `String "testing" ]; 34 | `List [ `String "V44"; `Int 255 ]; 35 | `List [ `String "V5"; `String "None" ]; 36 | `List [ `String "V5"; `List [ `String "Some"; `Bool true ] ]; 37 | ]; 38 | `Assoc [ ("v2", `String "A") ]; 39 | `Assoc [ ("v2", `List [ `String "B"; `Int 100 ]) ]; 40 | `List 41 | [ 42 | `String "C1"; 43 | `List [ `String "C2"; `Bool true ]; 44 | `List [ `String "C2"; `Bool false ]; 45 | ]; 46 | `List [ `Int 50; `Int 30; `Int (-1); `Int 400 ]; 47 | ] 48 | 49 | let pp_json fmt json = 50 | Format.pp_print_string fmt (Yojson.Safe.pretty_to_string ~std:true json) 51 | 52 | let () = Format.printf "%a\n" pp_json massive_json 53 | -------------------------------------------------------------------------------- /test/pretty/dune: -------------------------------------------------------------------------------- 1 | (executables 2 | (names test atd) 3 | (libraries yojson)) 4 | 5 | (rule 6 | (deps ./sample.json) 7 | (action 8 | (with-stdout-to 9 | test.output.json 10 | (run ./test.exe)))) 11 | 12 | (rule 13 | (action 14 | (with-stdout-to 15 | atd.output.json 16 | (run ./atd.exe)))) 17 | 18 | (rule 19 | (alias runtest) 20 | (action 21 | (progn 22 | (diff test.expected.json test.output.json) 23 | (diff atd.expected.json atd.output.json)))) 24 | -------------------------------------------------------------------------------- /test/pretty/sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "abc": [ 1, 2, -3 ], 3 | aaaaoooaoaooooooooaoaoaoooaoa: { 4 | "big int": 123456789012345678901837292020484756564574 5 | }, 6 | "array": [ 7 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10 | 0, 0, 0, 0, 0, 0, 0, 0, 0 11 | ], 12 | "'NULL' U+0000": "\u0000", 13 | "'VULGAR FRACTION ONE HALF' U+00BD": "\u00BD", 14 | "'PILE OF POO' U+1F4A9": "\uD83D\uDCA9", 15 | "min_int": "-4611686018427387904" 16 | } 17 | -------------------------------------------------------------------------------- /test/pretty/test.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "abc": [ 1, 2, -3 ], 3 | "aaaaoooaoaooooooooaoaoaoooaoa": { 4 | "big int": 123456789012345678901837292020484756564574 5 | }, 6 | "array": [ 7 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10 | 0, 0, 0 11 | ], 12 | "'NULL' U+0000": "\u0000", 13 | "'VULGAR FRACTION ONE HALF' U+00BD": "½", 14 | "'PILE OF POO' U+1F4A9": "💩", 15 | "min_int": "-4611686018427387904" 16 | } 17 | -------------------------------------------------------------------------------- /test/pretty/test.ml: -------------------------------------------------------------------------------- 1 | module J = Yojson.Safe 2 | 3 | let () = 4 | let j = J.from_file "sample.json" in 5 | Format.printf "%a@." (J.pretty_print ?std:None) j; 6 | () 7 | -------------------------------------------------------------------------------- /test/test.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | Alcotest.run "Yojson" 3 | [ 4 | ("equality", Test_monomorphic.equality); 5 | ("read", Test_read.single_json); 6 | ("write", Test_write.single_json); 7 | ("util", Test_util.tests); 8 | ] 9 | -------------------------------------------------------------------------------- /test/test_monomorphic.ml: -------------------------------------------------------------------------------- 1 | let null = `Null 2 | let bool = `Bool true 3 | let other_bool = `Bool false 4 | let int = `Int 42 5 | let other_int = `Int 23 6 | let float = `Float 42.0 7 | let other_float = `Float 23.0 8 | let string = `String "kameloso" 9 | let other_string = `String "syggelekokle" 10 | 11 | let scalar_equal () = 12 | let open Testable in 13 | Alcotest.(check yojson) "Equal Null" null null; 14 | Alcotest.(check (neg yojson)) "Unequal Null" null int; 15 | Alcotest.(check yojson) "Equal bool" bool bool; 16 | Alcotest.(check (neg yojson)) "Unequal bool" bool other_bool; 17 | Alcotest.(check (neg yojson)) "Not a bool" bool int; 18 | Alcotest.(check yojson) "Equal int" int int; 19 | Alcotest.(check (neg yojson)) "Unequal int" int other_int; 20 | Alcotest.(check (neg yojson)) "Not an int" int float; 21 | Alcotest.(check yojson) "Equal Float" float float; 22 | Alcotest.(check (neg yojson)) "Unequal Float" float other_float; 23 | Alcotest.(check yojson) "Equal strings" string string; 24 | Alcotest.(check (neg yojson)) "Unequal strings" string other_string; 25 | Alcotest.(check (neg yojson)) "Unequal strings" string float 26 | 27 | let list_equal () = 28 | let open Testable in 29 | let list = `List [ int; int; float ] in 30 | let other_list = `List [ int; other_int; float ] in 31 | let empty_list = `List [] in 32 | Alcotest.(check yojson) "Equal lists" list list; 33 | Alcotest.(check (neg yojson)) "Unequal lists" list other_list; 34 | Alcotest.(check (neg yojson)) "Empty lists" list empty_list 35 | 36 | let assoc_equal () = 37 | let open Testable in 38 | let assoc = `Assoc [ ("a", int); ("b", float) ] in 39 | let other_assoc = `Assoc [ ("a", int); ("c", string) ] in 40 | let empty_assoc = `Assoc [] in 41 | Alcotest.(check yojson) "Equal assocs" assoc assoc; 42 | Alcotest.(check (neg yojson)) "Unequal assocs" assoc other_assoc; 43 | Alcotest.(check (neg yojson)) "Empty assoc" assoc empty_assoc; 44 | let simple_key = `Assoc [ ("a", int) ] in 45 | let duplicate_key = `Assoc [ ("a", int); ("a", int) ] in 46 | let different_values_duplicate = `Assoc [ ("a", int); ("a", float) ] in 47 | let flipped_values_duplicate = `Assoc [ ("a", float); ("a", int) ] in 48 | Alcotest.(check (neg yojson)) 49 | "Duplicate keys don't unify" simple_key duplicate_key; 50 | Alcotest.(check yojson) 51 | "Duplicate keys should still be equal" different_values_duplicate 52 | different_values_duplicate; 53 | Alcotest.(check (neg yojson)) 54 | "Duplicate keys not equal when different order" different_values_duplicate 55 | flipped_values_duplicate 56 | 57 | let equality = 58 | [ 59 | ("Scalar equality", `Quick, scalar_equal); 60 | ("List equality", `Quick, list_equal); 61 | ("Assoc equality", `Quick, assoc_equal); 62 | ] 63 | -------------------------------------------------------------------------------- /test/test_monomorphic.mli: -------------------------------------------------------------------------------- 1 | val equality : unit Alcotest.test_case list 2 | -------------------------------------------------------------------------------- /test/test_read.ml: -------------------------------------------------------------------------------- 1 | let from_string () = 2 | Alcotest.(check Testable.yojson) 3 | __LOC__ Fixtures.json_value 4 | (Yojson.Safe.from_string Fixtures.json_string) 5 | 6 | let from_crlf_string () = 7 | Alcotest.(check Testable.yojson) 8 | __LOC__ Fixtures.json_value 9 | (Yojson.Safe.from_string Fixtures.json_string_crlf) 10 | 11 | let parse s () = s |> Yojson.Safe.from_string |> ignore 12 | let parse_basic s () = s |> Yojson.Basic.from_string |> ignore 13 | 14 | let from_string_fail_simple () = 15 | Alcotest.check_raises "Location of parsing failure is correct" 16 | (Yojson.Json_error "Line 1, bytes 0-5:\nInvalid token 'hello'") 17 | (parse "hello") 18 | 19 | let from_string_fail_lines () = 20 | Alcotest.check_raises "Location of parsing failure has right line" 21 | (Yojson.Json_error "Line 3, bytes 0-1:\nExpected ':' but found '}'") 22 | (parse {|{ 23 | hello 24 | }|}) 25 | 26 | let from_string_fail_bytes () = 27 | Alcotest.check_raises "Location has right line and bytes" 28 | (Yojson.Json_error 29 | "Line 2, bytes 6-9:\nExpected string or identifier but found '3\n}'") 30 | (parse {|{ 31 | 3 32 | }|}) 33 | 34 | let from_string_fail_unterminated () = 35 | Alcotest.check_raises "Runaway string in toplevel" 36 | (Yojson.Json_error "Line 1, bytes 12-13:\nUnexpected end of input") 37 | (parse {|"unterminated|}) 38 | 39 | let from_string_fail_nested_unterminated () = 40 | Alcotest.check_raises "Runaway string in structure" 41 | (Yojson.Json_error "Line 2, bytes 5-6:\nUnexpected end of input") 42 | (parse {|[1, 43 | "]|}) 44 | 45 | let from_string_fail_unterminated_structure () = 46 | Alcotest.check_raises "Array never closed" 47 | (Yojson.Json_error "Line 1, bytes 0-1:\nUnexpected end of input") 48 | (parse "[") 49 | 50 | let from_string_fail_unstarted_structure () = 51 | Alcotest.check_raises "Array never opened" 52 | (Yojson.Json_error "Line 1, bytes 0-1:\nInvalid token ']'") (parse "]") 53 | 54 | let from_string_fail_unstarted_object () = 55 | Alcotest.check_raises "Object never opened" 56 | (Yojson.Json_error "Line 1, bytes 0-1:\nInvalid token '}'") (parse "}") 57 | 58 | let from_string_fail_large_int () = 59 | Alcotest.check_raises "Too large integer" 60 | (* TODO: this location wrong, shouldn't be negative *) 61 | (Yojson.Json_error 62 | "Line 1, bytes -1-19:\nInt overflow '4611686018427387905'") 63 | (* 2^62 + 1 *) 64 | (parse_basic "4611686018427387905") 65 | 66 | let from_string_fail_escaped_char () = 67 | Alcotest.check_raises "Invalid escape sequence" 68 | (Yojson.Json_error "Line 1, bytes 2-4:\nInvalid escape sequence 'a\"'") 69 | (parse {|"\a"|}) 70 | 71 | let from_file () = 72 | let input_file = Filename.temp_file "test_yojson_from_file" ".json" in 73 | let oc = open_out input_file in 74 | output_string oc Fixtures.json_string; 75 | close_out oc; 76 | Alcotest.(check Testable.yojson) 77 | __LOC__ Fixtures.json_value 78 | (Yojson.Safe.from_file input_file); 79 | Sys.remove input_file 80 | 81 | let unquoted_from_string () = 82 | Alcotest.(check Testable.yojson) 83 | __LOC__ Fixtures.unquoted_value 84 | (Yojson.Safe.from_string Fixtures.unquoted_json) 85 | 86 | let map_ident () = 87 | let lexbuf = Lexing.from_string {|{foo:"hello"}|} in 88 | let lexer_state = Yojson.init_lexer () in 89 | 90 | let ident_expected expectation reference start len = 91 | let identifier = String.sub reference start len in 92 | Alcotest.(check string) 93 | (Format.asprintf "Reference '%s' start %d len %d matches '%s'" reference 94 | start len expectation) 95 | expectation identifier; 96 | () 97 | in 98 | let skip_over f = f lexer_state lexbuf in 99 | let map_f mapper f = mapper lexer_state f lexbuf in 100 | let map_ident = map_f Yojson.Safe.map_ident in 101 | 102 | skip_over Yojson.Safe.read_lcurl; 103 | map_ident (ident_expected "foo"); 104 | skip_over Yojson.Safe.read_colon; 105 | 106 | let key = skip_over Yojson.Safe.read_string in 107 | Alcotest.(check string) "String is as expected" "hello" key; 108 | 109 | Alcotest.check_raises "Reading } raises End_of_object" Yojson.End_of_object 110 | (fun () -> Yojson.Safe.read_object_end lexbuf) 111 | 112 | let single_json = 113 | [ 114 | ("from_string", `Quick, from_string); 115 | ("from_crlf_string", `Quick, from_crlf_string); 116 | ("from_string_fail_simple", `Quick, from_string_fail_simple); 117 | ("from_string_fail_lines", `Quick, from_string_fail_lines); 118 | ("from_string_fail_bytes", `Quick, from_string_fail_bytes); 119 | ("from_string_fail_unterminated", `Quick, from_string_fail_unterminated); 120 | ( "from_string_fail_nested_unterminated", 121 | `Quick, 122 | from_string_fail_nested_unterminated ); 123 | ( "from_string_fail_unterminated_structure", 124 | `Quick, 125 | from_string_fail_unterminated_structure ); 126 | ( "from_string_fail_unstarted_structure", 127 | `Quick, 128 | from_string_fail_unstarted_structure ); 129 | ( "from_string_fail_unstarted_object", 130 | `Quick, 131 | from_string_fail_unstarted_object ); 132 | ("from_string_fail_large_int", `Quick, from_string_fail_large_int); 133 | ("from_string_fail_escaped_char", `Quick, from_string_fail_escaped_char); 134 | ("from_file", `Quick, from_file); 135 | ("unquoted_from_string", `Quick, unquoted_from_string); 136 | ("map_ident", `Quick, map_ident); 137 | ] 138 | -------------------------------------------------------------------------------- /test/test_read.mli: -------------------------------------------------------------------------------- 1 | val single_json : unit Alcotest.test_case list 2 | -------------------------------------------------------------------------------- /test/test_util.ml: -------------------------------------------------------------------------------- 1 | let path_empty () = 2 | Alcotest.(check (option Testable.yojson)) 3 | __LOC__ (Some Fixtures.json_value) 4 | (Yojson.Safe.Util.path [] Fixtures.json_value) 5 | 6 | let path_missing () = 7 | Alcotest.(check (option Testable.yojson)) 8 | __LOC__ None 9 | (Yojson.Safe.Util.path [ "does not exist" ] Fixtures.json_value) 10 | 11 | let path_traverse () = 12 | Alcotest.(check (option Testable.yojson)) 13 | __LOC__ 14 | (Some (`Int 42)) 15 | (Yojson.Safe.Util.path [ "assoc"; "value" ] Fixtures.json_value) 16 | 17 | let tests = 18 | [ 19 | ("empty path", `Quick, path_empty); 20 | ("non-existing path", `Quick, path_missing); 21 | ("traversal", `Quick, path_traverse); 22 | ] 23 | -------------------------------------------------------------------------------- /test/test_util.mli: -------------------------------------------------------------------------------- 1 | val tests : unit Alcotest.test_case list 2 | -------------------------------------------------------------------------------- /test/test_write.ml: -------------------------------------------------------------------------------- 1 | let to_string_tests = 2 | let test ?suf expected = 3 | Alcotest.(check string) 4 | __LOC__ expected 5 | (Yojson.Safe.to_string ?suf Fixtures.json_value) 6 | in 7 | [ 8 | ( "to_string with default settings", 9 | `Quick, 10 | fun () -> test Fixtures.json_string ); 11 | ( "to_string with newline", 12 | `Quick, 13 | fun () -> test ~suf:"\n" Fixtures.json_string_newline ); 14 | ( "to_string without newline", 15 | `Quick, 16 | fun () -> test ~suf:"" Fixtures.json_string ); 17 | ] 18 | 19 | let replace_crlf s = 20 | (* finds indices of \r\n *) 21 | let rec find_rn_idx from acc = 22 | match String.index_from_opt s from '\r' with 23 | | None -> 24 | (* no \r left in string *) 25 | acc 26 | | Some i -> ( 27 | match s.[i + 1] with 28 | | exception Invalid_argument _ -> 29 | (* \r was last in string *) 30 | acc 31 | | '\n' -> find_rn_idx (i + 2) (i :: acc) 32 | | _ -> find_rn_idx (i + 1) acc) 33 | in 34 | (* reads backwards to avoid List.rev *) 35 | let rec cut_parts until acc = function 36 | | [] -> 37 | (* last part, read from front *) 38 | let part = String.sub s 0 until in 39 | part :: acc 40 | | i :: idx -> 41 | let part = String.sub s (i + 2) (until - i - 2) in 42 | cut_parts i (part :: acc) idx 43 | in 44 | find_rn_idx 0 [] |> cut_parts (String.length s) [] |> String.concat "\n" 45 | 46 | let to_file_tests = 47 | let test ?suf expected = 48 | let output_file = Filename.temp_file "test_yojson_to_file" ".json" in 49 | Yojson.Safe.to_file ?suf output_file Fixtures.json_value; 50 | let file_content = 51 | let ic = open_in_bin output_file in 52 | let length = in_channel_length ic in 53 | let s = really_input_string ic length in 54 | close_in ic; 55 | replace_crlf s 56 | in 57 | Sys.remove output_file; 58 | Alcotest.(check string) __LOC__ expected file_content 59 | in 60 | [ 61 | ( "to_file with default settings", 62 | `Quick, 63 | fun () -> test Fixtures.json_string_newline ); 64 | ( "to_file with newline", 65 | `Quick, 66 | fun () -> test ~suf:"\n" Fixtures.json_string_newline ); 67 | ( "to_file without newline", 68 | `Quick, 69 | fun () -> test ~suf:"" Fixtures.json_string ); 70 | ] 71 | 72 | let seq_to_file_tests = 73 | let test ?suf () = 74 | let output_file = Filename.temp_file "test_yojson_seq_to_file" ".json" in 75 | let data = [ `String "foo"; `String "bar" ] in 76 | Yojson.Safe.seq_to_file ?suf output_file (List.to_seq data); 77 | let read_data = 78 | let seq = Yojson.Safe.seq_from_file output_file in 79 | let acc = ref [] in 80 | Seq.iter (fun v -> acc := v :: !acc) seq; 81 | List.rev !acc 82 | in 83 | Sys.remove output_file; 84 | Alcotest.(check (list Testable.yojson)) 85 | "seq_{to,from}_file roundtrip" data read_data 86 | in 87 | [ 88 | ("seq_to_file with default settings", `Quick, fun () -> test ()); 89 | ("seq_to_file with newline", `Quick, fun () -> test ~suf:"\n" ()); 90 | ("seq_to_file without newline", `Quick, fun () -> test ~suf:"" ()); 91 | ] 92 | 93 | let single_json = 94 | List.flatten [ to_file_tests; to_string_tests; seq_to_file_tests ] 95 | -------------------------------------------------------------------------------- /test/test_write.mli: -------------------------------------------------------------------------------- 1 | val single_json : unit Alcotest.test_case list 2 | -------------------------------------------------------------------------------- /test/testable.ml: -------------------------------------------------------------------------------- 1 | let yojson = Alcotest.testable Yojson.Safe.pp Yojson.Safe.equal 2 | -------------------------------------------------------------------------------- /test/testable.mli: -------------------------------------------------------------------------------- 1 | val yojson : Yojson.Safe.t Alcotest.testable 2 | -------------------------------------------------------------------------------- /test_json5/dune: -------------------------------------------------------------------------------- 1 | (test 2 | (name test) 3 | (package yojson-five) 4 | (libraries alcotest yojson_five)) 5 | -------------------------------------------------------------------------------- /test_json5/test.ml: -------------------------------------------------------------------------------- 1 | module M = Yojson_five.Safe 2 | 3 | let yojson = Alcotest.testable M.pp M.equal 4 | 5 | (* any error message will match the string. *) 6 | let any_string = Alcotest.testable Fmt.string (fun _ _ -> true) 7 | 8 | let parsing_test_case name error_msg expected input = 9 | Alcotest.test_case name `Quick (fun () -> 10 | Alcotest.(check (result yojson error_msg)) 11 | name expected (M.from_string input)) 12 | 13 | let parsing_should_succeed name input expected = 14 | parsing_test_case name Alcotest.string (Ok expected) input 15 | 16 | let parsing_should_fail name input = 17 | let failure = Error "" in 18 | parsing_test_case name any_string failure input 19 | 20 | let parsing_should_fail_with_error name input expected = 21 | parsing_test_case name Alcotest.string (Error expected) input 22 | 23 | let parsing_tests = 24 | [ 25 | parsing_should_fail "Unexpected line break" {|"foo 26 | bar"|}; 27 | parsing_should_succeed "true" "true" (`Bool true); 28 | parsing_should_succeed "false" "false" (`Bool false); 29 | parsing_should_succeed "null" "null" `Null; 30 | parsing_should_succeed "double quotes string" {|"hello world"|} 31 | (`String "hello world"); 32 | parsing_should_succeed "single quotes string" {|'hello world'|} 33 | (`String "hello world"); 34 | parsing_should_succeed "float" "12345.67890" (`Float 12345.67890); 35 | parsing_should_succeed "hex" "0x1" (`Int 0x1); 36 | parsing_should_succeed "hex escape sequence" {|"\x61"|} (`String "a"); 37 | parsing_should_succeed "unicode escape sequence" {|"\u03bb"|} (`String "λ"); 38 | parsing_should_succeed "more string escaping" 39 | "\"Hello \\u03bb \\x77\\x6F\\x72\\x6C\\x64\"" (`String "Hello λ world"); 40 | parsing_should_succeed "escaping quotes" {|"\"\'"|} (`String {|"'|}); 41 | parsing_should_succeed "more escaping quotes" {|'\"\''|} (`String {|"'|}); 42 | parsing_should_succeed "escaping other chars" {|'\A\C\/\D\C'|} 43 | (`String "AC/DC"); 44 | parsing_should_succeed "built in escapes" {|'\b\n\r\t\f\v'|} 45 | (`String "\b\n\r\t\012\011"); 46 | parsing_should_succeed "null byte string" {|"\0"|} (`String "\x00"); 47 | parsing_should_succeed "octal string" {|"\077"|} (`String "?"); 48 | parsing_should_succeed "null and octal string" {|"\07"|} (`String "\x007"); 49 | parsing_should_succeed "int" "1" (`Int 1); 50 | parsing_should_succeed "backslash escape" {|"foo\\bar"|} 51 | (`String {|foo\bar|}); 52 | parsing_should_succeed "line break" "\"foo\\\nbar\"" (`String "foobar"); 53 | parsing_should_succeed "string and comment" "\"bar\" //foo" (`String "bar"); 54 | (* objects *) 55 | parsing_should_succeed "empty object" "{}" (`Assoc []); 56 | parsing_should_succeed "object with double quote string" {|{"foo": "bar"}|} 57 | (`Assoc [ ("foo", `String "bar") ]); 58 | parsing_should_succeed "object with single quote string" {|{'foo': 'bar'}|} 59 | (`Assoc [ ("foo", `String "bar") ]); 60 | parsing_should_succeed "object with unquoted string" {|{foo: 'bar'}|} 61 | (`Assoc [ ("foo", `String "bar") ]); 62 | parsing_should_succeed "trailing comma in object" {|{"one": 1,}|} 63 | (`Assoc [ ("one", `Int 1) ]); 64 | parsing_should_succeed "colon in key" {|{"colon:": 1}|} 65 | (`Assoc [ ("colon:", `Int 1) ]); 66 | parsing_should_fail "multiple trailing commas in object" {|{"one": 1,,}|}; 67 | parsing_should_fail "just trailing comma in object" "{,}"; 68 | parsing_should_fail "just trailing commas in object" "{,,,}"; 69 | parsing_should_fail "multiple colons in object" {|{one :: 1}|}; 70 | parsing_should_fail "newline in key" {|{new\nline: 1}|}; 71 | (* lists *) 72 | parsing_should_succeed "empty list" "[]" (`List []); 73 | parsing_should_succeed "heterogenous list" {|[1, "2", 3.0]|} 74 | (`List [ `Int 1; `String "2"; `Float 3. ]); 75 | parsing_should_succeed "trailing comma in list" "[1, 2, 3,]" 76 | (`List [ `Int 1; `Int 2; `Int 3 ]); 77 | parsing_should_succeed "trailing comma with space list" "[1, 2, 3, ]" 78 | (`List [ `Int 1; `Int 2; `Int 3 ]); 79 | parsing_should_succeed "newlines in list" "[1, 2\n, 3]" 80 | (`List [ `Int 1; `Int 2; `Int 3 ]); 81 | parsing_should_fail "multiple trailing commas in list" "[1, 2, 3,,]"; 82 | parsing_should_fail "just trailing comma in list" "[,]"; 83 | parsing_should_fail "multiple trailing commas in list" "[,,,]"; 84 | (* all together *) 85 | (let expected = 86 | `Assoc 87 | [ 88 | ("unquoted", `String "and you can quote me on that"); 89 | ("singleQuotes", `String "I can use \"double quotes\" here"); 90 | ("lineBreaks", `String {|Look, Mom! No \n's!|}); 91 | ("hexadecimal", `Int 0xdecaf); 92 | ("leadingDecimalPoint", `Float 0.8675309); 93 | ("andTrailing", `Float 8675309.0); 94 | ("positiveSign", `Int 1); 95 | ("trailingComma", `String "in objects"); 96 | ("andIn", `List [ `String "arrays" ]); 97 | ("backwardsCompatible", `String "with JSON"); 98 | ] 99 | in 100 | parsing_should_succeed "More elaborated" 101 | {|{ 102 | // comments 103 | unquoted: 'and you can quote me on that', 104 | singleQuotes: 'I can use "double quotes" here', 105 | lineBreaks: "Look, Mom! \ 106 | No \\n's!", 107 | hexadecimal: 0xdecaf, 108 | leadingDecimalPoint: .8675309, andTrailing: 8675309., 109 | positiveSign: +1, 110 | trailingComma: 'in objects', andIn: ['arrays',], 111 | "backwardsCompatible": "with JSON", 112 | }|} 113 | expected); 114 | parsing_should_fail_with_error "unexpected EOF in list" "[1, 2," 115 | "Line 1: Unexpected end of input"; 116 | parsing_should_fail_with_error "unexpected EOF on different line" "\n[1, 2," 117 | "Line 2: Unexpected end of input"; 118 | parsing_should_fail_with_error "unexpected EOF in assoc" {|{"foo": 1,|} 119 | "Line 1: Unexpected end of input"; 120 | parsing_should_fail_with_error "missing colon in assoc" {|{"foo"}|} 121 | "Line 1: Expected ':' but found '}'"; 122 | parsing_should_fail_with_error "bad identifier in assoc" {|{[0]}|} 123 | "Line 1: Expected string or identifier but found '['"; 124 | ] 125 | 126 | let writing_test_case name input expected = 127 | Alcotest.test_case name `Quick (fun () -> 128 | Alcotest.(check string) name expected (M.to_string input)) 129 | 130 | let writing_tests = 131 | [ 132 | writing_test_case "Empty object" (`Assoc []) "{}"; 133 | writing_test_case "Empty list" (`List []) "[]"; 134 | writing_test_case "true" (`Bool true) "true"; 135 | writing_test_case "false" (`Bool false) "false"; 136 | writing_test_case "null" `Null "null"; 137 | writing_test_case "string" (`String "hello world") "\"hello world\""; 138 | writing_test_case "float" (`Float 12345.6789) "12345.6789"; 139 | writing_test_case "hex" (`Int 0x1) "1"; 140 | writing_test_case "int" (`Int 1) "1"; 141 | ] 142 | 143 | let () = 144 | Alcotest.run "JSON5" 145 | [ ("parsing", parsing_tests); ("writing", writing_tests) ] 146 | -------------------------------------------------------------------------------- /yojson-bench.opam: -------------------------------------------------------------------------------- 1 | # This file is generated by dune, edit dune-project instead 2 | opam-version: "2.0" 3 | synopsis: "Run Yojson benchmarks" 4 | description: """ 5 | Yojson benchmarks require `Core_bench` which is not a dependency of Yojson, 6 | because it is not part of the regular installation/testing flow. This is solely 7 | meant for developers that are worried about performance changes in Yojson.""" 8 | maintainer: [ 9 | "paul-elliot@tarides.com" "nathan@tarides.com" "marek@tarides.com" 10 | ] 11 | authors: ["Martin Jambon"] 12 | license: "BSD-3-Clause" 13 | homepage: "https://github.com/ocaml-community/yojson" 14 | bug-reports: "https://github.com/ocaml-community/yojson/issues" 15 | depends: [ 16 | "dune" {>= "2.7"} 17 | "ocaml" {>= "4.14"} 18 | "yojson" {= version} 19 | "benchmark" 20 | "odoc" {with-doc} 21 | ] 22 | build: [ 23 | ["dune" "subst"] {dev} 24 | [ 25 | "dune" 26 | "build" 27 | "-p" 28 | name 29 | "-j" 30 | jobs 31 | "@install" 32 | "@runtest" {with-test} 33 | "@doc" {with-doc} 34 | ] 35 | ] 36 | dev-repo: "git+https://github.com/ocaml-community/yojson.git" 37 | -------------------------------------------------------------------------------- /yojson-five.opam: -------------------------------------------------------------------------------- 1 | # This file is generated by dune, edit dune-project instead 2 | opam-version: "2.0" 3 | synopsis: 4 | "Yojson-five is a parsing and printing library for the JSON5 format" 5 | description: """ 6 | Yojson-five is a parsing and printing library for the JSON5 format. 7 | It supports parsing JSON5 to Yojson.Basic.t and Yojson.Safe.t types.""" 8 | maintainer: [ 9 | "paul-elliot@tarides.com" "nathan@tarides.com" "marek@tarides.com" 10 | ] 11 | authors: ["Martin Jambon"] 12 | license: "BSD-3-Clause" 13 | homepage: "https://github.com/ocaml-community/yojson" 14 | doc: "https://ocaml.org/p/yojson-five/latest" 15 | bug-reports: "https://github.com/ocaml-community/yojson/issues" 16 | depends: [ 17 | "dune" {>= "2.7"} 18 | "ocaml" {>= "4.08"} 19 | "sedlex" {>= "2.5"} 20 | "yojson" {= version} 21 | "alcotest" {with-test & >= "0.8.5"} 22 | "odoc" {with-doc} 23 | ] 24 | build: [ 25 | ["dune" "subst"] {dev} 26 | [ 27 | "dune" 28 | "build" 29 | "-p" 30 | name 31 | "-j" 32 | jobs 33 | "@install" 34 | "@runtest" {with-test} 35 | "@doc" {with-doc} 36 | ] 37 | ] 38 | dev-repo: "git+https://github.com/ocaml-community/yojson.git" 39 | -------------------------------------------------------------------------------- /yojson.opam: -------------------------------------------------------------------------------- 1 | # This file is generated by dune, edit dune-project instead 2 | opam-version: "2.0" 3 | synopsis: 4 | "Yojson is an optimized parsing and printing library for the JSON format" 5 | description: """ 6 | Yojson is an optimized parsing and printing library for the JSON format. 7 | 8 | ydump is a pretty-printing command-line program provided with the 9 | yojson package.""" 10 | maintainer: [ 11 | "paul-elliot@tarides.com" "nathan@tarides.com" "marek@tarides.com" 12 | ] 13 | authors: ["Martin Jambon"] 14 | license: "BSD-3-Clause" 15 | homepage: "https://github.com/ocaml-community/yojson" 16 | doc: "https://ocaml.org/p/yojson/latest" 17 | bug-reports: "https://github.com/ocaml-community/yojson/issues" 18 | depends: [ 19 | "dune" {>= "2.7"} 20 | "ocaml" {>= "4.08"} 21 | "alcotest" {with-test & >= "0.8.5"} 22 | "odoc" {with-doc} 23 | ] 24 | build: [ 25 | ["dune" "subst"] {dev} 26 | [ 27 | "dune" 28 | "build" 29 | "-p" 30 | name 31 | "-j" 32 | jobs 33 | "@install" 34 | "@runtest" {with-test} 35 | "@doc" {with-doc} 36 | ] 37 | ] 38 | dev-repo: "git+https://github.com/ocaml-community/yojson.git" 39 | --------------------------------------------------------------------------------