├── .bumpversion.cfg ├── .clang-format ├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ └── release.yml ├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.md ├── docs ├── Makefile ├── _static │ └── .keep ├── api.rst ├── conf.py ├── development.rst ├── index.rst └── make.bat ├── jsonexamples ├── apache_builds.json ├── canada.json ├── citm_catalog.json ├── github_events.json ├── gsoc-2018.json ├── instruments.json ├── invalid.json ├── marine_ik.json ├── mesh.json ├── mesh.pretty.json ├── numbers.json ├── random.json ├── small │ ├── adversarial.json │ ├── demo.json │ ├── flatadversarial.json │ ├── repeat.json │ ├── truenull.json │ └── twitter_timeline.json ├── test_parsing │ ├── LICENSE │ ├── 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 ├── test_transform │ ├── LICENSE │ ├── number_-9223372036854775808.json │ ├── number_-9223372036854775809.json │ ├── number_1.0.json │ ├── number_1.000000000000000005.json │ ├── number_1000000000000000.json │ ├── number_10000000000000000999.json │ ├── number_1e-999.json │ ├── number_1e6.json │ ├── number_9223372036854775807.json │ ├── number_9223372036854775808.json │ ├── object_key_nfc_nfd.json │ ├── object_key_nfd_nfc.json │ ├── object_same_key_different_values.json │ ├── object_same_key_same_value.json │ ├── object_same_key_unclear_values.json │ ├── string_1_escaped_invalid_codepoint.json │ ├── string_1_invalid_codepoint.json │ ├── string_2_escaped_invalid_codepoints.json │ ├── string_2_invalid_codepoints.json │ ├── string_3_escaped_invalid_codepoints.json │ ├── string_3_invalid_codepoints.json │ └── string_with_escaped_NULL.json ├── twitter.json ├── twitterescaped.json └── update-center.json ├── misc ├── logo.png └── logo_small.png ├── pyproject.toml ├── pytest.ini ├── tests ├── test_document.py ├── test_merge_patch.py ├── test_numbers.py ├── test_patch.py ├── test_serialization.py ├── test_shim.py └── tests.json └── yyjson ├── __init__.py ├── __init__.pyi ├── binding.c ├── decimal.h ├── document.c ├── document.h ├── memory.c ├── memory.h ├── yyjson.c └── yyjson.h /.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 4.0.5 3 | commit = True 4 | tag = True 5 | 6 | [bumpversion:file:setup.py] 7 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | AlignAfterOpenBracket: BlockIndent -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * linguist-vendored 2 | *.py linguist-vendored=false 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: TkTech 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with a single custom sponsorship URL 13 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | on: 2 | release: 3 | types: 4 | - published 5 | push: 6 | branches: 7 | - main 8 | pull_request: 9 | 10 | name: Create release 11 | 12 | jobs: 13 | # Build & test simple source release before wasting hours building and 14 | # testing the binary build matrix. 15 | sdist: 16 | name: Creating source release 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v4.1.7 20 | 21 | - name: Setting up Python 22 | uses: actions/setup-python@v5 23 | with: 24 | python-version: 3.13 25 | 26 | - name: Installing python build dependencies 27 | run: | 28 | python -m pip install --upgrade pip 29 | python -m pip install --upgrade setuptools build 30 | 31 | - name: Building source distribution 32 | run: | 33 | pip install -e ".[test,release]" 34 | python -m build . --sdist 35 | 36 | - name: Running tests 37 | run: | 38 | pytest 39 | 40 | - name: Ensuring documentation builds 41 | run: | 42 | cd docs && make clean && make html 43 | 44 | - uses: actions/upload-artifact@v4.6.0 45 | with: 46 | name: dist-sdist 47 | path: dist/*.tar.gz 48 | 49 | build_wheels: 50 | needs: [sdist] 51 | name: "[${{ strategy.job-index }}/${{ strategy.job-total }}] py${{ matrix.py }} on ${{ matrix.os }}" 52 | runs-on: ${{ matrix.os }} 53 | strategy: 54 | fail-fast: true 55 | matrix: 56 | os: [ubuntu-latest, windows-latest, macos-14] 57 | py: ["cp39", "cp310", "cp311", "cp312", "cp313", "pp39", "pp310"] 58 | 59 | steps: 60 | - uses: actions/checkout@v4.1.7 61 | 62 | - uses: actions/setup-python@v5 63 | name: Setting up Python 64 | with: 65 | python-version: '3.13' 66 | 67 | - name: Set up QEMU 68 | if: runner.os == 'Linux' 69 | uses: docker/setup-qemu-action@v3 70 | with: 71 | platforms: all 72 | 73 | - name: Build & test wheels 74 | uses: pypa/cibuildwheel@v2.22.0 75 | env: 76 | CIBW_ARCHS_LINUX: auto aarch64 ppc64le s390x 77 | CIBW_ARCHS_MACOS: x86_64 arm64 universal2 78 | CIBW_BUILD: "${{ matrix.py }}-*" 79 | CIBW_TEST_SKIP: "*_arm64 *_universal2:arm64" 80 | 81 | - uses: actions/upload-artifact@v4.6.0 82 | with: 83 | name: dist-${{ matrix.os }}-${{ matrix.py }} 84 | path: ./wheelhouse/*.whl 85 | 86 | upload_all: 87 | needs: [build_wheels, sdist] 88 | name: Uploading built packages to pypi for release. 89 | runs-on: ubuntu-latest 90 | if: github.event_name == 'release' 91 | steps: 92 | - uses: actions/download-artifact@v4.1.8 93 | with: 94 | pattern: dist-* 95 | merge-multiple: true 96 | path: dist 97 | - uses: pypa/gh-action-pypi-publish@v1.4.2 98 | with: 99 | user: ${{ secrets.PYPI_USERNAME }} 100 | password: ${{ secrets.PYPI_PASSWORD }} 101 | 102 | build_documentation: 103 | name: Building & uploading documentation. 104 | needs: [upload_all] 105 | runs-on: ubuntu-latest 106 | if: github.event_name == 'release' 107 | steps: 108 | - uses: actions/checkout@v3 109 | 110 | - name: Setting up Python 111 | uses: actions/setup-python@v4 112 | with: 113 | python-version: 3.13 114 | 115 | - name: Installing python build dependencies 116 | run: | 117 | python -m pip install --upgrade pip 118 | python -m pip install --upgrade setuptools build 119 | 120 | - name: Installing release dependencies. 121 | run: | 122 | pip install -e ".[test,release]" 123 | 124 | - name: Building documentation 125 | run: | 126 | cd docs && make clean && make html 127 | 128 | - name: Publishing documentation 129 | run: | 130 | ghp-import -f -n -p docs/_build/html -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.pyd 3 | venv 4 | .eggs 5 | cython_debug/* 6 | build/ 7 | _build/ 8 | *.egg-info 9 | *.so 10 | dist/ 11 | .idea 12 | .vscode/settings.json 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Tyler Kennedy 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | 21 | ======== 22 | 23 | yyjson.c and yyjson.h are used under the following license: 24 | 25 | MIT License 26 | 27 | Copyright (c) 2020 YaoYuan 28 | 29 | Permission is hereby granted, free of charge, to any person obtaining a copy 30 | of this software and associated documentation files (the "Software"), to deal 31 | in the Software without restriction, including without limitation the rights 32 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 33 | copies of the Software, and to permit persons to whom the Software is 34 | furnished to do so, subject to the following conditions: 35 | 36 | The above copyright notice and this permission notice shall be included in all 37 | copies or substantial portions of the Software. 38 | 39 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 40 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 41 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 42 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 43 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 44 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 45 | SOFTWARE. 46 | 47 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include yyjson/binding.c 2 | include yyjson/yyjson.h 3 | include yyjson/yyjson.c 4 | include yyjson/memory.c 5 | include yyjson/memory.h 6 | include yyjson/document.c 7 | include yyjson/document.h 8 | include yyjson/decimal.h -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![PyPI - License](https://img.shields.io/pypi/l/yyjson.svg?style=flat-square) 2 | ![Tests](https://github.com/TkTech/py_yyjson/workflows/Run%20tests/badge.svg) 3 | 4 | # py_yyjson 5 | 6 | ![py_yyjson Logo](misc/logo_small.png) 7 | 8 | Fast and flexible Python JSON parsing built on the excellent [yyjson][] 9 | project. 10 | 11 | ![GitHub Sponsors](https://img.shields.io/github/sponsors/tktech) 12 | ![PyPI - License](https://img.shields.io/pypi/l/yyjson) 13 | ![PyPI - Version](https://img.shields.io/pypi/v/yyjson) 14 | 15 | - **Fast**: `yyjson` is several times faster than the builtin JSON module, and 16 | is [faster than most other JSON libraries][fast]. 17 | - **Flexible**: Parse JSON with strict specification compliance, or with 18 | extensions such as comments, trailing commas, Inf/NaN, numbers of any size, 19 | and more. 20 | - **Lightweight**: `yyjson` is a lightweight project dependency with low 21 | maintenance overhead. It's written in C, and has no dependencies other than 22 | a C89 compiler. Built wheels are between 50kb and 800kb depending on the 23 | platform. 24 | - **Portable**: Binary wheels are available for many versions of Python 25 | on many architectures, such as x86, x86_64, ARM, and ARM64, PowerPC, IBM Z, 26 | and more. PyPy is also supported. Supports Python 3.9 and newer. 27 | - **Manipulate documents**: The fastest JSON Merge-Patch (RFC 7386), JSON Patch 28 | (RFC 6902), and JSON Pointer (RFC 6901) implementations available for Python 29 | allow you to manipulate JSON documents without deserializing them into Python 30 | objects. 31 | - **Traceable**: `yyjson` uses Python's memory allocator by default, so you can 32 | trace memory leaks and other memory issues using Python's built-in tools. 33 | 34 | ## Documentation 35 | 36 | Find the latest documentation at https://tkte.ch/py_yyjson. 37 | 38 | [yyjson]: https://github.com/ibireme/yyjson 39 | [fast]: https://github.com/tktech/json_benchmark -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/_static/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/docs/_static/.keep -------------------------------------------------------------------------------- /docs/api.rst: -------------------------------------------------------------------------------- 1 | API 2 | --- 3 | 4 | .. note:: 5 | 6 | When creating and manipulating a :class:`Document`, it's important to keep 7 | in mind how the underlying yyjson library manages memory. If you modify a 8 | Document, such as removing or replacing a value, the memory for that value 9 | is not freed until the Document is destroyed. A Document isn't meant to be 10 | kept around and constantly modified, so don't use it as, say, a database. 11 | 12 | 13 | .. testsetup:: * 14 | 15 | from yyjson import Document, ReaderFlags, WriterFlags 16 | 17 | .. automodule:: yyjson 18 | :members: 19 | :undoc-members: 20 | :show-inheritance: 21 | 22 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | # import os 14 | # import sys 15 | # sys.path.insert(0, '/home/tktech/projects/py_yyjson/yyjson') 16 | 17 | 18 | # -- Project information ----------------------------------------------------- 19 | 20 | project = "py_yyjson" 21 | copyright = "2022, Tyler Kennedy " 22 | author = "Tyler Kennedy" 23 | 24 | 25 | # -- General configuration --------------------------------------------------- 26 | 27 | # Add any Sphinx extension module names here, as strings. They can be 28 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 29 | # ones. 30 | extensions = [ 31 | "sphinx.ext.autodoc", 32 | "sphinx.ext.viewcode", 33 | "sphinx.ext.todo", 34 | "sphinx.ext.doctest", 35 | "sphinx_copybutton", 36 | ] 37 | 38 | # Add any paths that contain templates here, relative to this directory. 39 | templates_path = ["_templates"] 40 | 41 | # The language for content autogenerated by Sphinx. Refer to documentation 42 | # for a list of supported languages. 43 | # 44 | # This is also used if you do content translation via gettext catalogs. 45 | # Usually you set "language" from the command line for these cases. 46 | language = "en" 47 | 48 | # List of patterns, relative to source directory, that match files and 49 | # directories to ignore when looking for source files. 50 | # This pattern also affects html_static_path and html_extra_path. 51 | exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] 52 | 53 | 54 | # -- Options for HTML output ------------------------------------------------- 55 | 56 | # The theme to use for HTML and HTML Help pages. See the documentation for 57 | # a list of builtin themes. 58 | # 59 | html_theme = "furo" 60 | html_title = "[py]yyjson" 61 | html_logo = "../misc/logo_small.png" 62 | 63 | # Add any paths that contain custom static files (such as style sheets) here, 64 | # relative to this directory. They are copied after the builtin static files, 65 | # so a file named "default.css" will overwrite the builtin "default.css". 66 | html_static_path = ["_static"] 67 | 68 | html_theme_options = {} 69 | 70 | 71 | # -- Extension configuration ------------------------------------------------- 72 | 73 | # -- Options for todo extension ---------------------------------------------- 74 | 75 | # If true, `todo` and `todoList` produce output, else they produce nothing. 76 | todo_include_todos = True 77 | -------------------------------------------------------------------------------- /docs/development.rst: -------------------------------------------------------------------------------- 1 | Development 2 | =========== 3 | 4 | To install development requirements, use: 5 | 6 | pip install -e ".[test]" 7 | 8 | You can then build the project using ``python setup.py develop``. Keep in 9 | mind since this is a C extension, you'll need a C compiler installed. On 10 | Windows, you can use Visual Studio Community Edition. On Linux, you can 11 | use GCC or Clang. You'll also need to re-run it each time you make a 12 | change to a ``.c`` or ``.h`` file to recompile. 13 | 14 | To run the tests, just type ``pytest``. To prepare for a release or to rebuild 15 | documentation, you need a few extra dependencies: 16 | 17 | pip install -e ".[release]" 18 | 19 | You can then rebuild the documentation by running ``make html`` within the 20 | ``docs/`` directory. 21 | 22 | 23 | Why not Poetry? 24 | --------------- 25 | 26 | Poetry is a great tool, but it's not a good fit for this project. Poetry 27 | is designed to manage Python projects, and this project is a C extension 28 | with a Python wrapper. Poetry's current support for building C extensions 29 | is a hack which may change at any time. -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | [py]yyjson 2 | ========== 3 | 4 | .. toctree:: 5 | :hidden: 6 | 7 | api.rst 8 | development.rst 9 | 10 | Fast and flexible Python JSON parsing built on the excellent `yyjson`_ library. 11 | 12 | .. image:: https://img.shields.io/github/sponsors/tktech 13 | :alt: GitHub Sponsors 14 | 15 | .. image:: https://img.shields.io/pypi/l/yyjson 16 | :alt: PyPI - License 17 | 18 | .. image:: https://img.shields.io/pypi/v/yyjson 19 | :alt: PyPI - Version 20 | 21 | 22 | Features 23 | -------- 24 | 25 | - **Fast**: `yyjson` is several times faster than the builtin JSON module, and 26 | is `faster than most other JSON libraries `_. 27 | - **Flexible**: Parse JSON with strict specification compliance, or with 28 | extensions such as comments, trailing commas, Inf/NaN, numbers of any size, 29 | and more. 30 | - **Lightweight**: `yyjson` is a lightweight project dependency with low 31 | maintenance overhead. It's written in C, and has no dependencies other than 32 | a C89 compiler. Built wheels are between 50kb and 800kb depending on the 33 | platform. 34 | - **Portable**: Binary wheels are available for many versions of Python 35 | on many architectures, such as x86, x86_64, ARM, and ARM64, PowerPC, IBM Z, 36 | and more. PyPy is also supported. Supports Python 3.9 and newer. 37 | - **Manipulate documents**: The fastest JSON Merge-Patch (RFC 7386), JSON Patch 38 | (RFC 6902), and JSON Pointer (RFC 6901) implementations available for Python 39 | allow you to manipulate JSON documents without deserializing them into Python 40 | objects. 41 | - **Traceable**: `yyjson` uses Python's memory allocator by default, so you can 42 | trace memory leaks and other memory issues using Python's built-in tools. 43 | 44 | 45 | Installation 46 | ------------ 47 | 48 | If binary wheels are available for your platform, you can install the latest 49 | version of yyjson with pip: 50 | 51 | pip install yyjson 52 | 53 | If you want to build from source, or if binary wheels aren't available, you'll 54 | just need a C89 compiler, such as GCC or Clang. 55 | 56 | Or you can install the latest development version from GitHub: 57 | 58 | pip install git+https://github.com/tktech/py_yyjson.git 59 | 60 | 61 | Benchmarks 62 | ---------- 63 | 64 | You can find the benchmarks for this project in its sister project, 65 | `json_benchmark`_. yyjson compares favourably, and often beats, most other 66 | libraries. 67 | 68 | Examples 69 | -------- 70 | 71 | Parsing 72 | ^^^^^^^ 73 | 74 | Parse a JSON document from a file: 75 | 76 | .. code-block:: python 77 | 78 | >>> from pathlib import Path 79 | >>> from yyjson import Document 80 | >>> Document(Path("canada.json")).as_obj 81 | {'type': 'FeatureCollection', 'features': [...], 'bbox': [...], 'crs': {...}} 82 | 83 | 84 | Parse a JSON document from a string: 85 | 86 | .. code-block:: python 87 | 88 | >>> from yyjson import Document 89 | >>> Document('{"hello": "world"}').as_obj 90 | {'hello': 'world'} 91 | 92 | Parse a JSON document from a bytes object: 93 | 94 | .. code-block:: python 95 | 96 | >>> from yyjson import Document 97 | >>> Document(b'{"hello": "world"}').as_obj 98 | {'hello': 'world'} 99 | 100 | 101 | Load part of a document 102 | ^^^^^^^^^^^^^^^^^^^^^^^ 103 | 104 | When you only need a small part of a document, you can use a JSON Pointer to 105 | extract just the part you actually need. This can be a massive performance 106 | improvement when working with large JSON documents, as most of the time 107 | spent parsing JSON in Python is spent just creating the Python objects! 108 | 109 | .. code-block:: python 110 | 111 | >> from pathlib import Path 112 | >> from yyjson import Document 113 | >>> doc = Document(Path("canada.json")) 114 | >>> features = doc.get_pointer("/features") 115 | 116 | 117 | Patch a document 118 | ^^^^^^^^^^^^^^^^ 119 | 120 | JSON manipulation operations are supported, such as 121 | `JSON Merge-Patch `_ and 122 | `JSON Patch `_. These operations 123 | allow you to manipulate JSON documents without deserializing them into 124 | Python objects at all. 125 | 126 | For example, lets add an entry to a GeoJSON file without deserializing 127 | the entire document into Python objects using JSON Patch: 128 | 129 | .. code-block:: python 130 | 131 | from pathlib import Path 132 | from yyjson import Document 133 | 134 | doc = Document(Path("canada.json") 135 | patch = Document([ 136 | {'op': 'add', 'path': '/features/-', 'value': { 137 | 'type': 'Feature', 138 | 'geometry': { 139 | 'type': 'Point', 140 | 'coordinates': [1, 2], 141 | }, 142 | 'properties': { 143 | 'name': 'New Feature', 144 | }, 145 | }}, 146 | ]) 147 | modified = doc.patch(patch) 148 | print(modified.dumps()) 149 | 150 | 151 | Serialize an object 152 | ^^^^^^^^^^^^^^^^^^^ 153 | 154 | Serialize a Python object to JSON: 155 | 156 | .. code-block:: python 157 | 158 | >>> from yyjson import Document 159 | >>> doc = Document({ 160 | ... "hello": "world", 161 | ... "foo": [1, 2, 3], 162 | ... "bar": {"a": 1, "b": 2} 163 | ... }) 164 | >>> doc.dumps() 165 | '{"hello":"world","foo":[1,2,3],"bar":{"a":1,"b":2}}' 166 | 167 | 168 | Customizing JSON Reading & Writing 169 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 170 | 171 | You can customize the JSON reading and writing process using 172 | :class:`yyjson.ReaderFlags` and :class:`yyjson.WriterFlags`. For example 173 | if we wanted to allow comments and trailing commas, we could do: 174 | 175 | 176 | .. code-block:: python 177 | 178 | >>> from yyjson import Document, ReaderFlags, WriterFlags 179 | >>> doc = Document( 180 | ... '{"hello": "world",} // This is a comment', 181 | ... ReaderFlags.ALLOW_COMMENTS | ReaderFlags.ALLOW_TRAILING_COMMAS 182 | ... ) 183 | 184 | 185 | Likewise we can customize the writing process: 186 | 187 | 188 | .. code-block:: python 189 | 190 | >>> from yyjson import Document, ReaderFlags, WriterFlags 191 | >>> doc = Document({ 192 | ... "hello": "world" 193 | ... }) 194 | >>> doc.dumps(flags=WriterFlags.PRETTY_TWO_SPACES) 195 | 196 | 197 | Reading Huge Numbers 198 | ^^^^^^^^^^^^^^^^^^^^ 199 | 200 | If you're reading huge floats/doubles or require perfect precision, you can 201 | tell yyjson to read them as Decimals: 202 | 203 | .. code-block:: python 204 | 205 | >>> from yyjson import Document, ReaderFlags 206 | >>> float('1.7976931348623157e+310') 207 | inf 208 | >>> doc = Document( 209 | ... '{"huge": 1.7976931348623157e+310}', 210 | ... flags=ReaderFlags.NUMBERS_AS_DECIMAL 211 | ... ) 212 | >>> print(doc.get_pointer('/huge')) 213 | 1.7976931348623157E+310 214 | 215 | 216 | Or use ``ReaderFlags.BIG_NUMBERS_AS_DECIMAL`` to only read numbers that are 217 | too large for Python's float type as Decimals: 218 | 219 | .. code-block:: python 220 | 221 | >>> from yyjson import Document, ReaderFlags 222 | >>> doc = Document( 223 | '{"huge": 1.7976931348623157e+310, "small": 1.0}', 224 | flags=ReaderFlags.BIG_NUMBERS_AS_DECIMAL 225 | ) 226 | >>> type(doc.get_pointer('/huge')) 227 | 228 | >>> type(doc.get_pointer('/small')) 229 | 230 | 231 | 232 | .. _yyjson: https://github.com/ibireme/yyjson 233 | .. _json_benchmark: https://github.com/tktech/json_benchmark -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | %SPHINXBUILD% >NUL 2>NUL 14 | if errorlevel 9009 ( 15 | echo. 16 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 17 | echo.installed, then set the SPHINXBUILD environment variable to point 18 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 19 | echo.may add the Sphinx directory to PATH. 20 | echo. 21 | echo.If you don't have Sphinx installed, grab it from 22 | echo.https://www.sphinx-doc.org/ 23 | exit /b 1 24 | ) 25 | 26 | if "%1" == "" goto help 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /jsonexamples/invalid.json: -------------------------------------------------------------------------------- 1 | { 2 | -------------------------------------------------------------------------------- /jsonexamples/small/adversarial.json: -------------------------------------------------------------------------------- 1 | { 2 | "\"Name rue": [ 3 | [ 116, 4 | "\"", 5 | 234, 6 | "true", 7 | false ] 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /jsonexamples/small/demo.json: -------------------------------------------------------------------------------- 1 | { 2 | "Image": { 3 | "Width": 800, 4 | "Height": 600, 5 | "Title": "View from 15th Floor", 6 | "Thumbnail": { 7 | "Url": "http://www.example.com/image/481989943", 8 | "Height": 125, 9 | "Width": 100 10 | }, 11 | "Animated" : false, 12 | "IDs": [116, 943, 234, 38793] 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /jsonexamples/small/flatadversarial.json: -------------------------------------------------------------------------------- 1 | { "\"Name": [ 116,"\\\"", 234, "true", false ], "t": 1.0e+10} 2 | -------------------------------------------------------------------------------- /jsonexamples/small/repeat.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 1, 3 | "jsonrpc": "2.0", 4 | "total": 100, 5 | "result": [ 6 | { 7 | "id": 1, 8 | "name": "Юрий Титов" 9 | }, 10 | { 11 | "id": 2, 12 | "name": "Валерий Васильев" 13 | }, 14 | { 15 | "id": 3, 16 | "name": "Парамон Белов" 17 | }, 18 | { 19 | "id": 4, 20 | "name": "Александр Иванов" 21 | }, 22 | { 23 | "id": 5, 24 | "name": "Дмитрий Фролов" 25 | }, 26 | { 27 | "id": 6, 28 | "name": "Яков Олейник" 29 | }, 30 | { 31 | "id": 7, 32 | "name": "Леонтий Зайцев" 33 | }, 34 | { 35 | "id": 8, 36 | "name": "Олег Егоров" 37 | }, 38 | { 39 | "id": 9, 40 | "name": "Леонтий Зайцев" 41 | }, 42 | { 43 | "id": 10, 44 | "name": "Федор Никитин" 45 | }, 46 | { 47 | "id": 11, 48 | "name": "Александр Иванов" 49 | }, 50 | { 51 | "id": 12, 52 | "name": "Рудольф Логинов" 53 | }, 54 | { 55 | "id": 13, 56 | "name": "Анатолий Бондаренко" 57 | }, 58 | { 59 | "id": 14, 60 | "name": "Константин Павлов" 61 | }, 62 | { 63 | "id": 15, 64 | "name": "Ефим Марченко" 65 | }, 66 | { 67 | "id": 16, 68 | "name": "Николай Макаров" 69 | }, 70 | { 71 | "id": 17, 72 | "name": "Мирослав Фролов" 73 | }, 74 | { 75 | "id": 18, 76 | "name": "Гордей Соколов" 77 | }, 78 | { 79 | "id": 19, 80 | "name": "Арсен Попов" 81 | }, 82 | { 83 | "id": 20, 84 | "name": "Павел Степанов" 85 | }, 86 | { 87 | "id": 21, 88 | "name": "Степан Баранов" 89 | }, 90 | { 91 | "id": 22, 92 | "name": "Захар Новиков" 93 | }, 94 | { 95 | "id": 23, 96 | "name": "Казимир Макаров" 97 | }, 98 | { 99 | "id": 24, 100 | "name": "Оскар Данилов" 101 | }, 102 | { 103 | "id": 25, 104 | "name": "Любомир Денисов" 105 | }, 106 | { 107 | "id": 26, 108 | "name": "Карл Рябов" 109 | }, 110 | { 111 | "id": 27, 112 | "name": "Владимир Смирнов" 113 | }, 114 | { 115 | "id": 28, 116 | "name": "Степан Баранов" 117 | }, 118 | { 119 | "id": 29, 120 | "name": "Карл Рябов" 121 | }, 122 | { 123 | "id": 30, 124 | "name": "Иоан Орлов" 125 | }, 126 | { 127 | "id": 31, 128 | "name": "Максим Степанов" 129 | }, 130 | { 131 | "id": 32, 132 | "name": "Арсений Кузнецов" 133 | }, 134 | { 135 | "id": 33, 136 | "name": "Борис Левченко" 137 | }, 138 | { 139 | "id": 34, 140 | "name": "Захар Новиков" 141 | }, 142 | { 143 | "id": 35, 144 | "name": "Мирослав Фролов" 145 | }, 146 | { 147 | "id": 36, 148 | "name": "Евдоким Демченко" 149 | }, 150 | { 151 | "id": 37, 152 | "name": "Осип Григорьев" 153 | }, 154 | { 155 | "id": 38, 156 | "name": "Станислав Тарасов" 157 | }, 158 | { 159 | "id": 39, 160 | "name": "Вениамин Нестеренко" 161 | }, 162 | { 163 | "id": 40, 164 | "name": "Эдуард Лебедев" 165 | }, 166 | { 167 | "id": 41, 168 | "name": "Гавриил Мельник" 169 | }, 170 | { 171 | "id": 42, 172 | "name": "Мартин Козлов" 173 | }, 174 | { 175 | "id": 43, 176 | "name": "Валентин Шевченко" 177 | }, 178 | { 179 | "id": 44, 180 | "name": "Артем Смирнов" 181 | }, 182 | { 183 | "id": 45, 184 | "name": "Федор Никитин" 185 | }, 186 | { 187 | "id": 46, 188 | "name": "Никита Яковлев" 189 | }, 190 | { 191 | "id": 47, 192 | "name": "Андрей Петров" 193 | }, 194 | { 195 | "id": 48, 196 | "name": "Гарри Морозов" 197 | }, 198 | { 199 | "id": 49, 200 | "name": "Егор Мальцев" 201 | }, 202 | { 203 | "id": 50, 204 | "name": "Иоан Орлов" 205 | }, 206 | { 207 | "id": 51, 208 | "name": "Арсен Попов" 209 | }, 210 | { 211 | "id": 52, 212 | "name": "Казимир Макаров" 213 | }, 214 | { 215 | "id": 53, 216 | "name": "Герман Морозов" 217 | }, 218 | { 219 | "id": 54, 220 | "name": "Тарас Алексеев" 221 | }, 222 | { 223 | "id": 55, 224 | "name": "Семен Козлов" 225 | }, 226 | { 227 | "id": 56, 228 | "name": "Любомир Денисов" 229 | }, 230 | { 231 | "id": 57, 232 | "name": "Даниил Соколов" 233 | }, 234 | { 235 | "id": 58, 236 | "name": "Олег Егоров" 237 | }, 238 | { 239 | "id": 59, 240 | "name": "Леонид Приходько" 241 | }, 242 | { 243 | "id": 60, 244 | "name": "Андрей Петров" 245 | }, 246 | { 247 | "id": 61, 248 | "name": "Георгий Василенко" 249 | }, 250 | { 251 | "id": 62, 252 | "name": "Лазарь Михайлов" 253 | }, 254 | { 255 | "id": 63, 256 | "name": "Станислав Тарасов" 257 | }, 258 | { 259 | "id": 64, 260 | "name": "Денис Данилов" 261 | }, 262 | { 263 | "id": 65, 264 | "name": "Степан Баранов" 265 | }, 266 | { 267 | "id": 66, 268 | "name": "Денис Данилов" 269 | }, 270 | { 271 | "id": 67, 272 | "name": "Станислав Тарасов" 273 | }, 274 | { 275 | "id": 68, 276 | "name": "Аркадий Кравченко" 277 | }, 278 | { 279 | "id": 69, 280 | "name": "Рубен Сорокин" 281 | }, 282 | { 283 | "id": 70, 284 | "name": "Мартын Гусев" 285 | }, 286 | { 287 | "id": 71, 288 | "name": "Федор Никитин" 289 | }, 290 | { 291 | "id": 72, 292 | "name": "Борис Левченко" 293 | }, 294 | { 295 | "id": 73, 296 | "name": "Гарри Морозов" 297 | }, 298 | { 299 | "id": 74, 300 | "name": "Иоан Орлов" 301 | }, 302 | { 303 | "id": 75, 304 | "name": "Максим Степанов" 305 | }, 306 | { 307 | "id": 76, 308 | "name": "Рудольф Логинов" 309 | }, 310 | { 311 | "id": 77, 312 | "name": "Роман Чернов" 313 | }, 314 | { 315 | "id": 78, 316 | "name": "Филипп Литвинов" 317 | }, 318 | { 319 | "id": 79, 320 | "name": "Гордей Соколов" 321 | }, 322 | { 323 | "id": 80, 324 | "name": "Леонтий Зайцев" 325 | }, 326 | { 327 | "id": 81, 328 | "name": "Донат Михайлов" 329 | }, 330 | { 331 | "id": 82, 332 | "name": "Александр Иванов" 333 | }, 334 | { 335 | "id": 83, 336 | "name": "Арсен Попов" 337 | }, 338 | { 339 | "id": 84, 340 | "name": "Валерий Васильев" 341 | }, 342 | { 343 | "id": 85, 344 | "name": "Никита Яковлев" 345 | }, 346 | { 347 | "id": 86, 348 | "name": "Гарри Морозов" 349 | }, 350 | { 351 | "id": 87, 352 | "name": "Любомир Денисов" 353 | }, 354 | { 355 | "id": 88, 356 | "name": "Евгений Новиков" 357 | }, 358 | { 359 | "id": 89, 360 | "name": "Степан Баранов" 361 | }, 362 | { 363 | "id": 90, 364 | "name": "Карл Рябов" 365 | }, 366 | { 367 | "id": 91, 368 | "name": "Владислав Бабич" 369 | }, 370 | { 371 | "id": 92, 372 | "name": "Филипп Литвинов" 373 | }, 374 | { 375 | "id": 93, 376 | "name": "Эдвард Давыдов" 377 | }, 378 | { 379 | "id": 94, 380 | "name": "Арнольд Бойко" 381 | }, 382 | { 383 | "id": 95, 384 | "name": "Оскар Данилов" 385 | }, 386 | { 387 | "id": 96, 388 | "name": "Семен Козлов" 389 | }, 390 | { 391 | "id": 97, 392 | "name": "Тарас Алексеев" 393 | }, 394 | { 395 | "id": 98, 396 | "name": "Людвиг Сергеев" 397 | }, 398 | { 399 | "id": 99, 400 | "name": "Роман Чернов" 401 | }, 402 | { 403 | "id": 100, 404 | "name": "Игнат Волков" 405 | } 406 | ] 407 | } -------------------------------------------------------------------------------- /jsonexamples/small/truenull.json: -------------------------------------------------------------------------------- 1 | [true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Nicolas Seriot 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_number_double_huge_neg_exp.json: -------------------------------------------------------------------------------- 1 | [123.456e-789] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_number_huge_exp.json: -------------------------------------------------------------------------------- 1 | [0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_number_neg_int_huge_exp.json: -------------------------------------------------------------------------------- 1 | [-1e+9999] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_number_pos_double_huge_exp.json: -------------------------------------------------------------------------------- 1 | [1.5e+9999] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_number_real_neg_overflow.json: -------------------------------------------------------------------------------- 1 | [-123123e100000] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_number_real_pos_overflow.json: -------------------------------------------------------------------------------- 1 | [123123e100000] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_number_real_underflow.json: -------------------------------------------------------------------------------- 1 | [123e-10000000] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_number_too_big_neg_int.json: -------------------------------------------------------------------------------- 1 | [-123123123123123123123123123123] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_number_too_big_pos_int.json: -------------------------------------------------------------------------------- 1 | [100000000000000000000] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_number_very_big_negative_int.json: -------------------------------------------------------------------------------- 1 | [-237462374673276894279832749832423479823246327846] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_object_key_lone_2nd_surrogate.json: -------------------------------------------------------------------------------- 1 | {"\uDFAA":0} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_string_1st_surrogate_but_2nd_missing.json: -------------------------------------------------------------------------------- 1 | ["\uDADA"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_string_1st_valid_surrogate_2nd_invalid.json: -------------------------------------------------------------------------------- 1 | ["\uD888\u1234"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_string_UTF-16LE_with_BOM.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/jsonexamples/test_parsing/i_string_UTF-16LE_with_BOM.json -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_string_UTF-8_invalid_sequence.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/jsonexamples/test_parsing/i_string_UTF-8_invalid_sequence.json -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_string_UTF8_surrogate_U+D800.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/jsonexamples/test_parsing/i_string_UTF8_surrogate_U+D800.json -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_string_incomplete_surrogate_and_escape_valid.json: -------------------------------------------------------------------------------- 1 | ["\uD800\n"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_string_incomplete_surrogate_pair.json: -------------------------------------------------------------------------------- 1 | ["\uDd1ea"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_string_incomplete_surrogates_escape_valid.json: -------------------------------------------------------------------------------- 1 | ["\uD800\uD800\n"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_string_invalid_lonely_surrogate.json: -------------------------------------------------------------------------------- 1 | ["\ud800"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_string_invalid_surrogate.json: -------------------------------------------------------------------------------- 1 | ["\ud800abc"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_string_invalid_utf-8.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/jsonexamples/test_parsing/i_string_invalid_utf-8.json -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_string_inverted_surrogates_U+1D11E.json: -------------------------------------------------------------------------------- 1 | ["\uDd1e\uD834"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_string_iso_latin_1.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/jsonexamples/test_parsing/i_string_iso_latin_1.json -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_string_lone_second_surrogate.json: -------------------------------------------------------------------------------- 1 | ["\uDFAA"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_string_lone_utf8_continuation_byte.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/jsonexamples/test_parsing/i_string_lone_utf8_continuation_byte.json -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_string_not_in_unicode_range.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/jsonexamples/test_parsing/i_string_not_in_unicode_range.json -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_string_overlong_sequence_2_bytes.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/jsonexamples/test_parsing/i_string_overlong_sequence_2_bytes.json -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_string_overlong_sequence_6_bytes.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/jsonexamples/test_parsing/i_string_overlong_sequence_6_bytes.json -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_string_overlong_sequence_6_bytes_null.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/jsonexamples/test_parsing/i_string_overlong_sequence_6_bytes_null.json -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_string_truncated-utf-8.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/jsonexamples/test_parsing/i_string_truncated-utf-8.json -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_string_utf16BE_no_BOM.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/jsonexamples/test_parsing/i_string_utf16BE_no_BOM.json -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_string_utf16LE_no_BOM.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/jsonexamples/test_parsing/i_string_utf16LE_no_BOM.json -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_structure_500_nested_arrays.json: -------------------------------------------------------------------------------- 1 | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/i_structure_UTF-8_BOM_empty_object.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_array_1_true_without_comma.json: -------------------------------------------------------------------------------- 1 | [1 true] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_array_a_invalid_utf8.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/jsonexamples/test_parsing/n_array_a_invalid_utf8.json -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_array_colon_instead_of_comma.json: -------------------------------------------------------------------------------- 1 | ["": 1] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_array_comma_after_close.json: -------------------------------------------------------------------------------- 1 | [""], -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_array_comma_and_number.json: -------------------------------------------------------------------------------- 1 | [,1] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_array_double_comma.json: -------------------------------------------------------------------------------- 1 | [1,,2] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_array_double_extra_comma.json: -------------------------------------------------------------------------------- 1 | ["x",,] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_array_extra_close.json: -------------------------------------------------------------------------------- 1 | ["x"]] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_array_extra_comma.json: -------------------------------------------------------------------------------- 1 | ["",] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_array_incomplete.json: -------------------------------------------------------------------------------- 1 | ["x" -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_array_incomplete_invalid_value.json: -------------------------------------------------------------------------------- 1 | [x -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_array_inner_array_no_comma.json: -------------------------------------------------------------------------------- 1 | [3[4]] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_array_invalid_utf8.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/jsonexamples/test_parsing/n_array_invalid_utf8.json -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_array_items_separated_by_semicolon.json: -------------------------------------------------------------------------------- 1 | [1:2] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_array_just_comma.json: -------------------------------------------------------------------------------- 1 | [,] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_array_just_minus.json: -------------------------------------------------------------------------------- 1 | [-] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_array_missing_value.json: -------------------------------------------------------------------------------- 1 | [ , ""] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_array_newlines_unclosed.json: -------------------------------------------------------------------------------- 1 | ["a", 2 | 4 3 | ,1, -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_array_number_and_comma.json: -------------------------------------------------------------------------------- 1 | [1,] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_array_number_and_several_commas.json: -------------------------------------------------------------------------------- 1 | [1,,] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_array_spaces_vertical_tab_formfeed.json: -------------------------------------------------------------------------------- 1 | [" a"\f] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_array_star_inside.json: -------------------------------------------------------------------------------- 1 | [*] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_array_unclosed.json: -------------------------------------------------------------------------------- 1 | ["" -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_array_unclosed_trailing_comma.json: -------------------------------------------------------------------------------- 1 | [1, -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_array_unclosed_with_new_lines.json: -------------------------------------------------------------------------------- 1 | [1, 2 | 1 3 | ,1 -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_array_unclosed_with_object_inside.json: -------------------------------------------------------------------------------- 1 | [{} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_incomplete_false.json: -------------------------------------------------------------------------------- 1 | [fals] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_incomplete_null.json: -------------------------------------------------------------------------------- 1 | [nul] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_incomplete_true.json: -------------------------------------------------------------------------------- 1 | [tru] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_multidigit_number_then_00.json: -------------------------------------------------------------------------------- 1 | 123 -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_++.json: -------------------------------------------------------------------------------- 1 | [++1234] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_+1.json: -------------------------------------------------------------------------------- 1 | [+1] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_+Inf.json: -------------------------------------------------------------------------------- 1 | [+Inf] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_-01.json: -------------------------------------------------------------------------------- 1 | [-01] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_-1.0..json: -------------------------------------------------------------------------------- 1 | [-1.0.] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_-2..json: -------------------------------------------------------------------------------- 1 | [-2.] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_-NaN.json: -------------------------------------------------------------------------------- 1 | [-NaN] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_.-1.json: -------------------------------------------------------------------------------- 1 | [.-1] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_.2e-3.json: -------------------------------------------------------------------------------- 1 | [.2e-3] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_0.1.2.json: -------------------------------------------------------------------------------- 1 | [0.1.2] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_0.3e+.json: -------------------------------------------------------------------------------- 1 | [0.3e+] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_0.3e.json: -------------------------------------------------------------------------------- 1 | [0.3e] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_0.e1.json: -------------------------------------------------------------------------------- 1 | [0.e1] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_0_capital_E+.json: -------------------------------------------------------------------------------- 1 | [0E+] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_0_capital_E.json: -------------------------------------------------------------------------------- 1 | [0E] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_0e+.json: -------------------------------------------------------------------------------- 1 | [0e+] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_0e.json: -------------------------------------------------------------------------------- 1 | [0e] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_1.0e+.json: -------------------------------------------------------------------------------- 1 | [1.0e+] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_1.0e-.json: -------------------------------------------------------------------------------- 1 | [1.0e-] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_1.0e.json: -------------------------------------------------------------------------------- 1 | [1.0e] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_1_000.json: -------------------------------------------------------------------------------- 1 | [1 000.0] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_1eE2.json: -------------------------------------------------------------------------------- 1 | [1eE2] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_2.e+3.json: -------------------------------------------------------------------------------- 1 | [2.e+3] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_2.e-3.json: -------------------------------------------------------------------------------- 1 | [2.e-3] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_2.e3.json: -------------------------------------------------------------------------------- 1 | [2.e3] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_9.e+.json: -------------------------------------------------------------------------------- 1 | [9.e+] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_Inf.json: -------------------------------------------------------------------------------- 1 | [Inf] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_NaN.json: -------------------------------------------------------------------------------- 1 | [NaN] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_U+FF11_fullwidth_digit_one.json: -------------------------------------------------------------------------------- 1 | [1] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_expression.json: -------------------------------------------------------------------------------- 1 | [1+2] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_hex_1_digit.json: -------------------------------------------------------------------------------- 1 | [0x1] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_hex_2_digits.json: -------------------------------------------------------------------------------- 1 | [0x42] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_infinity.json: -------------------------------------------------------------------------------- 1 | [Infinity] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_invalid+-.json: -------------------------------------------------------------------------------- 1 | [0e+-1] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_invalid-negative-real.json: -------------------------------------------------------------------------------- 1 | [-123.123foo] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_invalid-utf-8-in-bigger-int.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/jsonexamples/test_parsing/n_number_invalid-utf-8-in-bigger-int.json -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_invalid-utf-8-in-exponent.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/jsonexamples/test_parsing/n_number_invalid-utf-8-in-exponent.json -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_invalid-utf-8-in-int.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/jsonexamples/test_parsing/n_number_invalid-utf-8-in-int.json -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_minus_infinity.json: -------------------------------------------------------------------------------- 1 | [-Infinity] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_minus_sign_with_trailing_garbage.json: -------------------------------------------------------------------------------- 1 | [-foo] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_minus_space_1.json: -------------------------------------------------------------------------------- 1 | [- 1] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_neg_int_starting_with_zero.json: -------------------------------------------------------------------------------- 1 | [-012] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_neg_real_without_int_part.json: -------------------------------------------------------------------------------- 1 | [-.123] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_neg_with_garbage_at_end.json: -------------------------------------------------------------------------------- 1 | [-1x] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_real_garbage_after_e.json: -------------------------------------------------------------------------------- 1 | [1ea] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_real_with_invalid_utf8_after_e.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/jsonexamples/test_parsing/n_number_real_with_invalid_utf8_after_e.json -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_real_without_fractional_part.json: -------------------------------------------------------------------------------- 1 | [1.] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_starting_with_dot.json: -------------------------------------------------------------------------------- 1 | [.123] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_with_alpha.json: -------------------------------------------------------------------------------- 1 | [1.2a-3] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_with_alpha_char.json: -------------------------------------------------------------------------------- 1 | [1.8011670033376514H-308] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_number_with_leading_zero.json: -------------------------------------------------------------------------------- 1 | [012] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_object_bad_value.json: -------------------------------------------------------------------------------- 1 | ["x", truth] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_object_bracket_key.json: -------------------------------------------------------------------------------- 1 | {[: "x"} 2 | -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_object_comma_instead_of_colon.json: -------------------------------------------------------------------------------- 1 | {"x", null} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_object_double_colon.json: -------------------------------------------------------------------------------- 1 | {"x"::"b"} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_object_emoji.json: -------------------------------------------------------------------------------- 1 | {🇨🇭} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_object_garbage_at_end.json: -------------------------------------------------------------------------------- 1 | {"a":"a" 123} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_object_key_with_single_quotes.json: -------------------------------------------------------------------------------- 1 | {key: 'value'} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_object_lone_continuation_byte_in_key_and_trailing_comma.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/jsonexamples/test_parsing/n_object_lone_continuation_byte_in_key_and_trailing_comma.json -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_object_missing_colon.json: -------------------------------------------------------------------------------- 1 | {"a" b} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_object_missing_key.json: -------------------------------------------------------------------------------- 1 | {:"b"} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_object_missing_semicolon.json: -------------------------------------------------------------------------------- 1 | {"a" "b"} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_object_missing_value.json: -------------------------------------------------------------------------------- 1 | {"a": -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_object_no-colon.json: -------------------------------------------------------------------------------- 1 | {"a" -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_object_non_string_key.json: -------------------------------------------------------------------------------- 1 | {1:1} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_object_non_string_key_but_huge_number_instead.json: -------------------------------------------------------------------------------- 1 | {9999E9999:1} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_object_repeated_null_null.json: -------------------------------------------------------------------------------- 1 | {null:null,null:null} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_object_several_trailing_commas.json: -------------------------------------------------------------------------------- 1 | {"id":0,,,,,} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_object_single_quote.json: -------------------------------------------------------------------------------- 1 | {'a':0} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_object_trailing_comma.json: -------------------------------------------------------------------------------- 1 | {"id":0,} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_object_trailing_comment.json: -------------------------------------------------------------------------------- 1 | {"a":"b"}/**/ -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_object_trailing_comment_open.json: -------------------------------------------------------------------------------- 1 | {"a":"b"}/**// -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_object_trailing_comment_slash_open.json: -------------------------------------------------------------------------------- 1 | {"a":"b"}// -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_object_trailing_comment_slash_open_incomplete.json: -------------------------------------------------------------------------------- 1 | {"a":"b"}/ -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_object_two_commas_in_a_row.json: -------------------------------------------------------------------------------- 1 | {"a":"b",,"c":"d"} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_object_unquoted_key.json: -------------------------------------------------------------------------------- 1 | {a: "b"} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_object_unterminated-value.json: -------------------------------------------------------------------------------- 1 | {"a":"a -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_object_with_single_string.json: -------------------------------------------------------------------------------- 1 | { "foo" : "bar", "a" } -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_object_with_trailing_garbage.json: -------------------------------------------------------------------------------- 1 | {"a":"b"}# -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_single_space.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_1_surrogate_then_escape.json: -------------------------------------------------------------------------------- 1 | ["\uD800\"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_1_surrogate_then_escape_u.json: -------------------------------------------------------------------------------- 1 | ["\uD800\u"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_1_surrogate_then_escape_u1.json: -------------------------------------------------------------------------------- 1 | ["\uD800\u1"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_1_surrogate_then_escape_u1x.json: -------------------------------------------------------------------------------- 1 | ["\uD800\u1x"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_accentuated_char_no_quotes.json: -------------------------------------------------------------------------------- 1 | [é] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_backslash_00.json: -------------------------------------------------------------------------------- 1 | ["\"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_escape_x.json: -------------------------------------------------------------------------------- 1 | ["\x00"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_escaped_backslash_bad.json: -------------------------------------------------------------------------------- 1 | ["\\\"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_escaped_ctrl_char_tab.json: -------------------------------------------------------------------------------- 1 | ["\ "] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_escaped_emoji.json: -------------------------------------------------------------------------------- 1 | ["\🌀"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_incomplete_escape.json: -------------------------------------------------------------------------------- 1 | ["\"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_incomplete_escaped_character.json: -------------------------------------------------------------------------------- 1 | ["\u00A"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_incomplete_surrogate.json: -------------------------------------------------------------------------------- 1 | ["\uD834\uDd"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_incomplete_surrogate_escape_invalid.json: -------------------------------------------------------------------------------- 1 | ["\uD800\uD800\x"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_invalid-utf-8-in-escape.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/jsonexamples/test_parsing/n_string_invalid-utf-8-in-escape.json -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_invalid_backslash_esc.json: -------------------------------------------------------------------------------- 1 | ["\a"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_invalid_unicode_escape.json: -------------------------------------------------------------------------------- 1 | ["\uqqqq"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_invalid_utf8_after_escape.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/jsonexamples/test_parsing/n_string_invalid_utf8_after_escape.json -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_leading_uescaped_thinspace.json: -------------------------------------------------------------------------------- 1 | [\u0020"asd"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_no_quotes_with_bad_escape.json: -------------------------------------------------------------------------------- 1 | [\n] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_single_doublequote.json: -------------------------------------------------------------------------------- 1 | " -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_single_quote.json: -------------------------------------------------------------------------------- 1 | ['single quote'] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_single_string_no_double_quotes.json: -------------------------------------------------------------------------------- 1 | abc -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_start_escape_unclosed.json: -------------------------------------------------------------------------------- 1 | ["\ -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_unescaped_crtl_char.json: -------------------------------------------------------------------------------- 1 | ["aa"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_unescaped_newline.json: -------------------------------------------------------------------------------- 1 | ["new 2 | line"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_unescaped_tab.json: -------------------------------------------------------------------------------- 1 | [" "] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_unicode_CapitalU.json: -------------------------------------------------------------------------------- 1 | "\UA66D" -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_string_with_trailing_garbage.json: -------------------------------------------------------------------------------- 1 | ""x -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_U+2060_word_joined.json: -------------------------------------------------------------------------------- 1 | [⁠] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_UTF8_BOM_no_data.json: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_angle_bracket_..json: -------------------------------------------------------------------------------- 1 | <.> -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_angle_bracket_null.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_array_trailing_garbage.json: -------------------------------------------------------------------------------- 1 | [1]x -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_array_with_extra_array_close.json: -------------------------------------------------------------------------------- 1 | [1]] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_array_with_unclosed_string.json: -------------------------------------------------------------------------------- 1 | ["asd] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_ascii-unicode-identifier.json: -------------------------------------------------------------------------------- 1 | aå -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_capitalized_True.json: -------------------------------------------------------------------------------- 1 | [True] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_close_unopened_array.json: -------------------------------------------------------------------------------- 1 | 1] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_comma_instead_of_closing_brace.json: -------------------------------------------------------------------------------- 1 | {"x": true, -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_double_array.json: -------------------------------------------------------------------------------- 1 | [][] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_end_array.json: -------------------------------------------------------------------------------- 1 | ] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_incomplete_UTF8_BOM.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/jsonexamples/test_parsing/n_structure_incomplete_UTF8_BOM.json -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_lone-invalid-utf-8.json: -------------------------------------------------------------------------------- 1 | � -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_lone-open-bracket.json: -------------------------------------------------------------------------------- 1 | [ -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_no_data.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/jsonexamples/test_parsing/n_structure_no_data.json -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_null-byte-outside-string.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_number_with_trailing_garbage.json: -------------------------------------------------------------------------------- 1 | 2@ -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_object_followed_by_closing_object.json: -------------------------------------------------------------------------------- 1 | {}} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_object_unclosed_no_value.json: -------------------------------------------------------------------------------- 1 | {"": -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_object_with_comment.json: -------------------------------------------------------------------------------- 1 | {"a":/*comment*/"b"} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_object_with_trailing_garbage.json: -------------------------------------------------------------------------------- 1 | {"a": true} "x" -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_open_array_apostrophe.json: -------------------------------------------------------------------------------- 1 | [' -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_open_array_comma.json: -------------------------------------------------------------------------------- 1 | [, -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_open_array_open_object.json: -------------------------------------------------------------------------------- 1 | [{ -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_open_array_open_string.json: -------------------------------------------------------------------------------- 1 | ["a -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_open_array_string.json: -------------------------------------------------------------------------------- 1 | ["a" -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_open_object.json: -------------------------------------------------------------------------------- 1 | { -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_open_object_close_array.json: -------------------------------------------------------------------------------- 1 | {] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_open_object_comma.json: -------------------------------------------------------------------------------- 1 | {, -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_open_object_open_array.json: -------------------------------------------------------------------------------- 1 | {[ -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_open_object_open_string.json: -------------------------------------------------------------------------------- 1 | {"a -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_open_object_string_with_apostrophes.json: -------------------------------------------------------------------------------- 1 | {'a' -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_open_open.json: -------------------------------------------------------------------------------- 1 | ["\{["\{["\{["\{ -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_single_eacute.json: -------------------------------------------------------------------------------- 1 | � -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_single_star.json: -------------------------------------------------------------------------------- 1 | * -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_trailing_#.json: -------------------------------------------------------------------------------- 1 | {"a":"b"}#{} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_uescaped_LF_before_string.json: -------------------------------------------------------------------------------- 1 | [\u000A""] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_unclosed_array.json: -------------------------------------------------------------------------------- 1 | [1 -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_unclosed_array_partial_null.json: -------------------------------------------------------------------------------- 1 | [ false, nul -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_unclosed_array_unfinished_false.json: -------------------------------------------------------------------------------- 1 | [ true, fals -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_unclosed_array_unfinished_true.json: -------------------------------------------------------------------------------- 1 | [ false, tru -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_unclosed_object.json: -------------------------------------------------------------------------------- 1 | {"asd":"asd" -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_unicode-identifier.json: -------------------------------------------------------------------------------- 1 | å -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_whitespace_U+2060_word_joiner.json: -------------------------------------------------------------------------------- 1 | [⁠] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/n_structure_whitespace_formfeed.json: -------------------------------------------------------------------------------- 1 | [ ] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_array_arraysWithSpaces.json: -------------------------------------------------------------------------------- 1 | [[] ] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_array_empty-string.json: -------------------------------------------------------------------------------- 1 | [""] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_array_empty.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_array_ending_with_newline.json: -------------------------------------------------------------------------------- 1 | ["a"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_array_false.json: -------------------------------------------------------------------------------- 1 | [false] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_array_heterogeneous.json: -------------------------------------------------------------------------------- 1 | [null, 1, "1", {}] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_array_null.json: -------------------------------------------------------------------------------- 1 | [null] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_array_with_1_and_newline.json: -------------------------------------------------------------------------------- 1 | [1 2 | ] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_array_with_leading_space.json: -------------------------------------------------------------------------------- 1 | [1] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_array_with_several_null.json: -------------------------------------------------------------------------------- 1 | [1,null,null,null,2] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_array_with_trailing_space.json: -------------------------------------------------------------------------------- 1 | [2] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_number.json: -------------------------------------------------------------------------------- 1 | [123e65] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_number_0e+1.json: -------------------------------------------------------------------------------- 1 | [0e+1] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_number_0e1.json: -------------------------------------------------------------------------------- 1 | [0e1] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_number_after_space.json: -------------------------------------------------------------------------------- 1 | [ 4] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_number_double_close_to_zero.json: -------------------------------------------------------------------------------- 1 | [-0.000000000000000000000000000000000000000000000000000000000000000000000000000001] 2 | -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_number_int_with_exp.json: -------------------------------------------------------------------------------- 1 | [20e1] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_number_minus_zero.json: -------------------------------------------------------------------------------- 1 | [-0] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_number_negative_int.json: -------------------------------------------------------------------------------- 1 | [-123] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_number_negative_one.json: -------------------------------------------------------------------------------- 1 | [-1] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_number_negative_zero.json: -------------------------------------------------------------------------------- 1 | [-0] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_number_real_capital_e.json: -------------------------------------------------------------------------------- 1 | [1E22] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_number_real_capital_e_neg_exp.json: -------------------------------------------------------------------------------- 1 | [1E-2] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_number_real_capital_e_pos_exp.json: -------------------------------------------------------------------------------- 1 | [1E+2] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_number_real_exponent.json: -------------------------------------------------------------------------------- 1 | [123e45] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_number_real_fraction_exponent.json: -------------------------------------------------------------------------------- 1 | [123.456e78] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_number_real_neg_exp.json: -------------------------------------------------------------------------------- 1 | [1e-2] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_number_real_pos_exponent.json: -------------------------------------------------------------------------------- 1 | [1e+2] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_number_simple_int.json: -------------------------------------------------------------------------------- 1 | [123] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_number_simple_real.json: -------------------------------------------------------------------------------- 1 | [123.456789] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_object.json: -------------------------------------------------------------------------------- 1 | {"asd":"sdf", "dfg":"fgh"} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_object_basic.json: -------------------------------------------------------------------------------- 1 | {"asd":"sdf"} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_object_duplicated_key.json: -------------------------------------------------------------------------------- 1 | {"a":"b","a":"c"} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_object_duplicated_key_and_value.json: -------------------------------------------------------------------------------- 1 | {"a":"b","a":"b"} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_object_empty.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_object_empty_key.json: -------------------------------------------------------------------------------- 1 | {"":0} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_object_escaped_null_in_key.json: -------------------------------------------------------------------------------- 1 | {"foo\u0000bar": 42} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_object_extreme_numbers.json: -------------------------------------------------------------------------------- 1 | { "min": -1.0e+28, "max": 1.0e+28 } -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_object_long_strings.json: -------------------------------------------------------------------------------- 1 | {"x":[{"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}], "id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_object_simple.json: -------------------------------------------------------------------------------- 1 | {"a":[]} -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_object_string_unicode.json: -------------------------------------------------------------------------------- 1 | {"title":"\u041f\u043e\u043b\u0442\u043e\u0440\u0430 \u0417\u0435\u043c\u043b\u0435\u043a\u043e\u043f\u0430" } -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_object_with_newlines.json: -------------------------------------------------------------------------------- 1 | { 2 | "a": "b" 3 | } -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_1_2_3_bytes_UTF-8_sequences.json: -------------------------------------------------------------------------------- 1 | ["\u0060\u012a\u12AB"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_accepted_surrogate_pair.json: -------------------------------------------------------------------------------- 1 | ["\uD801\udc37"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_accepted_surrogate_pairs.json: -------------------------------------------------------------------------------- 1 | ["\ud83d\ude39\ud83d\udc8d"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_allowed_escapes.json: -------------------------------------------------------------------------------- 1 | ["\"\\\/\b\f\n\r\t"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_backslash_and_u_escaped_zero.json: -------------------------------------------------------------------------------- 1 | ["\\u0000"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_backslash_doublequotes.json: -------------------------------------------------------------------------------- 1 | ["\""] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_comments.json: -------------------------------------------------------------------------------- 1 | ["a/*b*/c/*d//e"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_double_escape_a.json: -------------------------------------------------------------------------------- 1 | ["\\a"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_double_escape_n.json: -------------------------------------------------------------------------------- 1 | ["\\n"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_escaped_control_character.json: -------------------------------------------------------------------------------- 1 | ["\u0012"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_escaped_noncharacter.json: -------------------------------------------------------------------------------- 1 | ["\uFFFF"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_in_array.json: -------------------------------------------------------------------------------- 1 | ["asd"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_in_array_with_leading_space.json: -------------------------------------------------------------------------------- 1 | [ "asd"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_last_surrogates_1_and_2.json: -------------------------------------------------------------------------------- 1 | ["\uDBFF\uDFFF"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_nbsp_uescaped.json: -------------------------------------------------------------------------------- 1 | ["new\u00A0line"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_nonCharacterInUTF-8_U+10FFFF.json: -------------------------------------------------------------------------------- 1 | ["􏿿"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_nonCharacterInUTF-8_U+FFFF.json: -------------------------------------------------------------------------------- 1 | ["￿"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_null_escape.json: -------------------------------------------------------------------------------- 1 | ["\u0000"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_one-byte-utf-8.json: -------------------------------------------------------------------------------- 1 | ["\u002c"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_pi.json: -------------------------------------------------------------------------------- 1 | ["π"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_reservedCharacterInUTF-8_U+1BFFF.json: -------------------------------------------------------------------------------- 1 | ["𛿿"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_simple_ascii.json: -------------------------------------------------------------------------------- 1 | ["asd "] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_space.json: -------------------------------------------------------------------------------- 1 | " " -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json: -------------------------------------------------------------------------------- 1 | ["\uD834\uDd1e"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_three-byte-utf-8.json: -------------------------------------------------------------------------------- 1 | ["\u0821"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_two-byte-utf-8.json: -------------------------------------------------------------------------------- 1 | ["\u0123"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_u+2028_line_sep.json: -------------------------------------------------------------------------------- 1 | ["
"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_u+2029_par_sep.json: -------------------------------------------------------------------------------- 1 | ["
"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_uEscape.json: -------------------------------------------------------------------------------- 1 | ["\u0061\u30af\u30EA\u30b9"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_uescaped_newline.json: -------------------------------------------------------------------------------- 1 | ["new\u000Aline"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_unescaped_char_delete.json: -------------------------------------------------------------------------------- 1 | [""] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_unicode.json: -------------------------------------------------------------------------------- 1 | ["\uA66D"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_unicodeEscapedBackslash.json: -------------------------------------------------------------------------------- 1 | ["\u005C"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_unicode_2.json: -------------------------------------------------------------------------------- 1 | ["⍂㈴⍂"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_unicode_U+10FFFE_nonchar.json: -------------------------------------------------------------------------------- 1 | ["\uDBFF\uDFFE"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_unicode_U+1FFFE_nonchar.json: -------------------------------------------------------------------------------- 1 | ["\uD83F\uDFFE"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json: -------------------------------------------------------------------------------- 1 | ["\u200B"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_unicode_U+2064_invisible_plus.json: -------------------------------------------------------------------------------- 1 | ["\u2064"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_unicode_U+FDD0_nonchar.json: -------------------------------------------------------------------------------- 1 | ["\uFDD0"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_unicode_U+FFFE_nonchar.json: -------------------------------------------------------------------------------- 1 | ["\uFFFE"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_unicode_escaped_double_quote.json: -------------------------------------------------------------------------------- 1 | ["\u0022"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_utf8.json: -------------------------------------------------------------------------------- 1 | ["€𝄞"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_string_with_del_character.json: -------------------------------------------------------------------------------- 1 | ["aa"] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_structure_lonely_false.json: -------------------------------------------------------------------------------- 1 | false -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_structure_lonely_int.json: -------------------------------------------------------------------------------- 1 | 42 -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_structure_lonely_negative_real.json: -------------------------------------------------------------------------------- 1 | -0.1 -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_structure_lonely_null.json: -------------------------------------------------------------------------------- 1 | null -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_structure_lonely_string.json: -------------------------------------------------------------------------------- 1 | "asd" -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_structure_lonely_true.json: -------------------------------------------------------------------------------- 1 | true -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_structure_string_empty.json: -------------------------------------------------------------------------------- 1 | "" -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_structure_trailing_newline.json: -------------------------------------------------------------------------------- 1 | ["a"] 2 | -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_structure_true_in_array.json: -------------------------------------------------------------------------------- 1 | [true] -------------------------------------------------------------------------------- /jsonexamples/test_parsing/y_structure_whitespace_array.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /jsonexamples/test_transform/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Nicolas Seriot 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /jsonexamples/test_transform/number_-9223372036854775808.json: -------------------------------------------------------------------------------- 1 | [-9223372036854775808] 2 | -------------------------------------------------------------------------------- /jsonexamples/test_transform/number_-9223372036854775809.json: -------------------------------------------------------------------------------- 1 | [-9223372036854775809] 2 | -------------------------------------------------------------------------------- /jsonexamples/test_transform/number_1.0.json: -------------------------------------------------------------------------------- 1 | [1.0] 2 | -------------------------------------------------------------------------------- /jsonexamples/test_transform/number_1.000000000000000005.json: -------------------------------------------------------------------------------- 1 | [1.000000000000000005] 2 | -------------------------------------------------------------------------------- /jsonexamples/test_transform/number_1000000000000000.json: -------------------------------------------------------------------------------- 1 | [1000000000000000] 2 | -------------------------------------------------------------------------------- /jsonexamples/test_transform/number_10000000000000000999.json: -------------------------------------------------------------------------------- 1 | [10000000000000000999] 2 | -------------------------------------------------------------------------------- /jsonexamples/test_transform/number_1e-999.json: -------------------------------------------------------------------------------- 1 | [1E-999] 2 | -------------------------------------------------------------------------------- /jsonexamples/test_transform/number_1e6.json: -------------------------------------------------------------------------------- 1 | [1E6] 2 | -------------------------------------------------------------------------------- /jsonexamples/test_transform/number_9223372036854775807.json: -------------------------------------------------------------------------------- 1 | [9223372036854775807] 2 | -------------------------------------------------------------------------------- /jsonexamples/test_transform/number_9223372036854775808.json: -------------------------------------------------------------------------------- 1 | [9223372036854775808] 2 | -------------------------------------------------------------------------------- /jsonexamples/test_transform/object_key_nfc_nfd.json: -------------------------------------------------------------------------------- 1 | {"é":"NFC","é":"NFD"} -------------------------------------------------------------------------------- /jsonexamples/test_transform/object_key_nfd_nfc.json: -------------------------------------------------------------------------------- 1 | {"é":"NFD","é":"NFC"} -------------------------------------------------------------------------------- /jsonexamples/test_transform/object_same_key_different_values.json: -------------------------------------------------------------------------------- 1 | {"a":1,"a":2} -------------------------------------------------------------------------------- /jsonexamples/test_transform/object_same_key_same_value.json: -------------------------------------------------------------------------------- 1 | {"a":1,"a":1} -------------------------------------------------------------------------------- /jsonexamples/test_transform/object_same_key_unclear_values.json: -------------------------------------------------------------------------------- 1 | {"a":0, "a":-0} 2 | -------------------------------------------------------------------------------- /jsonexamples/test_transform/string_1_escaped_invalid_codepoint.json: -------------------------------------------------------------------------------- 1 | ["\uD800"] -------------------------------------------------------------------------------- /jsonexamples/test_transform/string_1_invalid_codepoint.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/jsonexamples/test_transform/string_1_invalid_codepoint.json -------------------------------------------------------------------------------- /jsonexamples/test_transform/string_2_escaped_invalid_codepoints.json: -------------------------------------------------------------------------------- 1 | ["\uD800\uD800"] -------------------------------------------------------------------------------- /jsonexamples/test_transform/string_2_invalid_codepoints.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/jsonexamples/test_transform/string_2_invalid_codepoints.json -------------------------------------------------------------------------------- /jsonexamples/test_transform/string_3_escaped_invalid_codepoints.json: -------------------------------------------------------------------------------- 1 | ["\uD800\uD800\uD800"] -------------------------------------------------------------------------------- /jsonexamples/test_transform/string_3_invalid_codepoints.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/jsonexamples/test_transform/string_3_invalid_codepoints.json -------------------------------------------------------------------------------- /jsonexamples/test_transform/string_with_escaped_NULL.json: -------------------------------------------------------------------------------- 1 | ["A\u0000B"] -------------------------------------------------------------------------------- /misc/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/misc/logo.png -------------------------------------------------------------------------------- /misc/logo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TkTech/py_yyjson/a6eb448d6be885ec6af385b18748b55cff33a3ee/misc/logo_small.png -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=74.1"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "yyjson" 7 | version = "4.0.6" 8 | description = "JSON parser & serializer built on yyjson" 9 | readme = "README.md" 10 | authors = [ 11 | {name = "Tyler Kennedy", email = "tk@tkte.ch" } 12 | ] 13 | license = { file= "LICENSE" } 14 | keywords = ["json", "parser", "serializer", "patcher"] 15 | classifiers = [ 16 | "Development Status :: 5 - Production/Stable", 17 | "Intended Audience :: Developers", 18 | "License :: OSI Approved :: MIT License", 19 | "Programming Language :: Python :: 3", 20 | "Programming Language :: Python :: 3.9", 21 | "Programming Language :: Python :: 3.10", 22 | "Programming Language :: Python :: 3.11", 23 | "Programming Language :: Python :: 3.12", 24 | "Programming Language :: Python :: 3.13", 25 | "Programming Language :: Python :: Implementation :: CPython", 26 | "Programming Language :: Python :: Implementation :: PyPy", 27 | "Operating System :: OS Independent", 28 | "Topic :: Software Development :: Libraries :: Python Modules", 29 | ] 30 | 31 | [project.urls] 32 | Homepage = "https://github.com/tktech/py_yyjson" 33 | Repository = "https://github.com/tktech/py_yyjson.git" 34 | 35 | [project.optional-dependencies] 36 | test = ["pytest"] 37 | release = [ 38 | "sphinx", 39 | "sphinx-copybutton", 40 | "ghp-import", 41 | "bumpversion", 42 | "black", 43 | "furo" 44 | ] 45 | 46 | [tool.setuptools] 47 | ext-modules = [ 48 | { name = "cyyjson", sources = ["yyjson/binding.c", "yyjson/yyjson.c", "yyjson/memory.c", "yyjson/document.c"], py-limited-api = true} 49 | ] 50 | packages = ["yyjson"] 51 | 52 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest-watch] 2 | beforerun = python setup.py build_ext -i -f 3 | ext = .py,.c 4 | -------------------------------------------------------------------------------- /tests/test_document.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | import pytest 4 | 5 | from yyjson import Document, WriterFlags, ReaderFlags 6 | 7 | # The maximum value of a signed 64 bit value. 8 | LLONG_MAX = 9223372036854775807 9 | # The maximum value of an unsigned 64 bit value. 10 | ULLONG_MAX = 18446744073709551615 11 | 12 | 13 | def test_document_is_mutable(): 14 | """Ensure we can determine if a document is mutable.""" 15 | doc = Document({"hello": "world"}) 16 | assert doc.is_thawed is True 17 | 18 | doc = Document('{"hello": "world"}') 19 | assert doc.is_thawed is False 20 | 21 | 22 | def test_document_from_str(): 23 | """Ensure we can parse a document from a str.""" 24 | doc = Document('{"hello": "world"}') 25 | assert doc.as_obj == {"hello": "world"} 26 | 27 | 28 | def test_document_types(): 29 | """Ensure each primitive type can be upcast (which does not have its own 30 | dedicated test.)""" 31 | values = ( 32 | ('"hello"', "hello"), 33 | ("1", 1), 34 | ("-1", -1), 35 | ('{"hello": "world"}', {"hello": "world"}), 36 | ("[0, 1, 2]", [0, 1, 2]), 37 | ) 38 | 39 | for src, dst in values: 40 | doc = Document(src) 41 | assert doc.as_obj == dst 42 | 43 | 44 | def test_document_dumps(): 45 | """ 46 | Ensure we can properly dump a document to a string. 47 | """ 48 | doc = Document('{"hello": "world"}') 49 | 50 | # Minified by default. 51 | assert doc.dumps() == '{"hello":"world"}' 52 | assert doc.dumps(flags=WriterFlags.PRETTY) == ("{\n" ' "hello": "world"\n' "}") 53 | 54 | doc = Document("{}") 55 | assert doc.dumps() == "{}" 56 | 57 | doc = Document({}) 58 | assert doc.dumps() == "{}" 59 | 60 | doc = Document([]) 61 | assert doc.dumps() == "[]" 62 | 63 | doc = Document({"hello": {"there": [0, 1, 2]}}) 64 | assert doc.dumps(at_pointer="/hello/there") == "[0,1,2]" 65 | 66 | 67 | def test_document_dumps_nan_and_inf(): 68 | """ 69 | Ensure we can dump documents with Infinity and NaN. 70 | """ 71 | # In standards mode, NaN & Inf should be a hard error. 72 | with pytest.raises(ValueError): 73 | Document('{"hello": NaN}') 74 | 75 | with pytest.raises(ValueError): 76 | Document('{"hello": Infinity}') 77 | 78 | doc = Document( 79 | """{ 80 | "hello": NaN, 81 | "world": Infinity 82 | }""", 83 | flags=ReaderFlags.ALLOW_INF_AND_NAN, 84 | ) 85 | obj = doc.as_obj 86 | assert math.isnan(obj["hello"]) 87 | assert math.isinf(obj["world"]) 88 | 89 | 90 | def test_document_raw_type(): 91 | """ 92 | Ensure we can dump objects that contain integers of any size. This is 93 | against the JSON specification, but the same as the built-in JSON module. 94 | 95 | In YYJSON, these values are stored in yyjson_raw, which essentially just 96 | points to the value as a string and does not attempt to interpret it as a 97 | number. 98 | """ 99 | # Ensure the maximum yyjson_sint value can be stored. 100 | doc = Document([LLONG_MAX]) 101 | assert doc.dumps() == "[9223372036854775807]" 102 | 103 | # Ensure the maximum yyjson_sint value + 1 can be stored as a yyjson_uint. 104 | doc = Document([LLONG_MAX + 1]) 105 | assert doc.dumps() == "[9223372036854775808]" 106 | 107 | # Ensure the maximum yyjson_uint value can be stored. 108 | doc = Document([ULLONG_MAX]) 109 | assert doc.dumps() == "[18446744073709551615]" 110 | 111 | # Ensure the maximum yyjson_uint value + 1 can be stored as a yyjson_raw. 112 | doc = Document([ULLONG_MAX + 1]) 113 | assert doc.dumps() == "[18446744073709551616]" 114 | assert doc.as_obj == [ULLONG_MAX + 1] 115 | 116 | # Ensure we can parse a document with and without the RAW flags set. 117 | doc = Document("[18446744073709551616000000000]", flags=ReaderFlags.NUMBERS_AS_RAW) 118 | assert doc.dumps() == "[18446744073709551616000000000]" 119 | assert doc.as_obj == [18446744073709551616000000000] 120 | 121 | 122 | def test_document_float_type(): 123 | """ 124 | Ensure we can load and dump floats. 125 | """ 126 | doc = Document([1.25]) 127 | assert doc.dumps() == "[1.25]" 128 | assert doc.as_obj == [1.25] 129 | 130 | doc = Document("1.25") 131 | assert doc.dumps() == "1.25" 132 | assert doc.as_obj == 1.25 133 | 134 | 135 | def test_document_boolean_type(): 136 | """ 137 | Ensure we can load and dump boolean types. 138 | """ 139 | doc = Document("true") 140 | assert doc.dumps() == "true" 141 | assert doc.as_obj is True 142 | 143 | doc = Document("false") 144 | assert doc.dumps() == "false" 145 | assert doc.as_obj is False 146 | 147 | doc = Document([True]) 148 | assert doc.dumps() == "[true]" 149 | assert doc.as_obj == [True] 150 | 151 | doc = Document([False]) 152 | assert doc.dumps() == "[false]" 153 | assert doc.as_obj == [False] 154 | 155 | 156 | def test_document_none_type(): 157 | """ 158 | Ensure we can load and dump the None type. 159 | """ 160 | doc = Document("null") 161 | assert doc.dumps() == "null" 162 | assert doc.as_obj is None 163 | 164 | doc = Document([None]) 165 | assert doc.dumps() == "[null]" 166 | assert doc.as_obj == [None] 167 | 168 | 169 | def test_document_get_pointer(): 170 | """ 171 | Ensure JSON pointers work. 172 | """ 173 | doc = Document( 174 | """{ 175 | "size" : 3, 176 | "users" : [ 177 | {"id": 1, "name": "Harry"}, 178 | {"id": 2, "name": "Ron"}, 179 | {"id": 3, "name": "Hermione"} 180 | ]} 181 | """ 182 | ) 183 | 184 | assert doc.get_pointer("/size") == 3 185 | assert doc.get_pointer("/users/0") == {"id": 1, "name": "Harry"} 186 | assert doc.get_pointer("/users/1/name") == "Ron" 187 | 188 | with pytest.raises(ValueError) as exc: 189 | doc.get_pointer("bob") 190 | 191 | assert "no prefix" in str(exc.value) 192 | 193 | doc = Document( 194 | { 195 | "size": 3, 196 | "users": [ 197 | {"id": 1, "name": "Harry"}, 198 | {"id": 2, "name": "Ron"}, 199 | {"id": 3, "name": "Hermione"}, 200 | ], 201 | } 202 | ) 203 | 204 | assert doc.get_pointer("/size") == 3 205 | assert doc.get_pointer("/users/0") == {"id": 1, "name": "Harry"} 206 | assert doc.get_pointer("/users/1/name") == "Ron" 207 | 208 | with pytest.raises(ValueError): 209 | doc.get_pointer("bob") 210 | 211 | 212 | def test_document_length(): 213 | """ 214 | Ensure we can get the length of mapping types. 215 | """ 216 | doc = Document("""{"hello": "world"}""") 217 | assert len(doc) == 1 218 | 219 | doc = Document("""[0, 1, 2]""") 220 | assert len(doc) == 3 221 | 222 | doc = Document("1") 223 | assert len(doc) == 0 224 | 225 | doc = Document({}) 226 | assert len(doc) == 0 227 | 228 | doc = Document([0, 1, 2]) 229 | assert len(doc) == 3 230 | 231 | 232 | def test_document_freeze(): 233 | """ 234 | Ensure we can freeze mutable documents. 235 | """ 236 | # Documents created from Python objects are always mutable by default, 237 | # so use that as our starting point. 238 | doc = Document({"hello": "world"}) 239 | assert doc.is_thawed is True 240 | 241 | doc.freeze() 242 | assert doc.is_thawed is False 243 | -------------------------------------------------------------------------------- /tests/test_merge_patch.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for JSON Merge-Patch (RFC 7386) support. 3 | """ 4 | import pytest 5 | 6 | from yyjson import Document 7 | 8 | 9 | @pytest.mark.parametrize( 10 | "context", 11 | [ 12 | { 13 | "original": '{"hello": "earth", "goodbye": "moon"}', 14 | "patch": '{"hello": "mars"}', 15 | "modified": {"hello": "mars", "goodbye": "moon"}, 16 | }, 17 | { 18 | "original": '{"content": {"hello": "earth", "goodbye": "moon"}}', 19 | "patch": '{"hello": "mars"}', 20 | "modified": {"hello": "mars", "goodbye": "moon"}, 21 | "at_pointer": "/content", 22 | }, 23 | { 24 | "original": {"hello": "earth", "goodbye": "moon"}, 25 | "patch": {"hello": "mars"}, 26 | "modified": {"hello": "mars", "goodbye": "moon"}, 27 | }, 28 | { 29 | "original": {"content": {"hello": "earth", "goodbye": "moon"}}, 30 | "patch": {"hello": "mars"}, 31 | "modified": {"hello": "mars", "goodbye": "moon"}, 32 | "at_pointer": "/content", 33 | }, 34 | { 35 | "original": {"hello": "earth", "goodbye": "moon"}, 36 | "patch": '{"hello": "mars"}', 37 | "modified": {"hello": "mars", "goodbye": "moon"}, 38 | }, 39 | { 40 | "original": '{"hello": "earth", "goodbye": "moon"}', 41 | "patch": {"hello": "mars"}, 42 | "modified": {"hello": "mars", "goodbye": "moon"}, 43 | }, 44 | ], 45 | ) 46 | def test_merge_patch(context): 47 | """ 48 | Ensures we can do a simple JSON Merge-Patch with various combinations of 49 | mutable and immutable documents. 50 | """ 51 | original = Document(context["original"]) 52 | patch = Document(context["patch"]) 53 | 54 | modified = original.patch( 55 | patch, at_pointer=context.get("at_pointer"), use_merge_patch=True 56 | ) 57 | 58 | assert modified.as_obj == context["modified"] 59 | -------------------------------------------------------------------------------- /tests/test_numbers.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from yyjson import Document, ReaderFlags 4 | 5 | 6 | def test_big_numbers(): 7 | """ 8 | Test the round-tripping of big numbers. 9 | 10 | The test set is from: 11 | https://blog.trl.sn/blog/what-is-a-json-number/#python-3-8-1 12 | """ 13 | test_numbers = [ 14 | "10", 15 | "1000000000", 16 | "10000000000000001", 17 | "100000000000000000001", 18 | "1" + "0" * 4301, 19 | "10.0", 20 | "10000000000000001.1", 21 | "1." + "1" * 34, 22 | "1E+2", 23 | "1E+309", 24 | ] 25 | 26 | for num in test_numbers: 27 | deserialized = Document(num, flags=ReaderFlags.NUMBERS_AS_RAW) 28 | 29 | obj = deserialized.as_obj 30 | 31 | assert str(obj) == num 32 | assert Document(obj).dumps() == num 33 | -------------------------------------------------------------------------------- /tests/test_patch.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for JSON Patch (RFC 6902) support. 3 | """ 4 | from pathlib import Path 5 | 6 | import pytest 7 | 8 | from yyjson import Document 9 | 10 | 11 | @pytest.mark.parametrize( 12 | "context", 13 | [ 14 | { 15 | "original": '{"baz": "qux", "foo": "bar"}', 16 | "patch": [ 17 | {"op": "replace", "path": "/baz", "value": "boo"}, 18 | {"op": "add", "path": "/hello", "value": ["world"]}, 19 | {"op": "remove", "path": "/foo"}, 20 | ], 21 | "modified": {"baz": "boo", "hello": ["world"]}, 22 | }, 23 | { 24 | "original": {"baz": "qux", "foo": "bar"}, 25 | "patch": [ 26 | {"op": "replace", "path": "/baz", "value": "boo"}, 27 | {"op": "add", "path": "/hello", "value": ["world"]}, 28 | {"op": "remove", "path": "/foo"}, 29 | ], 30 | "modified": {"baz": "boo", "hello": ["world"]}, 31 | }, 32 | ], 33 | ) 34 | def test_json_patch(context): 35 | """ 36 | Ensures we can do a simple JSON Patch with various combinations of mutable 37 | and immutable documents. 38 | """ 39 | original = Document(context["original"]) 40 | 41 | patch = Document(context["patch"]) 42 | 43 | modified = original.patch(patch) 44 | 45 | assert modified.as_obj == context["modified"] 46 | 47 | 48 | def test_json_patch_samples(): 49 | tests = Document(Path(__file__).parent / "tests.json").as_obj 50 | 51 | for test in tests: 52 | if test.get("disabled"): 53 | continue 54 | 55 | original = Document(test["doc"]) 56 | patch = Document(test["patch"]) 57 | 58 | try: 59 | modified = original.patch(patch) 60 | except Exception as exc: 61 | # We're _supposed_ to raise an error if there's an error key, 62 | # but our error messages aren't going to match at all so just 63 | # let it go. 64 | if test.get("error"): 65 | continue 66 | 67 | raise exc 68 | 69 | assert modified.as_obj == test["expected"] 70 | 71 | -------------------------------------------------------------------------------- /tests/test_serialization.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import yyjson 4 | 5 | 6 | class ClassThatCantBeSerialized: 7 | pass 8 | 9 | 10 | def test_serialize_unknown(): 11 | """ 12 | Ensure we raise an appropriate exception when trying to serialize an 13 | object we can't handle. 14 | """ 15 | with pytest.raises(TypeError): 16 | yyjson.Document({"example": ClassThatCantBeSerialized()}) 17 | 18 | with pytest.raises(TypeError): 19 | yyjson.Document([ClassThatCantBeSerialized()]) 20 | 21 | 22 | def test_serialize_default_func(): 23 | """ 24 | Ensure that we can serialize an object by providing a default function. 25 | """ 26 | 27 | def default(obj): 28 | if isinstance(obj, ClassThatCantBeSerialized): 29 | return "I'm a string now!" 30 | raise TypeError(f"Can't serialize {obj}") 31 | 32 | doc = yyjson.Document( 33 | {"example": ClassThatCantBeSerialized()}, default=default 34 | ) 35 | assert doc.as_obj["example"] == "I'm a string now!" 36 | -------------------------------------------------------------------------------- /tests/test_shim.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test the shims for compatibility with the standard library JSON module. 3 | 4 | We ignore most options. 5 | """ 6 | from io import BytesIO, StringIO 7 | 8 | import yyjson 9 | 10 | 11 | def test_dump(): 12 | """ 13 | Ensure we can dump a document to a string. 14 | """ 15 | with StringIO() as test: 16 | yyjson.dump({"a": 1, "b": 2}, test) 17 | assert test.getvalue() == '{"a":1,"b":2}' 18 | 19 | 20 | def test_dumps(): 21 | """ 22 | Ensure we can dump a document to a string. 23 | """ 24 | assert yyjson.dumps({"a": 1, "b": 2}) == '{"a":1,"b":2}' 25 | 26 | 27 | def test_load(): 28 | """ 29 | Ensure we can load a document from a string. 30 | """ 31 | with BytesIO(b'{"a":1,"b":2}') as test: 32 | assert yyjson.load(test) == {"a": 1, "b": 2} 33 | 34 | 35 | def test_loads(): 36 | """ 37 | Ensure we can load a document from a string. 38 | """ 39 | assert yyjson.loads('{"a":1,"b":2}') == {"a": 1, "b": 2} 40 | -------------------------------------------------------------------------------- /tests/tests.json: -------------------------------------------------------------------------------- 1 | [ 2 | { "comment": "empty list, empty docs", 3 | "doc": {}, 4 | "patch": [], 5 | "expected": {} }, 6 | 7 | { "comment": "empty patch list", 8 | "doc": {"foo": 1}, 9 | "patch": [], 10 | "expected": {"foo": 1} }, 11 | 12 | { "comment": "rearrangements OK?", 13 | "doc": {"foo": 1, "bar": 2}, 14 | "patch": [], 15 | "expected": {"bar":2, "foo": 1} }, 16 | 17 | { "comment": "rearrangements OK? How about one level down ... array", 18 | "doc": [{"foo": 1, "bar": 2}], 19 | "patch": [], 20 | "expected": [{"bar":2, "foo": 1}] }, 21 | 22 | { "comment": "rearrangements OK? How about one level down...", 23 | "doc": {"foo":{"foo": 1, "bar": 2}}, 24 | "patch": [], 25 | "expected": {"foo":{"bar":2, "foo": 1}} }, 26 | 27 | { "comment": "add replaces any existing field", 28 | "doc": {"foo": null}, 29 | "patch": [{"op": "add", "path": "/foo", "value":1}], 30 | "expected": {"foo": 1} }, 31 | 32 | { "comment": "toplevel array", 33 | "doc": [], 34 | "patch": [{"op": "add", "path": "/0", "value": "foo"}], 35 | "expected": ["foo"] }, 36 | 37 | { "comment": "toplevel array, no change", 38 | "doc": ["foo"], 39 | "patch": [], 40 | "expected": ["foo"] }, 41 | 42 | { "comment": "toplevel object, numeric string", 43 | "doc": {}, 44 | "patch": [{"op": "add", "path": "/foo", "value": "1"}], 45 | "expected": {"foo":"1"} }, 46 | 47 | { "comment": "toplevel object, integer", 48 | "doc": {}, 49 | "patch": [{"op": "add", "path": "/foo", "value": 1}], 50 | "expected": {"foo":1} }, 51 | 52 | { "comment": "Toplevel scalar values OK?", 53 | "doc": "foo", 54 | "patch": [{"op": "replace", "path": "", "value": "bar"}], 55 | "expected": "bar", 56 | "disabled": true }, 57 | 58 | { "comment": "replace object document with array document?", 59 | "doc": {}, 60 | "patch": [{"op": "add", "path": "", "value": []}], 61 | "expected": [] }, 62 | 63 | { "comment": "replace array document with object document?", 64 | "doc": [], 65 | "patch": [{"op": "add", "path": "", "value": {}}], 66 | "expected": {} }, 67 | 68 | { "comment": "append to root array document?", 69 | "doc": [], 70 | "patch": [{"op": "add", "path": "/-", "value": "hi"}], 71 | "expected": ["hi"] }, 72 | 73 | { "comment": "Add, / target", 74 | "doc": {}, 75 | "patch": [ {"op": "add", "path": "/", "value":1 } ], 76 | "expected": {"":1} }, 77 | 78 | { "comment": "Add, /foo/ deep target (trailing slash)", 79 | "doc": {"foo": {}}, 80 | "patch": [ {"op": "add", "path": "/foo/", "value":1 } ], 81 | "expected": {"foo":{"": 1}} }, 82 | 83 | { "comment": "Add composite value at top level", 84 | "doc": {"foo": 1}, 85 | "patch": [{"op": "add", "path": "/bar", "value": [1, 2]}], 86 | "expected": {"foo": 1, "bar": [1, 2]} }, 87 | 88 | { "comment": "Add into composite value", 89 | "doc": {"foo": 1, "baz": [{"qux": "hello"}]}, 90 | "patch": [{"op": "add", "path": "/baz/0/foo", "value": "world"}], 91 | "expected": {"foo": 1, "baz": [{"qux": "hello", "foo": "world"}]} }, 92 | 93 | { "doc": {"bar": [1, 2]}, 94 | "patch": [{"op": "add", "path": "/bar/8", "value": "5"}], 95 | "error": "Out of bounds (upper)" }, 96 | 97 | { "doc": {"bar": [1, 2]}, 98 | "patch": [{"op": "add", "path": "/bar/-1", "value": "5"}], 99 | "error": "Out of bounds (lower)" }, 100 | 101 | { "doc": {"foo": 1}, 102 | "patch": [{"op": "add", "path": "/bar", "value": true}], 103 | "expected": {"foo": 1, "bar": true} }, 104 | 105 | { "doc": {"foo": 1}, 106 | "patch": [{"op": "add", "path": "/bar", "value": false}], 107 | "expected": {"foo": 1, "bar": false} }, 108 | 109 | { "doc": {"foo": 1}, 110 | "patch": [{"op": "add", "path": "/bar", "value": null}], 111 | "expected": {"foo": 1, "bar": null} }, 112 | 113 | { "comment": "0 can be an array index or object element name", 114 | "doc": {"foo": 1}, 115 | "patch": [{"op": "add", "path": "/0", "value": "bar"}], 116 | "expected": {"foo": 1, "0": "bar" } }, 117 | 118 | { "doc": ["foo"], 119 | "patch": [{"op": "add", "path": "/1", "value": "bar"}], 120 | "expected": ["foo", "bar"] }, 121 | 122 | { "doc": ["foo", "sil"], 123 | "patch": [{"op": "add", "path": "/1", "value": "bar"}], 124 | "expected": ["foo", "bar", "sil"] }, 125 | 126 | { "doc": ["foo", "sil"], 127 | "patch": [{"op": "add", "path": "/0", "value": "bar"}], 128 | "expected": ["bar", "foo", "sil"] }, 129 | 130 | { "comment": "push item to array via last index + 1", 131 | "doc": ["foo", "sil"], 132 | "patch": [{"op":"add", "path": "/2", "value": "bar"}], 133 | "expected": ["foo", "sil", "bar"] }, 134 | 135 | { "comment": "add item to array at index > length should fail", 136 | "doc": ["foo", "sil"], 137 | "patch": [{"op":"add", "path": "/3", "value": "bar"}], 138 | "error": "index is greater than number of items in array" }, 139 | 140 | { "comment": "test against implementation-specific numeric parsing", 141 | "doc": {"1e0": "foo"}, 142 | "patch": [{"op": "test", "path": "/1e0", "value": "foo"}], 143 | "expected": {"1e0": "foo"} }, 144 | 145 | { "comment": "test with bad number should fail", 146 | "doc": ["foo", "bar"], 147 | "patch": [{"op": "test", "path": "/1e0", "value": "bar"}], 148 | "error": "test op shouldn't get array element 1" }, 149 | 150 | { "doc": ["foo", "sil"], 151 | "patch": [{"op": "add", "path": "/bar", "value": 42}], 152 | "error": "Object operation on array target" }, 153 | 154 | { "doc": ["foo", "sil"], 155 | "patch": [{"op": "add", "path": "/1", "value": ["bar", "baz"]}], 156 | "expected": ["foo", ["bar", "baz"], "sil"], 157 | "comment": "value in array add not flattened" }, 158 | 159 | { "doc": {"foo": 1, "bar": [1, 2, 3, 4]}, 160 | "patch": [{"op": "remove", "path": "/bar"}], 161 | "expected": {"foo": 1} }, 162 | 163 | { "doc": {"foo": 1, "baz": [{"qux": "hello"}]}, 164 | "patch": [{"op": "remove", "path": "/baz/0/qux"}], 165 | "expected": {"foo": 1, "baz": [{}]} }, 166 | 167 | { "doc": {"foo": 1, "baz": [{"qux": "hello"}]}, 168 | "patch": [{"op": "replace", "path": "/foo", "value": [1, 2, 3, 4]}], 169 | "expected": {"foo": [1, 2, 3, 4], "baz": [{"qux": "hello"}]} }, 170 | 171 | { "doc": {"foo": [1, 2, 3, 4], "baz": [{"qux": "hello"}]}, 172 | "patch": [{"op": "replace", "path": "/baz/0/qux", "value": "world"}], 173 | "expected": {"foo": [1, 2, 3, 4], "baz": [{"qux": "world"}]} }, 174 | 175 | { "doc": ["foo"], 176 | "patch": [{"op": "replace", "path": "/0", "value": "bar"}], 177 | "expected": ["bar"] }, 178 | 179 | { "doc": [""], 180 | "patch": [{"op": "replace", "path": "/0", "value": 0}], 181 | "expected": [0] }, 182 | 183 | { "doc": [""], 184 | "patch": [{"op": "replace", "path": "/0", "value": true}], 185 | "expected": [true] }, 186 | 187 | { "doc": [""], 188 | "patch": [{"op": "replace", "path": "/0", "value": false}], 189 | "expected": [false] }, 190 | 191 | { "doc": [""], 192 | "patch": [{"op": "replace", "path": "/0", "value": null}], 193 | "expected": [null] }, 194 | 195 | { "doc": ["foo", "sil"], 196 | "patch": [{"op": "replace", "path": "/1", "value": ["bar", "baz"]}], 197 | "expected": ["foo", ["bar", "baz"]], 198 | "comment": "value in array replace not flattened" }, 199 | 200 | { "comment": "replace whole document", 201 | "doc": {"foo": "bar"}, 202 | "patch": [{"op": "replace", "path": "", "value": {"baz": "qux"}}], 203 | "expected": {"baz": "qux"} }, 204 | 205 | { "comment": "test replace with missing parent key should fail", 206 | "doc": {"bar": "baz"}, 207 | "patch": [{"op": "replace", "path": "/foo/bar", "value": false}], 208 | "error": "replace op should fail with missing parent key" }, 209 | 210 | { "comment": "spurious patch properties", 211 | "doc": {"foo": 1}, 212 | "patch": [{"op": "test", "path": "/foo", "value": 1, "spurious": 1}], 213 | "expected": {"foo": 1} }, 214 | 215 | { "doc": {"foo": null}, 216 | "patch": [{"op": "test", "path": "/foo", "value": null}], 217 | "expected": {"foo": null}, 218 | "comment": "null value should be valid obj property" }, 219 | 220 | { "doc": {"foo": null}, 221 | "patch": [{"op": "replace", "path": "/foo", "value": "truthy"}], 222 | "expected": {"foo": "truthy"}, 223 | "comment": "null value should be valid obj property to be replaced with something truthy" }, 224 | 225 | { "doc": {"foo": null}, 226 | "patch": [{"op": "move", "from": "/foo", "path": "/bar"}], 227 | "expected": {"bar": null}, 228 | "comment": "null value should be valid obj property to be moved" }, 229 | 230 | { "doc": {"foo": null}, 231 | "patch": [{"op": "copy", "from": "/foo", "path": "/bar"}], 232 | "expected": {"foo": null, "bar": null}, 233 | "comment": "null value should be valid obj property to be copied" }, 234 | 235 | { "doc": {"foo": null}, 236 | "patch": [{"op": "remove", "path": "/foo"}], 237 | "expected": {}, 238 | "comment": "null value should be valid obj property to be removed" }, 239 | 240 | { "doc": {"foo": "bar"}, 241 | "patch": [{"op": "replace", "path": "/foo", "value": null}], 242 | "expected": {"foo": null}, 243 | "comment": "null value should still be valid obj property replace other value" }, 244 | 245 | { "doc": {"foo": {"foo": 1, "bar": 2}}, 246 | "patch": [{"op": "test", "path": "/foo", "value": {"bar": 2, "foo": 1}}], 247 | "expected": {"foo": {"foo": 1, "bar": 2}}, 248 | "comment": "test should pass despite rearrangement" }, 249 | 250 | { "doc": {"foo": [{"foo": 1, "bar": 2}]}, 251 | "patch": [{"op": "test", "path": "/foo", "value": [{"bar": 2, "foo": 1}]}], 252 | "expected": {"foo": [{"foo": 1, "bar": 2}]}, 253 | "comment": "test should pass despite (nested) rearrangement" }, 254 | 255 | { "doc": {"foo": {"bar": [1, 2, 5, 4]}}, 256 | "patch": [{"op": "test", "path": "/foo", "value": {"bar": [1, 2, 5, 4]}}], 257 | "expected": {"foo": {"bar": [1, 2, 5, 4]}}, 258 | "comment": "test should pass - no error" }, 259 | 260 | { "doc": {"foo": {"bar": [1, 2, 5, 4]}}, 261 | "patch": [{"op": "test", "path": "/foo", "value": [1, 2]}], 262 | "error": "test op should fail" }, 263 | 264 | { "comment": "Whole document", 265 | "doc": { "foo": 1 }, 266 | "patch": [{"op": "test", "path": "", "value": {"foo": 1}}], 267 | "disabled": true }, 268 | 269 | { "comment": "Empty-string element", 270 | "doc": { "": 1 }, 271 | "patch": [{"op": "test", "path": "/", "value": 1}], 272 | "expected": { "": 1 } }, 273 | 274 | { "doc": { 275 | "foo": ["bar", "baz"], 276 | "": 0, 277 | "a/b": 1, 278 | "c%d": 2, 279 | "e^f": 3, 280 | "g|h": 4, 281 | "i\\j": 5, 282 | "k\"l": 6, 283 | " ": 7, 284 | "m~n": 8 285 | }, 286 | "patch": [{"op": "test", "path": "/foo", "value": ["bar", "baz"]}, 287 | {"op": "test", "path": "/foo/0", "value": "bar"}, 288 | {"op": "test", "path": "/", "value": 0}, 289 | {"op": "test", "path": "/a~1b", "value": 1}, 290 | {"op": "test", "path": "/c%d", "value": 2}, 291 | {"op": "test", "path": "/e^f", "value": 3}, 292 | {"op": "test", "path": "/g|h", "value": 4}, 293 | {"op": "test", "path": "/i\\j", "value": 5}, 294 | {"op": "test", "path": "/k\"l", "value": 6}, 295 | {"op": "test", "path": "/ ", "value": 7}, 296 | {"op": "test", "path": "/m~0n", "value": 8}], 297 | "expected": { 298 | "": 0, 299 | " ": 7, 300 | "a/b": 1, 301 | "c%d": 2, 302 | "e^f": 3, 303 | "foo": [ 304 | "bar", 305 | "baz" 306 | ], 307 | "g|h": 4, 308 | "i\\j": 5, 309 | "k\"l": 6, 310 | "m~n": 8 311 | } 312 | }, 313 | { "comment": "Move to same location has no effect", 314 | "doc": {"foo": 1}, 315 | "patch": [{"op": "move", "from": "/foo", "path": "/foo"}], 316 | "expected": {"foo": 1} }, 317 | 318 | { "doc": {"foo": 1, "baz": [{"qux": "hello"}]}, 319 | "patch": [{"op": "move", "from": "/foo", "path": "/bar"}], 320 | "expected": {"baz": [{"qux": "hello"}], "bar": 1} }, 321 | 322 | { "doc": {"baz": [{"qux": "hello"}], "bar": 1}, 323 | "patch": [{"op": "move", "from": "/baz/0/qux", "path": "/baz/1"}], 324 | "expected": {"baz": [{}, "hello"], "bar": 1} }, 325 | 326 | { "doc": {"baz": [{"qux": "hello"}], "bar": 1}, 327 | "patch": [{"op": "copy", "from": "/baz/0", "path": "/boo"}], 328 | "expected": {"baz":[{"qux":"hello"}],"bar":1,"boo":{"qux":"hello"}} }, 329 | 330 | { "comment": "replacing the root of the document is possible with add", 331 | "doc": {"foo": "bar"}, 332 | "patch": [{"op": "add", "path": "", "value": {"baz": "qux"}}], 333 | "expected": {"baz":"qux"}}, 334 | 335 | { "comment": "Adding to \"/-\" adds to the end of the array", 336 | "doc": [ 1, 2 ], 337 | "patch": [ { "op": "add", "path": "/-", "value": { "foo": [ "bar", "baz" ] } } ], 338 | "expected": [ 1, 2, { "foo": [ "bar", "baz" ] } ]}, 339 | 340 | { "comment": "Adding to \"/-\" adds to the end of the array, even n levels down", 341 | "doc": [ 1, 2, [ 3, [ 4, 5 ] ] ], 342 | "patch": [ { "op": "add", "path": "/2/1/-", "value": { "foo": [ "bar", "baz" ] } } ], 343 | "expected": [ 1, 2, [ 3, [ 4, 5, { "foo": [ "bar", "baz" ] } ] ] ]}, 344 | 345 | { "comment": "test remove with bad number should fail", 346 | "doc": {"foo": 1, "baz": [{"qux": "hello"}]}, 347 | "patch": [{"op": "remove", "path": "/baz/1e0/qux"}], 348 | "error": "remove op shouldn't remove from array with bad number" }, 349 | 350 | { "comment": "test remove on array", 351 | "doc": [1, 2, 3, 4], 352 | "patch": [{"op": "remove", "path": "/0"}], 353 | "expected": [2, 3, 4] }, 354 | 355 | { "comment": "test repeated removes", 356 | "doc": [1, 2, 3, 4], 357 | "patch": [{ "op": "remove", "path": "/1" }, 358 | { "op": "remove", "path": "/2" }], 359 | "expected": [1, 3] }, 360 | 361 | { "comment": "test remove with bad index should fail", 362 | "doc": [1, 2, 3, 4], 363 | "patch": [{"op": "remove", "path": "/1e0"}], 364 | "error": "remove op shouldn't remove from array with bad number" }, 365 | 366 | { "comment": "test replace with bad number should fail", 367 | "doc": [""], 368 | "patch": [{"op": "replace", "path": "/1e0", "value": false}], 369 | "error": "replace op shouldn't replace in array with bad number" }, 370 | 371 | { "comment": "test copy with bad number should fail", 372 | "doc": {"baz": [1,2,3], "bar": 1}, 373 | "patch": [{"op": "copy", "from": "/baz/1e0", "path": "/boo"}], 374 | "error": "copy op shouldn't work with bad number" }, 375 | 376 | { "comment": "test move with bad number should fail", 377 | "doc": {"foo": 1, "baz": [1,2,3,4]}, 378 | "patch": [{"op": "move", "from": "/baz/1e0", "path": "/foo"}], 379 | "error": "move op shouldn't work with bad number" }, 380 | 381 | { "comment": "test add with bad number should fail", 382 | "doc": ["foo", "sil"], 383 | "patch": [{"op": "add", "path": "/1e0", "value": "bar"}], 384 | "error": "add op shouldn't add to array with bad number" }, 385 | 386 | { "comment": "missing 'path' parameter", 387 | "doc": {}, 388 | "patch": [ { "op": "add", "value": "bar" } ], 389 | "error": "missing 'path' parameter" }, 390 | 391 | { "comment": "'path' parameter with null value", 392 | "doc": {}, 393 | "patch": [ { "op": "add", "path": null, "value": "bar" } ], 394 | "error": "null is not valid value for 'path'" }, 395 | 396 | { "comment": "invalid JSON Pointer token", 397 | "doc": {}, 398 | "patch": [ { "op": "add", "path": "foo", "value": "bar" } ], 399 | "error": "JSON Pointer should start with a slash" }, 400 | 401 | { "comment": "missing 'value' parameter to add", 402 | "doc": [ 1 ], 403 | "patch": [ { "op": "add", "path": "/-" } ], 404 | "error": "missing 'value' parameter" }, 405 | 406 | { "comment": "missing 'value' parameter to replace", 407 | "doc": [ 1 ], 408 | "patch": [ { "op": "replace", "path": "/0" } ], 409 | "error": "missing 'value' parameter" }, 410 | 411 | { "comment": "missing 'value' parameter to test", 412 | "doc": [ null ], 413 | "patch": [ { "op": "test", "path": "/0" } ], 414 | "error": "missing 'value' parameter" }, 415 | 416 | { "comment": "missing value parameter to test - where undef is falsy", 417 | "doc": [ false ], 418 | "patch": [ { "op": "test", "path": "/0" } ], 419 | "error": "missing 'value' parameter" }, 420 | 421 | { "comment": "missing from parameter to copy", 422 | "doc": [ 1 ], 423 | "patch": [ { "op": "copy", "path": "/-" } ], 424 | "error": "missing 'from' parameter" }, 425 | 426 | { "comment": "missing from location to copy", 427 | "doc": { "foo": 1 }, 428 | "patch": [ { "op": "copy", "from": "/bar", "path": "/foo" } ], 429 | "error": "missing 'from' location" }, 430 | 431 | { "comment": "missing from parameter to move", 432 | "doc": { "foo": 1 }, 433 | "patch": [ { "op": "move", "path": "" } ], 434 | "error": "missing 'from' parameter" }, 435 | 436 | { "comment": "missing from location to move", 437 | "doc": { "foo": 1 }, 438 | "patch": [ { "op": "move", "from": "/bar", "path": "/foo" } ], 439 | "error": "missing 'from' location" }, 440 | 441 | { "comment": "duplicate ops", 442 | "doc": { "foo": "bar" }, 443 | "patch": [ { "op": "add", "path": "/baz", "value": "qux", 444 | "op": "move", "from":"/foo" } ], 445 | "error": "patch has two 'op' members", 446 | "disabled": true }, 447 | 448 | { "comment": "unrecognized op should fail", 449 | "doc": {"foo": 1}, 450 | "patch": [{"op": "spam", "path": "/foo", "value": 1}], 451 | "error": "Unrecognized op 'spam'" }, 452 | 453 | { "comment": "test with bad array number that has leading zeros", 454 | "doc": ["foo", "bar"], 455 | "patch": [{"op": "test", "path": "/00", "value": "foo"}], 456 | "error": "test op should reject the array value, it has leading zeros" }, 457 | 458 | { "comment": "test with bad array number that has leading zeros", 459 | "doc": ["foo", "bar"], 460 | "patch": [{"op": "test", "path": "/01", "value": "bar"}], 461 | "error": "test op should reject the array value, it has leading zeros" }, 462 | 463 | { "comment": "Removing nonexistent field", 464 | "doc": {"foo" : "bar"}, 465 | "patch": [{"op": "remove", "path": "/baz"}], 466 | "error": "removing a nonexistent field should fail" }, 467 | 468 | { "comment": "Removing deep nonexistent path", 469 | "doc": {"foo" : "bar"}, 470 | "patch": [{"op": "remove", "path": "/missing1/missing2"}], 471 | "error": "removing a nonexistent field should fail" }, 472 | 473 | { "comment": "Removing nonexistent index", 474 | "doc": ["foo", "bar"], 475 | "patch": [{"op": "remove", "path": "/2"}], 476 | "error": "removing a nonexistent index should fail" }, 477 | 478 | { "comment": "Patch with different capitalisation than doc", 479 | "doc": {"foo":"bar"}, 480 | "patch": [{"op": "add", "path": "/FOO", "value": "BAR"}], 481 | "expected": {"foo": "bar", "FOO": "BAR"} 482 | } 483 | 484 | ] 485 | -------------------------------------------------------------------------------- /yyjson/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ["Document", "ReaderFlags", "WriterFlags"] 2 | 3 | import enum 4 | 5 | from cyyjson import Document 6 | 7 | 8 | class ReaderFlags(enum.IntFlag): 9 | """ 10 | Flags that can be passed into JSON reading functions to control parsing 11 | behaviour. 12 | """ 13 | 14 | #: Stop when done instead of issues an error if there's additional content 15 | #: after a JSON document. This option may be used to parse small pieces of 16 | #: JSON in larger data, such as NDJSON. 17 | STOP_WHEN_DONE = 0x02 18 | #: Allow single trailing comma at the end of an object or array, such as 19 | #: [1,2,3,] {"a":1,"b":2,}. 20 | ALLOW_TRAILING_COMMAS = 0x04 21 | #: Allow C-style single line and multiple line comments. 22 | ALLOW_COMMENTS = 0x08 23 | #: Allow inf/nan number and literal, case-insensitive, such as 1e999, NaN, 24 | #: inf, -Infinity 25 | ALLOW_INF_AND_NAN = 0x10 26 | #: Alias for `NUMBERS_AS_DECIMAL`. 27 | NUMBERS_AS_RAW = 0x20 28 | #: Read all numbers as Decimal objects instead of native types. This option 29 | #: is useful for preserving the exact precision of numbers or for handling 30 | #: numbers that are too large to fit in a native type. 31 | NUMBERS_AS_DECIMAL = 0x20 32 | #: Alias for `BIG_NUMBERS_AS_DECIMAL`. 33 | BIGNUM_AS_RAW = 0x80 34 | #: Like `NUMBERS_AS_DECIMAL`, but only for numbers that are too large to 35 | #: fit in a native type. 36 | BIG_NUMBERS_AS_DECIMAL = 0x80 37 | 38 | 39 | class WriterFlags(enum.IntFlag): 40 | """ 41 | Flags that can be passed into JSON writing functions to control writing 42 | behaviour. 43 | """ 44 | 45 | #: Write the JSON with 4-space indents and newlines. 46 | PRETTY = 0x01 47 | #: Write JSON pretty with 2 space indent. This flag will override 48 | #: the PRETTY flag. 49 | PRETTY_TWO_SPACES = 0x40 50 | #: Escapes unicode as \uXXXXX so that all output is ASCII. 51 | ESCAPE_UNICODE = 0x02 52 | #: Escapes / as \\/. 53 | ESCAPE_SLASHES = 0x04 54 | #: Writes Infinity and NaN. 55 | ALLOW_INF_AND_NAN = 0x08 56 | #: Writes Infinity and NaN as `null` instead of raising an error. 57 | INF_AND_NAN_AS_NULL = 0x10 58 | #: Write a newline at the end of the JSON string. 59 | WRITE_NEWLINE_AT_END = 0x80 60 | 61 | 62 | def load(fp): 63 | return Document(fp.read()).as_obj 64 | 65 | 66 | def loads(s): 67 | return Document(s).as_obj 68 | 69 | 70 | def dumps(obj, *, default=None): 71 | return Document(obj, default=default).dumps() 72 | 73 | 74 | def dump(obj, fp, *, default=None): 75 | fp.write(Document(obj, default=default).dumps()) 76 | -------------------------------------------------------------------------------- /yyjson/__init__.pyi: -------------------------------------------------------------------------------- 1 | import enum 2 | from pathlib import Path 3 | from typing import Any, Optional, List, Dict, Union, Callable 4 | 5 | class ReaderFlags(enum.IntFlag): 6 | STOP_WHEN_DONE = 0x02 7 | ALLOW_TRAILING_COMMAS = 0x04 8 | ALLOW_COMMENTS = 0x08 9 | ALLOW_INF_AND_NAN = 0x10 10 | NUMBERS_AS_RAW = 0x20 11 | NUMBERS_AS_DECIMAL = 0x20 12 | BIGNUM_AS_RAW = 0x80 13 | BIG_NUMBERS_AS_DECIMAL = 0x80 14 | 15 | class WriterFlags(enum.IntFlag): 16 | PRETTY = 0x01 17 | ESCAPE_UNICODE = 0x02 18 | ESCAPE_SLASHES = 0x04 19 | ALLOW_INF_AND_NAN = 0x08 20 | INF_AND_NAN_AS_NULL = 0x10 21 | WRITE_NEWLINE_AT_END = 0x80 22 | 23 | Content = Union[str, bytes, List, Dict, Path] 24 | 25 | class Document: 26 | as_obj: Any 27 | def __init__( 28 | self, 29 | content: Content, 30 | flags: Optional[ReaderFlags] = ..., 31 | default: Callable[[Any], Any] = ..., 32 | ): ... 33 | def __len__(self) -> int: ... 34 | def get_pointer(self, pointer: str) -> Any: ... 35 | def dumps( 36 | self, 37 | flags: Optional[WriterFlags] = ..., 38 | at_pointer: Optional[str] = ..., 39 | ) -> str: ... 40 | def patch( 41 | self, 42 | patch: "Document", 43 | *, 44 | at_pointer: Optional[str] = None, 45 | use_merge_patch: bool = False 46 | ) -> "Document": ... 47 | @property 48 | def is_thawed(self) -> bool: ... 49 | def freeze(self) -> None: ... 50 | def thaw(self) -> None: ... 51 | 52 | def load( 53 | fp, 54 | *, 55 | cls=None, 56 | object_hook=None, 57 | parse_float=None, 58 | parse_int=None, 59 | parse_constant=None, 60 | object_pairs_hook=None, 61 | **kw 62 | ): ... 63 | def loads( 64 | s, 65 | *, 66 | cls=None, 67 | object_hook=None, 68 | parse_float=None, 69 | parse_int=None, 70 | parse_constant=None, 71 | object_pairs_hook=None, 72 | **kw 73 | ): ... 74 | def dumps( 75 | obj, 76 | *, 77 | skipkeys=False, 78 | ensure_ascii=True, 79 | check_circular=True, 80 | allow_nan=True, 81 | cls=None, 82 | indent=None, 83 | separators=None, 84 | default=None, 85 | sort_keys=False, 86 | **kw 87 | ): ... 88 | def dump( 89 | obj, 90 | fp, 91 | *, 92 | skipkeys=False, 93 | ensure_ascii=True, 94 | check_circular=True, 95 | allow_nan=True, 96 | cls=None, 97 | indent=None, 98 | separators=None, 99 | default=None, 100 | sort_keys=False, 101 | **kw 102 | ): ... 103 | -------------------------------------------------------------------------------- /yyjson/binding.c: -------------------------------------------------------------------------------- 1 | #define PY_SSIZE_T_CLEAN 2 | #include 3 | 4 | #include "document.h" 5 | #include "memory.h" 6 | #include "decimal.h" 7 | #include "yyjson.h" 8 | 9 | PyObject *YY_DecimalModule = NULL; 10 | PyObject *YY_DecimalClass = NULL; 11 | 12 | static PyModuleDef yymodule = { 13 | PyModuleDef_HEAD_INIT, .m_name = "cyyjson", 14 | .m_doc = "Python bindings for the yyjson project.", .m_size = -1}; 15 | 16 | PyMODINIT_FUNC PyInit_cyyjson(void) { 17 | PyObject* m; 18 | 19 | if (PyType_Ready(&DocumentType) < 0) { 20 | return NULL; 21 | } 22 | 23 | m = PyModule_Create(&yymodule); 24 | if (m == NULL) { 25 | return NULL; 26 | } 27 | 28 | Py_INCREF(&DocumentType); 29 | if (PyModule_AddObject(m, "Document", (PyObject*)&DocumentType) < 0) { 30 | Py_DECREF(&DocumentType); 31 | Py_DECREF(m); 32 | return NULL; 33 | } 34 | 35 | // We need to pre-import the Decimal module to have it available globally. 36 | YY_DecimalModule = PyImport_ImportModule("decimal"); 37 | if (YY_DecimalModule == NULL) { 38 | return NULL; 39 | } 40 | Py_INCREF(YY_DecimalModule); 41 | 42 | YY_DecimalClass = PyObject_GetAttrString(YY_DecimalModule, "Decimal"); 43 | if (YY_DecimalClass == NULL) { 44 | return NULL; 45 | } 46 | Py_INCREF(YY_DecimalClass); 47 | 48 | return m; 49 | } 50 | -------------------------------------------------------------------------------- /yyjson/decimal.h: -------------------------------------------------------------------------------- 1 | #ifndef PY_YYJSON_DECIMAL_H 2 | #define PY_YYJSON_DECIMAL_H 3 | 4 | #include 5 | 6 | extern PyObject *YY_DecimalModule; 7 | extern PyObject *YY_DecimalClass; 8 | 9 | #endif -------------------------------------------------------------------------------- /yyjson/document.c: -------------------------------------------------------------------------------- 1 | #include "document.h" 2 | 3 | #include "memory.h" 4 | #include "decimal.h" 5 | 6 | #define ENSURE_MUTABLE(self) \ 7 | if (self->i_doc) { \ 8 | self->m_doc = yyjson_doc_mut_copy(self->i_doc, self->alc); \ 9 | yyjson_doc_free(self->i_doc); \ 10 | self->i_doc = NULL; \ 11 | } 12 | 13 | static PyObject *mut_element_to_primitive(yyjson_mut_val *val); 14 | static PyObject *element_to_primitive(yyjson_val *val); 15 | 16 | static PyObject *pathlib = NULL; 17 | static PyObject *path = NULL; 18 | 19 | /** 20 | * Count the number of UTF-8 characters in the given string. 21 | */ 22 | static inline size_t num_utf8_chars(const char *src, size_t len) { 23 | size_t count = 0; 24 | for (size_t i = 0; i < len; i++) { 25 | if (yyjson_likely(src[i] >> 6 != 2)) { 26 | count++; 27 | } 28 | } 29 | return count; 30 | } 31 | 32 | /** 33 | * Convert the given UTF-8 string into a Python unicode object. 34 | */ 35 | static inline PyObject *unicode_from_str(const char *src, size_t len) { 36 | #ifndef PYPY_VERSION 37 | // Exploit the internals of CPython's unicode implementation to 38 | // implement a fast-path for ASCII data, which is by far the 39 | // most common case. This is the single greatest performance gain 40 | // of any optimization in this library. 41 | // 42 | // The details of these structures are here: 43 | // https://github.com/python/cpython/blob/main/Include/cpython/unicodeobject.h#L53 44 | size_t num_chars = num_utf8_chars(src, len); 45 | 46 | if (yyjson_likely(num_chars == len)) { 47 | PyObject *uni = PyUnicode_New(len, 127); 48 | if (!uni) return NULL; 49 | PyASCIIObject *uni_ascii = (PyASCIIObject *)uni; 50 | memcpy(uni_ascii + 1, src, len); 51 | return uni; 52 | } 53 | #endif 54 | 55 | return PyUnicode_DecodeUTF8(src, len, NULL); 56 | } 57 | 58 | /** 59 | * Recursively convert the given value into an equivalent high-level Python 60 | * object. 61 | **/ 62 | static PyObject *element_to_primitive(yyjson_val *val) { 63 | yyjson_type type = yyjson_get_type(val); 64 | 65 | switch (type) { 66 | case YYJSON_TYPE_NULL: 67 | Py_RETURN_NONE; 68 | case YYJSON_TYPE_BOOL: 69 | if (yyjson_get_subtype(val) == YYJSON_SUBTYPE_TRUE) { 70 | Py_RETURN_TRUE; 71 | } else { 72 | Py_RETURN_FALSE; 73 | } 74 | case YYJSON_TYPE_NUM: { 75 | switch (yyjson_get_subtype(val)) { 76 | case YYJSON_SUBTYPE_UINT: 77 | return PyLong_FromUnsignedLongLong(yyjson_get_uint(val)); 78 | case YYJSON_SUBTYPE_SINT: 79 | return PyLong_FromLongLong(yyjson_get_sint(val)); 80 | case YYJSON_SUBTYPE_REAL: 81 | return PyFloat_FromDouble(yyjson_get_real(val)); 82 | } 83 | } 84 | case YYJSON_TYPE_STR: { 85 | size_t str_len = yyjson_get_len(val); 86 | const char *str = yyjson_get_str(val); 87 | return unicode_from_str(str, str_len); 88 | } 89 | case YYJSON_TYPE_ARR: { 90 | PyObject *arr = PyList_New(yyjson_arr_size(val)); 91 | if (!arr) { 92 | return NULL; 93 | } 94 | 95 | yyjson_val *obj_val; 96 | PyObject *py_val; 97 | 98 | yyjson_arr_iter iter = {0}; 99 | yyjson_arr_iter_init(val, &iter); 100 | 101 | size_t idx = 0; 102 | while ((obj_val = yyjson_arr_iter_next(&iter))) { 103 | py_val = element_to_primitive(obj_val); 104 | if (!py_val) { 105 | return NULL; 106 | } 107 | 108 | PyList_SET_ITEM(arr, idx++, py_val); 109 | } 110 | 111 | return arr; 112 | } 113 | case YYJSON_TYPE_OBJ: { 114 | PyObject *dict = PyDict_New(); 115 | if (!dict) { 116 | return NULL; 117 | } 118 | 119 | yyjson_val *obj_key, *obj_val; 120 | PyObject *py_key, *py_val; 121 | const char *str; 122 | size_t str_len; 123 | 124 | yyjson_obj_iter iter = {0}; 125 | yyjson_obj_iter_init(val, &iter); 126 | 127 | while ((obj_key = yyjson_obj_iter_next(&iter))) { 128 | obj_val = yyjson_obj_iter_get_val(obj_key); 129 | 130 | str_len = yyjson_get_len(obj_key); 131 | str = yyjson_get_str(obj_key); 132 | 133 | py_key = unicode_from_str(str, str_len); 134 | py_val = element_to_primitive(obj_val); 135 | 136 | if (!py_key) { 137 | return NULL; 138 | } 139 | 140 | if (!py_val) { 141 | Py_DECREF(py_key); 142 | return NULL; 143 | } 144 | 145 | if (PyDict_SetItem(dict, py_key, py_val) == -1) { 146 | return NULL; 147 | } 148 | 149 | Py_DECREF(py_key); 150 | Py_DECREF(py_val); 151 | } 152 | return dict; 153 | } 154 | case YYJSON_TYPE_RAW: { 155 | size_t str_len = yyjson_get_len(val); 156 | const char *str = yyjson_get_raw(val); 157 | PyObject *uni = unicode_from_str(str, str_len); 158 | PyObject *result = PyObject_CallOneArg(YY_DecimalClass, uni); 159 | Py_DECREF(uni); 160 | return result; 161 | } 162 | case YYJSON_TYPE_NONE: 163 | default: 164 | PyErr_SetString(PyExc_TypeError, "Unknown tape type encountered."); 165 | return NULL; 166 | } 167 | } 168 | 169 | /** 170 | * Recursively convert the given value into an equivalent high-level Python 171 | * object. 172 | **/ 173 | static PyObject *mut_element_to_primitive(yyjson_mut_val *val) { 174 | yyjson_type type = yyjson_mut_get_type(val); 175 | 176 | switch (type) { 177 | case YYJSON_TYPE_NULL: 178 | Py_RETURN_NONE; 179 | case YYJSON_TYPE_BOOL: 180 | if (yyjson_mut_get_subtype(val) == YYJSON_SUBTYPE_TRUE) { 181 | Py_RETURN_TRUE; 182 | } else { 183 | Py_RETURN_FALSE; 184 | } 185 | case YYJSON_TYPE_NUM: { 186 | switch (yyjson_mut_get_subtype(val)) { 187 | case YYJSON_SUBTYPE_UINT: 188 | return PyLong_FromUnsignedLongLong(yyjson_mut_get_uint(val)); 189 | case YYJSON_SUBTYPE_SINT: 190 | return PyLong_FromLongLong(yyjson_mut_get_sint(val)); 191 | case YYJSON_SUBTYPE_REAL: 192 | return PyFloat_FromDouble(yyjson_mut_get_real(val)); 193 | } 194 | } 195 | case YYJSON_TYPE_STR: { 196 | size_t str_len = yyjson_mut_get_len(val); 197 | const char *str = yyjson_mut_get_str(val); 198 | 199 | return PyUnicode_FromStringAndSize(str, str_len); 200 | } 201 | case YYJSON_TYPE_ARR: { 202 | PyObject *arr = PyList_New(yyjson_mut_arr_size(val)); 203 | if (!arr) { 204 | return NULL; 205 | } 206 | 207 | yyjson_mut_val *obj_val; 208 | PyObject *py_val; 209 | 210 | yyjson_mut_arr_iter iter = {0}; 211 | yyjson_mut_arr_iter_init(val, &iter); 212 | 213 | size_t idx = 0; 214 | while ((obj_val = yyjson_mut_arr_iter_next(&iter))) { 215 | py_val = mut_element_to_primitive(obj_val); 216 | if (!py_val) { 217 | return NULL; 218 | } 219 | 220 | PyList_SET_ITEM(arr, idx++, py_val); 221 | } 222 | 223 | return arr; 224 | } 225 | case YYJSON_TYPE_OBJ: { 226 | PyObject *dict = PyDict_New(); 227 | if (!dict) { 228 | return NULL; 229 | } 230 | 231 | yyjson_mut_val *obj_key, *obj_val; 232 | PyObject *py_key, *py_val; 233 | 234 | yyjson_mut_obj_iter iter = {0}; 235 | yyjson_mut_obj_iter_init(val, &iter); 236 | 237 | while ((obj_key = yyjson_mut_obj_iter_next(&iter))) { 238 | obj_val = yyjson_mut_obj_iter_get_val(obj_key); 239 | 240 | py_key = mut_element_to_primitive(obj_key); 241 | py_val = mut_element_to_primitive(obj_val); 242 | 243 | if (!py_key) { 244 | return NULL; 245 | } 246 | 247 | if (!py_val) { 248 | Py_DECREF(py_key); 249 | return NULL; 250 | } 251 | 252 | if (PyDict_SetItem(dict, py_key, py_val) == -1) { 253 | return NULL; 254 | } 255 | 256 | Py_DECREF(py_key); 257 | Py_DECREF(py_val); 258 | } 259 | return dict; 260 | } 261 | case YYJSON_TYPE_RAW: { 262 | size_t str_len = yyjson_mut_get_len(val); 263 | const char *str = yyjson_mut_get_raw(val); 264 | PyObject *uni = unicode_from_str(str, str_len); 265 | PyObject *result = PyObject_CallOneArg(YY_DecimalClass, uni); 266 | Py_DECREF(uni); 267 | return result; 268 | } 269 | case YYJSON_TYPE_NONE: 270 | default: 271 | PyErr_SetString(PyExc_TypeError, "Unknown tape type encountered."); 272 | return NULL; 273 | } 274 | } 275 | 276 | PyTypeObject *type_for_conversion(PyObject *obj) { 277 | if (obj->ob_type == &PyUnicode_Type) { 278 | return &PyUnicode_Type; 279 | } else if (obj->ob_type == &PyLong_Type) { 280 | return &PyLong_Type; 281 | } else if (obj->ob_type == &PyFloat_Type) { 282 | return &PyFloat_Type; 283 | } else if (obj->ob_type == &PyDict_Type) { 284 | return &PyDict_Type; 285 | } else if (obj->ob_type == &PyList_Type) { 286 | return &PyList_Type; 287 | } else if (obj->ob_type == &PyBool_Type) { 288 | return &PyBool_Type; 289 | } else if (obj->ob_type == Py_None->ob_type) { 290 | return Py_None->ob_type; 291 | } 292 | return NULL; 293 | } 294 | 295 | /** 296 | * Recursively convert a Python object into yyjson elements. 297 | */ 298 | static inline yyjson_mut_val *mut_primitive_to_element( 299 | DocumentObject *self, 300 | yyjson_mut_doc *doc, 301 | PyObject *obj 302 | ) { 303 | const PyTypeObject *ob_type = type_for_conversion(obj); 304 | 305 | if (yyjson_unlikely(ob_type == NULL) && self->default_func != NULL) { 306 | PyObject *result = PyObject_CallFunctionObjArgs(self->default_func, obj, NULL); 307 | if (result == NULL) { 308 | return NULL; 309 | } 310 | yyjson_mut_val *val = mut_primitive_to_element(self, doc, result); 311 | Py_DECREF(result); 312 | return val; 313 | } 314 | 315 | if (ob_type == &PyUnicode_Type) { 316 | Py_ssize_t str_len; 317 | const char *str = PyUnicode_AsUTF8AndSize(obj, &str_len); 318 | return yyjson_mut_strncpy(doc, str, str_len); 319 | } else if (ob_type == &PyLong_Type) { 320 | // Serialization of integers is a little special, since Python allows 321 | // integers of (effectively) any size. While > 53bit is technically 322 | // against the spec, at least 64bit is widely supported and the builtin 323 | // Python JSON module supports integers of any size. 324 | int overflow = 0; 325 | const int64_t num = PyLong_AsLongLongAndOverflow(obj, &overflow); 326 | if (!overflow) { 327 | if (num == -1 && PyErr_Occurred()) return NULL; 328 | return yyjson_mut_sint(doc, num); 329 | } else { 330 | // Number overflowed, try an unsigned long long. 331 | const uint64_t unum = PyLong_AsUnsignedLongLong(obj); 332 | if (unum == (uint64_t)-1 && PyErr_Occurred()) { 333 | // Number might have been too large even for a unit64_t, resort 334 | // to a raw type by converting the number to its string 335 | // representation. 336 | PyErr_Clear(); // Erase the OverflowError 337 | PyObject *str_repr = PyObject_Str(obj); 338 | Py_ssize_t str_len; 339 | const char *str = PyUnicode_AsUTF8AndSize(str_repr, &str_len); 340 | return yyjson_mut_rawncpy(doc, str, str_len); 341 | } else { 342 | return yyjson_mut_uint(doc, unum); 343 | } 344 | } 345 | } else if (ob_type == &PyList_Type) { 346 | yyjson_mut_val *val = yyjson_mut_arr(doc); 347 | yyjson_mut_val *object_value = NULL; 348 | for (Py_ssize_t i = 0; i < PyList_GET_SIZE(obj); i++) { 349 | object_value = mut_primitive_to_element(self, doc, PyList_GET_ITEM(obj, i)); 350 | 351 | if (yyjson_unlikely(object_value == NULL)) { 352 | return NULL; 353 | } 354 | 355 | yyjson_mut_arr_append(val, object_value); 356 | } 357 | return val; 358 | } else if (ob_type == &PyDict_Type) { 359 | yyjson_mut_val *val = yyjson_mut_obj(doc); 360 | yyjson_mut_val *object_value = NULL; 361 | Py_ssize_t i = 0; 362 | PyObject *key, *value; 363 | 364 | while (PyDict_Next(obj, &i, &key, &value)) { 365 | Py_ssize_t str_len; 366 | const char *str = PyUnicode_AsUTF8AndSize(key, &str_len); 367 | object_value = mut_primitive_to_element(self, doc, value); 368 | if (yyjson_unlikely(object_value == NULL)) { 369 | return NULL; 370 | } 371 | yyjson_mut_obj_add( 372 | val, yyjson_mut_strncpy(doc, str, str_len), object_value 373 | ); 374 | } 375 | return val; 376 | } else if (ob_type == &PyFloat_Type) { 377 | double dnum = PyFloat_AsDouble(obj); 378 | if (dnum == -1 && PyErr_Occurred()) return NULL; 379 | return yyjson_mut_real(doc, dnum); 380 | } else if (obj == Py_True) { 381 | return yyjson_mut_true(doc); 382 | } else if (obj == Py_False) { 383 | return yyjson_mut_false(doc); 384 | } else if (obj == Py_None) { 385 | return yyjson_mut_null(doc); 386 | } else if (yyjson_unlikely(PyObject_IsInstance(obj, YY_DecimalClass))) { 387 | PyObject *str_repr = PyObject_Str(obj); 388 | Py_ssize_t str_len; 389 | const char *str = PyUnicode_AsUTF8AndSize(str_repr, &str_len); 390 | yyjson_mut_val *val = yyjson_mut_rawncpy(doc, str, str_len); 391 | Py_DECREF(str_repr); 392 | return val; 393 | } else { 394 | PyErr_Format(PyExc_TypeError, 395 | "Object of type '%s' is not JSON serializable", 396 | Py_TYPE(obj)->tp_name 397 | ); 398 | return NULL; 399 | } 400 | } 401 | 402 | static void Document_dealloc(DocumentObject *self) { 403 | if (self->i_doc != NULL) yyjson_doc_free(self->i_doc); 404 | if (self->m_doc != NULL) yyjson_mut_doc_free(self->m_doc); 405 | Py_XDECREF(self->default_func); 406 | Py_TYPE(self)->tp_free((PyObject *)self); 407 | } 408 | 409 | static PyObject *Document_new( 410 | PyTypeObject *type, PyObject *args, PyObject *kwds 411 | ) { 412 | DocumentObject *self; 413 | self = (DocumentObject *)type->tp_alloc(type, 0); 414 | 415 | if (self != NULL) { 416 | self->m_doc = NULL; 417 | self->i_doc = NULL; 418 | self->alc = &PyMem_Allocator; 419 | } 420 | 421 | return (PyObject *)self; 422 | } 423 | 424 | PyDoc_STRVAR( 425 | Document_init_doc, 426 | "A single JSON document.\n" 427 | "\n" 428 | "A `Document` can be built from a JSON-serializable Python object,\n" 429 | "a JSON document in a ``str``, a JSON document encoded to ``bytes``,\n" 430 | "or a ``Path()`` object to read a file from disk.\n" 431 | "Ex:\n" 432 | "\n" 433 | ".. doctest::\n" 434 | "\n" 435 | " >>> Document({'a': 1, 'b': 2})\n" 436 | " >>> Document(b'{\"a\": 1, \"b\": 2}')\n" 437 | " >>> Document('{\"a\": 1, \"b\": 2}')\n" 438 | " >>> Document(Path('path/to/file.json'))\n" 439 | "\n" 440 | "By default, the parsing is strict and follows the JSON specifications.\n" 441 | "You can change this behaviour by passing in :class:`ReaderFlags`. Ex:\n" 442 | "\n" 443 | ".. doctest::\n" 444 | "\n" 445 | " >>> Document('''{\n" 446 | " ... // Comments in JSON!?!?\n" 447 | " ... \"a\": 1\n" 448 | " ... }''', flags=ReaderFlags.ALLOW_COMMENTS)\n" 449 | "\n" 450 | ".. note::\n" 451 | "\n" 452 | " yyjson has distinct APIs and data structures for mutable and " 453 | "immutable\n" 454 | " documents. This class is a wrapper around both of them, and will\n" 455 | " automatically convert between them as needed.\n" 456 | "\n" 457 | ":param content: The initial content of the document.\n" 458 | ":type content: ``str``, ``bytes``, ``Path``, ``dict``, ``list``\n" 459 | ":param flags: Flags that modify the document parsing behaviour.\n" 460 | ":type flags: :class:`ReaderFlags`, optional\n" 461 | ":param default: A function called to convert objects that are not\n" 462 | " JSON serializable. Should return a JSON serializable version\n" 463 | " of the object or raise a TypeError.\n" 464 | ":type default: callable, optional" 465 | ); 466 | static int Document_init(DocumentObject *self, PyObject *args, PyObject *kwds) { 467 | static char *kwlist[] = {"content", "flags", "default", NULL}; 468 | PyObject *content; 469 | PyObject *default_func = NULL; 470 | yyjson_read_err err; 471 | yyjson_read_flag r_flag = 0; 472 | 473 | if (!PyArg_ParseTupleAndKeywords( 474 | args, kwds, "O|$IO", kwlist, &content, &r_flag, &default_func 475 | )) { 476 | return -1; 477 | } 478 | 479 | if (default_func && default_func != Py_None && !PyCallable_Check(default_func)) { 480 | PyErr_SetString(PyExc_TypeError, "default must be callable"); 481 | return -1; 482 | } 483 | 484 | self->default_func = default_func == Py_None ? NULL : default_func; 485 | Py_XINCREF(default_func); 486 | 487 | if (yyjson_unlikely(pathlib == NULL)) { 488 | pathlib = PyImport_ImportModule("pathlib"); 489 | if (yyjson_unlikely(pathlib == NULL)) { 490 | return -1; 491 | } 492 | path = PyObject_GetAttrString(pathlib, "Path"); 493 | if (yyjson_unlikely(path == NULL)) { 494 | return -1; 495 | } 496 | } 497 | 498 | if (yyjson_likely(PyBytes_Check(content))) { 499 | Py_ssize_t content_len; 500 | const char *content_as_utf8 = NULL; 501 | 502 | PyBytes_AsStringAndSize(content, (char **)&content_as_utf8, &content_len); 503 | 504 | self->i_doc = yyjson_read_opts( 505 | // As long as we don't expose the insitu reader flag, it's safe to 506 | // discard the const here. 507 | (char *)content_as_utf8, content_len, r_flag, self->alc, &err 508 | ); 509 | 510 | if (!self->i_doc) { 511 | PyErr_SetString(PyExc_ValueError, err.msg); 512 | return -1; 513 | } 514 | 515 | return 0; 516 | } else if (yyjson_likely(PyUnicode_Check(content))) { 517 | // We were given a string, so just parse it into a document. 518 | Py_ssize_t content_len; 519 | const char *content_as_utf8 = NULL; 520 | 521 | content_as_utf8 = PyUnicode_AsUTF8AndSize(content, &content_len); 522 | 523 | self->i_doc = yyjson_read_opts( 524 | // As long as we don't expose the insitu reader flag, it's safe to 525 | // discard the const here. 526 | (char *)content_as_utf8, content_len, r_flag, self->alc, &err 527 | ); 528 | 529 | if (!self->i_doc) { 530 | PyErr_SetString(PyExc_ValueError, err.msg); 531 | return -1; 532 | } 533 | 534 | return 0; 535 | } else if (yyjson_unlikely(PyObject_IsInstance(content, path))) { 536 | // We were given a Path object to a location on disk. 537 | PyObject *as_str = PyObject_Str(content); 538 | if (!as_str) { 539 | return -1; 540 | } 541 | 542 | Py_ssize_t str_len; 543 | const char *str = PyUnicode_AsUTF8AndSize(as_str, &str_len); 544 | if (!str) { 545 | Py_XDECREF(as_str); 546 | return -1; 547 | } 548 | 549 | self->i_doc = yyjson_read_file(str, r_flag, self->alc, &err); 550 | 551 | Py_XDECREF(as_str); 552 | Py_XDECREF(str); 553 | 554 | if (!self->i_doc) { 555 | PyErr_SetString(PyExc_ValueError, err.msg); 556 | return -1; 557 | } 558 | 559 | return 0; 560 | } else { 561 | self->m_doc = yyjson_mut_doc_new(self->alc); 562 | 563 | if (!self->m_doc) { 564 | PyErr_SetString( 565 | PyExc_ValueError, "Unable to create empty mutable document." 566 | ); 567 | return -1; 568 | } 569 | 570 | yyjson_mut_val *val = mut_primitive_to_element(self, self->m_doc, content); 571 | 572 | if (val == NULL) { 573 | return -1; 574 | } 575 | 576 | yyjson_mut_doc_set_root(self->m_doc, val); 577 | 578 | return 0; 579 | } 580 | } 581 | 582 | /** 583 | * Recursively convert the document into Python objects. 584 | */ 585 | static PyObject *Document_as_obj(DocumentObject *self, void *closure) { 586 | if (self->i_doc) { 587 | return element_to_primitive(yyjson_doc_get_root(self->i_doc)); 588 | } else { 589 | return mut_element_to_primitive(yyjson_mut_doc_get_root(self->m_doc)); 590 | } 591 | } 592 | 593 | /** 594 | * Is the document mutable? 595 | */ 596 | static PyObject *Document_is_thawed(DocumentObject *self, void *closure) { 597 | return PyBool_FromLong(self->m_doc != NULL); 598 | } 599 | 600 | PyDoc_STRVAR( 601 | Document_dumps_doc, 602 | "Dumps the document to a string and returns it.\n" 603 | "\n" 604 | "By default, serializes to a minified string and strictly follows the\n" 605 | "JSON specification. Ex:\n" 606 | "\n" 607 | ".. doctest::\n" 608 | "\n" 609 | " >>> doc = Document({'hello': 'world'})\n" 610 | " >>> print(doc.dumps())\n" 611 | " {\"hello\":\"world\"}\n" 612 | "\n" 613 | "This behaviour can be controlled by passing :class:`WriterFlags`. Ex:\n" 614 | "\n" 615 | ".. doctest::\n" 616 | "\n" 617 | " >>> doc = Document({'hello': 'world'})\n" 618 | " >>> print(doc.dumps(flags=WriterFlags.PRETTY))\n" 619 | " {\n" 620 | " \"hello\": \"world\"\n" 621 | " }\n" 622 | "\n" 623 | "To dump just part of a document, you can pass a JSON pointer (RFC 6901)\n" 624 | "as ``at_pointer``, ex:\n" 625 | "\n" 626 | ".. doctest::\n" 627 | "\n" 628 | " >>> doc = Document({'results': {'count': 3, 'rows': [55, 66, 77]}})\n" 629 | " >>> print(doc.dumps(at_pointer='/results/rows'))\n" 630 | " [55,66,77]\n" 631 | "\n" 632 | ":param flags: Flags that control JSON writing behaviour.\n" 633 | ":type flags: :class:`yyjson.WriterFlags`, optional\n" 634 | ":param at_pointer: An optional JSON pointer specifying what part of the\n" 635 | " document should be dumped. If not specified, defaults\n" 636 | " to the entire ``Document``.\n" 637 | ":type at_pointer: str, optional\n" 638 | ":returns: The serialized ``Document``.\n" 639 | ":rtype: ``str``" 640 | ); 641 | static PyObject *Document_dumps( 642 | DocumentObject *self, PyObject *args, PyObject *kwds 643 | ) { 644 | static char *kwlist[] = {"flags", "at_pointer", NULL}; 645 | yyjson_write_flag w_flag = 0; 646 | const char *pointer = NULL; 647 | Py_ssize_t pointer_size; 648 | 649 | if (!PyArg_ParseTupleAndKeywords( 650 | args, kwds, "|$Is#", kwlist, &w_flag, &pointer, &pointer_size 651 | )) { 652 | return NULL; 653 | } 654 | 655 | char *result = NULL; 656 | size_t w_len; 657 | yyjson_write_err w_err; 658 | PyObject *obj_result = NULL; 659 | 660 | if (self->i_doc) { 661 | yyjson_val *val_to_serialize = NULL; 662 | 663 | if (pointer) { 664 | val_to_serialize = 665 | yyjson_doc_ptr_getn(self->i_doc, pointer, pointer_size); 666 | } else { 667 | val_to_serialize = yyjson_doc_get_root(self->i_doc); 668 | } 669 | 670 | result = yyjson_val_write_opts( 671 | val_to_serialize, w_flag, self->alc, &w_len, &w_err 672 | ); 673 | } else { 674 | yyjson_mut_val *mut_val_to_serialize = NULL; 675 | 676 | if (pointer) { 677 | mut_val_to_serialize = 678 | yyjson_mut_doc_ptr_getn(self->m_doc, pointer, pointer_size); 679 | } else { 680 | mut_val_to_serialize = yyjson_mut_doc_get_root(self->m_doc); 681 | } 682 | 683 | result = yyjson_mut_val_write_opts( 684 | mut_val_to_serialize, w_flag, self->alc, &w_len, &w_err 685 | ); 686 | } 687 | 688 | if (yyjson_unlikely(!result)) { 689 | PyErr_SetString(PyExc_ValueError, w_err.msg); 690 | return NULL; 691 | } 692 | 693 | obj_result = PyUnicode_FromStringAndSize(result, w_len); 694 | self->alc->free(NULL, result); 695 | 696 | return obj_result; 697 | } 698 | 699 | PyDoc_STRVAR( 700 | Document_get_pointer_doc, 701 | "Returns the JSON element at the given JSON pointer (RFC 6901).\n" 702 | "\n" 703 | ":param pointer: JSON Pointer to search for.\n" 704 | ":type pointer: ``str``" 705 | ); 706 | static PyObject *Document_get_pointer(DocumentObject *self, PyObject *args) { 707 | char *pointer = NULL; 708 | Py_ssize_t pointer_len; 709 | 710 | if (!PyArg_ParseTuple(args, "s#", &pointer, &pointer_len)) { 711 | return NULL; 712 | } 713 | 714 | yyjson_ptr_err err; 715 | 716 | if (self->i_doc) { 717 | yyjson_val *result = 718 | yyjson_doc_ptr_getx(self->i_doc, pointer, pointer_len, &err); 719 | 720 | if (!result) { 721 | PyErr_SetString( 722 | PyExc_ValueError, err.msg ? err.msg : "Not a valid JSON Pointer" 723 | ); 724 | return NULL; 725 | } 726 | 727 | return element_to_primitive(result); 728 | } else { 729 | yyjson_mut_val *result = 730 | yyjson_mut_doc_ptr_getx(self->m_doc, pointer, pointer_len, NULL, &err); 731 | 732 | if (!result) { 733 | PyErr_SetString( 734 | PyExc_ValueError, err.msg ? err.msg : "Not a valid JSON Pointer" 735 | ); 736 | return NULL; 737 | } 738 | 739 | return mut_element_to_primitive(result); 740 | } 741 | } 742 | 743 | PyDoc_STRVAR( 744 | Document_freeze_doc, 745 | "Freezes the document, copying it into yyjson's read-only internal " 746 | "object.\n" 747 | "\n" 748 | "This object can be used as a normal ``Document`` object, but uses less\n" 749 | "memory after creation, and offers slightly improved performance.\n" 750 | "\n" 751 | ".. note::\n" 752 | "\n" 753 | " If a ``Document`` method that requires mutation is called on a " 754 | "frozen\n" 755 | " ``Document``, such as :func:`patch()`, it will be automatically " 756 | "thawed.\n" 757 | " This is an advanced function and can usually be ignored.\n" 758 | ); 759 | static PyObject *Document_freeze(DocumentObject *self) { 760 | if (self->m_doc) { 761 | self->i_doc = yyjson_mut_doc_imut_copy(self->m_doc, self->alc); 762 | yyjson_mut_doc_free(self->m_doc); 763 | self->m_doc = NULL; 764 | } 765 | 766 | Py_RETURN_NONE; 767 | } 768 | 769 | PyDoc_STRVAR( 770 | Document_thaw_doc, 771 | "Thaws the document, copying it into yyjson's mutable internal object.\n" 772 | "\n" 773 | "This object can be used as a normal ``Document`` object, but will use\n" 774 | "slightly more memory after creation, and offers slightly worse\n" 775 | "performance.\n" 776 | "\n" 777 | ".. note::\n" 778 | "\n" 779 | " This is an advanced function and can usually be ignored.\n" 780 | ); 781 | static PyObject *Document_thaw(DocumentObject *self) { 782 | if (self->i_doc) { 783 | self->m_doc = yyjson_doc_mut_copy(self->i_doc, self->alc); 784 | yyjson_doc_free(self->i_doc); 785 | self->i_doc = NULL; 786 | } 787 | 788 | Py_RETURN_NONE; 789 | } 790 | 791 | PyDoc_STRVAR( 792 | Document_patch_doc, 793 | "Patch a ``Document`` with another ``Document``, using either JSON Patch " 794 | "(RFC 6902)\n" 795 | "or JSON Merge-Patch (RFC 7386).\n" 796 | "\n" 797 | "By default, this will apply a JSON Patch. Specify " 798 | "``use_merge_patch=True`` to\n" 799 | "use JSON Merge-Patch instead.\n" 800 | "\n" 801 | ".. note::\n" 802 | "\n" 803 | " This method will automatically thaw a frozen ``Document``.\n" 804 | "\n" 805 | ":param patch: The ``Document`` to patch with.\n" 806 | ":type patch: ``Document``\n" 807 | ":param at_pointer: The (optional) JSON Pointer (RFC 6901) to patch at,\n" 808 | " instead of patching the entire document.\n" 809 | ":type at_pointer: ``str``\n" 810 | ":param use_merge_patch: Whether to use JSON Merge-Patch (RFC 7386) " 811 | "instead of\n" 812 | " JSON Patch (RFC 6902).\n" 813 | "\n" 814 | ); 815 | static PyObject *Document_patch( 816 | DocumentObject *self, PyObject *args, PyObject *kwds 817 | ) { 818 | // Create a new, essentially empty Document which will serve as the 819 | // container for the patch result. 820 | DocumentObject *obj = (DocumentObject *)PyObject_CallFunction( 821 | (PyObject *)&DocumentType, "(O)", Py_None 822 | ); 823 | Py_INCREF(Py_None); 824 | 825 | if (!obj) { 826 | PyErr_SetString( 827 | PyExc_ValueError, 828 | "Unable to create container Document for results of merge-patch" 829 | ); 830 | return NULL; 831 | } 832 | 833 | static char *kwlist[] = {"patch", "at_pointer", "use_merge_patch", NULL}; 834 | 835 | const char *pointer = NULL; 836 | Py_ssize_t pointer_size; 837 | PyObject *patch = NULL; 838 | int use_merge_patch = false; 839 | 840 | if (!PyArg_ParseTupleAndKeywords( 841 | args, kwds, 842 | /* We can switch the "i" for "p" to be explicit with our bool 843 | * flag, but only after we drop support for everything < 3.3. */ 844 | "O|$z#i", kwlist, &patch, &pointer, &pointer_size, &use_merge_patch 845 | )) { 846 | return NULL; 847 | } 848 | 849 | // If a pointer was provided, that's the value we're going to be patching, 850 | // otherwise we use the root of the document. 851 | if (self->i_doc) { 852 | yyjson_val *original = NULL; 853 | 854 | if (pointer != NULL) { 855 | yyjson_ptr_err ptr_err; 856 | 857 | original = 858 | yyjson_doc_ptr_getx(self->i_doc, pointer, pointer_size, &ptr_err); 859 | if (!original) { 860 | PyErr_SetString( 861 | PyExc_ValueError, 862 | ptr_err.msg ? ptr_err.msg : "Not a valid JSON Pointer" 863 | ); 864 | return NULL; 865 | } 866 | } else { 867 | original = yyjson_doc_get_root(self->i_doc); 868 | if (yyjson_unlikely(!original)) { 869 | PyErr_SetString(PyExc_ValueError, "Document has no root."); 870 | return NULL; 871 | } 872 | } 873 | 874 | if (!PyObject_IsInstance(patch, (PyObject *)&DocumentType)) { 875 | PyErr_SetString(PyExc_TypeError, "Patch must be a Document."); 876 | return NULL; 877 | } 878 | 879 | DocumentObject *patch_doc = (DocumentObject *)patch; 880 | 881 | // If the patch is a mutable document, we need to freeze it before we can 882 | // use it with with the immutable merge_patch API. 883 | if (patch_doc->m_doc) { 884 | patch_doc->i_doc = 885 | yyjson_mut_doc_imut_copy(patch_doc->m_doc, patch_doc->alc); 886 | yyjson_mut_doc_free(patch_doc->m_doc); 887 | patch_doc->m_doc = NULL; 888 | } 889 | 890 | yyjson_val *patch_val = yyjson_doc_get_root(patch_doc->i_doc); 891 | if (!patch_val) { 892 | PyErr_SetString(PyExc_ValueError, "Patch document has no root value."); 893 | return NULL; 894 | } 895 | 896 | yyjson_mut_val *patched_val = NULL; 897 | 898 | if (use_merge_patch) { 899 | patched_val = yyjson_merge_patch(obj->m_doc, original, patch_val); 900 | } else { 901 | yyjson_patch_err patch_err; 902 | 903 | patched_val = yyjson_patch(obj->m_doc, original, patch_val, &patch_err); 904 | 905 | if (!patched_val) { 906 | PyErr_SetString( 907 | PyExc_ValueError, 908 | patch_err.msg ? patch_err.msg : "Unable to apply patch to document." 909 | ); 910 | return NULL; 911 | } 912 | } 913 | 914 | if (!patched_val) { 915 | PyErr_SetString(PyExc_ValueError, "Unable to apply patch to document."); 916 | return NULL; 917 | } 918 | 919 | yyjson_mut_doc_set_root(obj->m_doc, patched_val); 920 | return (PyObject *)obj; 921 | } else { 922 | yyjson_mut_val *original = NULL; 923 | 924 | if (pointer != NULL) { 925 | yyjson_ptr_err ptr_err; 926 | 927 | original = yyjson_mut_doc_ptr_getx( 928 | self->m_doc, pointer, pointer_size, NULL, &ptr_err 929 | ); 930 | if (!original) { 931 | PyErr_SetString( 932 | PyExc_ValueError, 933 | ptr_err.msg ? ptr_err.msg : "Not a valid JSON Pointer" 934 | ); 935 | return NULL; 936 | } 937 | } else { 938 | original = yyjson_mut_doc_get_root(self->m_doc); 939 | if (yyjson_unlikely(!original)) { 940 | PyErr_SetString(PyExc_ValueError, "Document has no root."); 941 | return NULL; 942 | } 943 | } 944 | 945 | DocumentObject *patch_doc = (DocumentObject *)patch; 946 | ENSURE_MUTABLE(patch_doc); 947 | 948 | yyjson_mut_val *patch_val = yyjson_mut_doc_get_root(patch_doc->m_doc); 949 | if (!patch_val) { 950 | PyErr_SetString(PyExc_ValueError, "Patch document has no root value."); 951 | return NULL; 952 | } 953 | 954 | yyjson_mut_val *patched_val; 955 | 956 | if (use_merge_patch) { 957 | patched_val = yyjson_mut_merge_patch(obj->m_doc, original, patch_val); 958 | } else { 959 | yyjson_patch_err patch_err; 960 | 961 | patched_val = 962 | yyjson_mut_patch(obj->m_doc, original, patch_val, &patch_err); 963 | 964 | if (!patched_val) { 965 | PyErr_SetString( 966 | PyExc_ValueError, 967 | patch_err.msg ? patch_err.msg : "Unable to apply patch to document." 968 | ); 969 | return NULL; 970 | } 971 | } 972 | 973 | if (!patched_val) { 974 | PyErr_SetString(PyExc_ValueError, "Unable to apply patch to document."); 975 | return NULL; 976 | } 977 | 978 | yyjson_mut_doc_set_root(obj->m_doc, patched_val); 979 | return (PyObject *)obj; 980 | } 981 | } 982 | 983 | static Py_ssize_t Document_length(DocumentObject *self) { 984 | if (self->i_doc) { 985 | return yyjson_get_len(yyjson_doc_get_root(self->i_doc)); 986 | } else { 987 | return yyjson_mut_get_len(yyjson_mut_doc_get_root(self->m_doc)); 988 | } 989 | } 990 | 991 | static PyMethodDef Document_methods[] = { 992 | {"patch", (PyCFunction)(void (*)(void))Document_patch, 993 | METH_VARARGS | METH_KEYWORDS, Document_patch_doc}, 994 | {"dumps", (PyCFunction)(void (*)(void))Document_dumps, 995 | METH_VARARGS | METH_KEYWORDS, Document_dumps_doc}, 996 | {"get_pointer", (PyCFunction)(void (*)(void))Document_get_pointer, 997 | METH_VARARGS, Document_get_pointer_doc}, 998 | {"freeze", (PyCFunction)(void (*)(void))Document_freeze, METH_NOARGS, 999 | Document_freeze_doc}, 1000 | {"thaw", (PyCFunction)(void (*)(void))Document_thaw, METH_NOARGS, 1001 | Document_thaw_doc}, 1002 | {NULL} /* Sentinel */ 1003 | }; 1004 | 1005 | static PyGetSetDef Document_members[] = { 1006 | {"as_obj", (getter)Document_as_obj, NULL, 1007 | "Converts the Document to a native Python object, such as a ``dict`` or " 1008 | "``list``.", 1009 | NULL}, 1010 | {"is_thawed", (getter)Document_is_thawed, NULL, 1011 | "Returns whether the Document is thawed/mutable.", NULL}, 1012 | {NULL} /* Sentinel */ 1013 | }; 1014 | 1015 | static PyMappingMethods Document_mapping_methods = { 1016 | (lenfunc)Document_length, NULL, NULL}; 1017 | 1018 | PyTypeObject DocumentType = { 1019 | PyVarObject_HEAD_INIT(NULL, 0).tp_name = "cyyjson.Document", 1020 | .tp_doc = Document_init_doc, 1021 | .tp_basicsize = sizeof(DocumentObject), 1022 | .tp_itemsize = 0, 1023 | .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, 1024 | .tp_new = Document_new, 1025 | .tp_init = (initproc)Document_init, 1026 | .tp_dealloc = (destructor)Document_dealloc, 1027 | .tp_getset = Document_members, 1028 | .tp_as_mapping = &Document_mapping_methods, 1029 | .tp_methods = Document_methods}; 1030 | -------------------------------------------------------------------------------- /yyjson/document.h: -------------------------------------------------------------------------------- 1 | #ifndef PY_YYJSON_DOCUMENT_H 2 | #define PY_YYJSON_DOCUMENT_H 3 | 4 | #define PY_SSIZE_T_CLEAN 5 | #include 6 | 7 | #include "yyjson.h" 8 | 9 | /** 10 | * Represents a single yyjson document. 11 | */ 12 | typedef struct { 13 | PyObject_HEAD 14 | /** A mutable document. */ 15 | yyjson_mut_doc* m_doc; 16 | /** An immutable document. */ 17 | yyjson_doc* i_doc; 18 | /** The memory allocator in use for this document. */ 19 | yyjson_alc* alc; 20 | /** default callback for serializing unknown types. */ 21 | PyObject* default_func; 22 | } DocumentObject; 23 | 24 | extern PyTypeObject DocumentType; 25 | 26 | #endif -------------------------------------------------------------------------------- /yyjson/memory.c: -------------------------------------------------------------------------------- 1 | #include "memory.h" 2 | 3 | /** wrapper to use PyMem_Malloc with yyjson's allocator. **/ 4 | void* py_malloc(void* ctx, size_t size) { return PyMem_Malloc(size); } 5 | 6 | /** wrapper to use PyMem_Realloc with yyjson's allocator. **/ 7 | void* py_realloc(void* ctx, void* ptr, size_t old_size, size_t size) { 8 | return PyMem_Realloc(ptr, size); 9 | } 10 | 11 | /** wrapper to use PyMem_Free with yyjson's allocator. **/ 12 | void py_free(void* ctx, void* ptr) { PyMem_Free(ptr); } 13 | 14 | yyjson_alc PyMem_Allocator = {py_malloc, py_realloc, py_free, NULL}; 15 | -------------------------------------------------------------------------------- /yyjson/memory.h: -------------------------------------------------------------------------------- 1 | #ifndef PY_YYJSON_MEMORY_H 2 | #define PY_YYJSON_MEMORY_H 3 | 4 | #define PY_SSIZE_T_CLEAN 5 | #include 6 | 7 | #include "yyjson.h" 8 | 9 | /** wrapper to use PyMem_Malloc with yyjson's allocator. **/ 10 | void* py_malloc(void* ctx, size_t size); 11 | 12 | /** wrapper to use PyMem_Realloc with yyjson's allocator. **/ 13 | void* py_realloc(void* ctx, void* ptr, size_t old_size, size_t size); 14 | 15 | /** wrapper to use PyMem_Free with yyjson's allocator. **/ 16 | void py_free(void* ctx, void* ptr); 17 | 18 | extern yyjson_alc PyMem_Allocator; 19 | 20 | #endif 21 | --------------------------------------------------------------------------------