[py]yyjson

Python bindings to the fantastic yyjson project. This module provides a fast, flexible, portable, and correct JSON parser and serializer.

GitHub Sponsors PyPI - License PyPI - Version

Features

  • Fast: yyjson is several times faster than the builtin JSON module, and is faster than most other JSON libraries.

  • Flexible: Parse JSON with strict specification compliance, or with extensions such as comments, trailing commas, Inf/NaN, numbers of any size, and more.

  • Lightweight: yyjson is a lightweight project dependency with low maintenance overhead. It’s written in C, and has no dependencies other than a C89 compiler. Built wheels are between 50kb and 800kb depending on the platform.

  • Portable: Binary wheels are available for many versions of Python on many architectures, such as x86, x86_64, ARM, and ARM64, PowerPC, IBM Z, and more. PyPy is also supported. Supports Python 3.9 and newer.

  • Manipulate documents: The fastest JSON Merge-Patch (RFC 7386), JSON Patch (RFC 6902), and JSON Pointer (RFC 6901) implementations available for Python allow you to manipulate JSON documents without deserializing them into Python objects.

  • Traceable: yyjson uses Python’s memory allocator by default, so you can trace memory leaks and other memory issues using Python’s built-in tools.

Installation

If binary wheels are available for your platform, you can install the latest version of yyjson with pip:

pip install yyjson

If you want to build from source, or if binary wheels aren’t available, you’ll just need a C89 compiler, such as GCC or Clang.

Or you can install the latest development version from GitHub:

pip install git+https://github.com/tktech/py_yyjson.git

Benchmarks

You can find the benchmarks for this project in its sister project, json_benchmark. yyjson compares favourably, and often beats, most other libraries.

Examples

Parsing

Parse a JSON document from a file:

>>> from pathlib import Path
>>> from yyjson import Document
>>> doc = Document(Path("canada.json")).as_obj
>>> doc
{'type': 'FeatureCollection', 'features': [...], 'bbox': [...], 'crs': {...}}

Parse a JSON document from a string:

>>> from yyjson import Document
>>> doc = Document('{"hello": "world"}').as_obj
>>> doc
{'hello': 'world'}

Parse a JSON document from a bytes object:

>>> from yyjson import Document
>>> doc = Document(b'{"hello": "world"}').as_obj
>>> doc
{'hello': 'world'}

Load part of a document

When you only need a small part of a document, you can use a JSON Pointer to extract just the part you actually need. This can be a massive performance improvement when working with large JSON documents, as most of the time spent parsing JSON in Python is spent just creating the Python objects!

>> from pathlib import Path
>> from yyjson import Document
>>> doc = Document(Path("canada.json"))
>>> features = doc.get_pointer("/features")

Patch a document

JSON manipulation operations are supported, such as JSON Merge-Patch and JSON Patch. These operations allow you to manipulate JSON documents without deserializing them into Python objects at all.

For example, lets add an entry to a GeoJSON file without deserializing the entire document into Python objects using JSON Patch:

from pathlib import Path
from yyjson import Document

doc = Document(Path("canada.json")
patch = Document([
    {'op': 'add', 'path': '/features/-', 'value': {
        'type': 'Feature',
        'geometry': {
            'type': 'Point',
            'coordinates': [1, 2],
        },
        'properties': {
            'name': 'New Feature',
        },
    }},
])
modified = doc.patch(patch)
print(modified.dumps())

Serialize an object

Serialize a Python object to JSON:

>>> from yyjson import Document
>>> doc = Document({
...   "hello": "world",
...   "foo": [1, 2, 3],
...   "bar": {"a": 1, "b": 2}
... })
>>> doc.dumps()
'{"hello":"world","foo":[1,2,3],"bar":{"a":1,"b":2}}'

Customizing JSON Reading & Writing

You can customize the JSON reading and writing process using yyjson.ReaderFlags and yyjson.WriterFlags. For example if we wanted to allow comments and trailing commas, we could do:

>>> from yyjson import Document, ReaderFlags, WriterFlags
>>> doc = Document(
...   '{"hello": "world",} // This is a comment',
...   ReaderFlags.ALLOW_COMMENTS | ReaderFlags.ALLOW_TRAILING_COMMAS
... )

Likewise we can customize the writing process:

>>> from yyjson import Document, ReaderFlags, WriterFlags
>>> doc = Document({
...   "hello": "world"
... })
>>> doc.dumps(flags=WriterFlags.PRETTY_TWO_SPACES)

Reading Huge Numbers

If you’re reading huge floats/doubles or require perfect precision, you can tell yyjson to read them as Decimals:

>>> from yyjson import Document, ReaderFlags
>>> float('1.7976931348623157e+310')
inf
>>> doc = Document(
...   '{"huge": 1.7976931348623157e+310}',
...   flags=ReaderFlags.NUMBERS_AS_DECIMAL
... )
>>> print(doc.get_pointer('/huge'))
1.7976931348623157E+310

Or use ReaderFlags.BIG_NUMBERS_AS_DECIMAL to only read numbers that are too large for Python’s float type as Decimals:

>>> from yyjson import Document, ReaderFlags
>>> doc = Document(
    '{"huge": 1.7976931348623157e+310, "small": 1.0}',
    flags=ReaderFlags.BIG_NUMBERS_AS_DECIMAL
)
>>> type(doc.get_pointer('/huge'))
<class 'decimal.Decimal'>
>>> type(doc.get_pointer('/small'))
<class 'float'>