Skip to content

reers/ReerJSON

Repository files navigation

ReerJSON

A faster version of JSONDecoder & JSONEncoder powered by yyjson

Coverage: 88% SwiftPM compatible

ReerJSON is a really fast JSON parser, and it's inspired by ZippyJSON and Ananda.

⚠️Important: When measuring the performance of Swift libraries, make sure you're building in Release Mode. When building Swift code on DEBUG compilation, it can be 10-20x slower than equivalent code on RELEASE.

Benchmarks

JSONDecoder

iOS 17+

CleanShot 2025-09-18 at 14 31 41@2x

CleanShot 2025-09-18 at 14 33 30@2x

Lower than iOS 17

CleanShot 2025-09-18 at 14 37 04@2x

macOS

CleanShot 2025-09-18 at 13 40 05@2x

JSONEncoder

ios1 CleanShot 2026-04-15 at 12 43 58@2x CleanShot 2026-04-15 at 12 46 05@2x

On older/lower-end chips (e.g. A11), yyjson's "build DOM tree then serialize" approach is bottlenecked by smaller caches and weaker branch prediction — exactly the hardware traits yyjson's README states it depends on ("high instruction level parallelism, excellent branch predictor"). IkigaJSON's streaming byte-buffer design avoids building an intermediate tree, making it less sensitive to these hardware limitations and thus faster in certain scenarios on constrained processors

macOS

macos

Tested with ReerJSON 1.0.0, ZippyJSON 1.2.15, IkigaJSON 2.3.2

Code for Benchmarks

Installation

Swift Package Manager

Add dependency in Package.swift or project Package Dependencies

.package(url: "https://github.com/reers/ReerJSON.git", from: "1.0.0"),

Depend on ReerJSON in your target.

.product(name: "ReerJSON", package: "ReerJSON" ),

Usage

Decoder && Encoder

ReerJSONDecoder and ReerJSONEncoder are API-compatible replacements for Foundation's JSONDecoder and JSONEncoder. Simply swap the type and add the import, no other code changes required:

import ReerJSON

// Before
let decoder = JSONDecoder()
let encoder = JSONEncoder()

// After
let decoder = ReerJSONDecoder()
let encoder = ReerJSONEncoder()

All public interfaces, behaviors, error types, and coding strategies are identical to the Foundation counterparts. The ReerJSON test suite includes exhaustive test cases covering every feature, ensuring full compatibility.

DOM-Style Access

Parse JSON and access values directly without defining types:

import ReerJSON

let json = #"{"users": [{"name": "Alice"}, {"name": "Bob"}]}"#
let value = try JSONValue(string: json)

// Access nested values with subscripts
if let name = value["users"]?[0]?["name"]?.string {
    print(name) // "Alice"
}

In-Place Parsing

For maximum performance with large JSON files, use in-place parsing to avoid copying the input data:

var data = try Data(contentsOf: fileURL)
let json = try JSONValue.parseInPlace(consuming: &data)
// `data` is now consumed and should not be used

In-place parsing allows yyjson to parse directly within the input buffer, avoiding memory allocation for string storage. The inout parameter makes it clear that the data is consumed by this operation.

Note

For most use cases, the standard YYJSONValue(data:) initializer is sufficient. Use in-place parsing only when performance is critical and you can accept the ownership semantics.

JSONSerialization Alternative

Use ReerJSONSerialization with the same API as Foundation's JSONSerialization:

import ReerJSON

let json = #"{"message": "Hello, World!"}"#
let data = json.data(using: .utf8)!

let object = try ReerJSONSerialization.jsonObject(with: data)
if let dict = object as? [String: Any] {
    print(dict["message"] as? String ?? "") // "Hello, World!"
}

Configure output formatting with WritingOptions:

// Pretty printing with 2-space indent (useful for Xcode asset catalogs)
let data = try ReerJSONSerialization.data(
    withJSONObject: dict,
    options: [.indentationTwoSpaces, .sortedKeys]
)

// ASCII-only output with trailing newline
let data = try ReerJSONSerialization.data(
    withJSONObject: dict,
    options: [.escapeUnicode, .newlineAtEnd]
)

Differences

Except for the items listed below, ReerJSON behaves exactly the same as Foundation—every capability, every thrown error, and every edge case is covered by a comprehensive test suite.

Decoder

Decoder Diff Foundation ReerJSON
JSON5
assumesTopLevelDictionary
Infinity and NaN ±Infinity, ±NaN ±Infinity, ±NaN, ±Inf and case-insensitive. See details

Encoder

Encoder Diff Foundation ReerJSON
Unicode escape casing \u001f (lowercase) \u001F (uppercase). Both are valid JSON per RFC 8259
Pretty-print colon "key" : value (space before and after colon) "key": value (space after colon only)

License

This project is licensed under the MIT License. Portions of this project incorporate code from the following source code or test code:

See the LICENSE file for the full text of both licenses.

Acknowledgments

We would like to express our gratitude to the following projects and their contributors:

  • ibireme/yyjson - For providing the high-performance JSON parsing library that powers ReerJSON.
  • swiftlang/swift-foundation - For implementation reference and comprehensive test suites that helped ensure compatibility.
  • michaeleisel/ZippyJSON - For the innovative Swift JSON parsing approach and valuable test cases.
  • michaeleisel/JJLISO8601DateFormatter - For the high-performance date formatting implementation.
  • mattt/swift-yyjson - For the JSONSerialization replacement and DOM-style JSONValue/JSONDocument APIs. The ReerJSONSerialization, Value, Configuration, Error, and Helpers source files and their tests are adapted from this project.
  • nixzhu/Ananda - For the pioneering work in integrating yyjson with Swift and providing architectural inspiration.

Special thanks to all the open-source contributors who made this project possible.

About

A faster version of JSONDecoder and JSONEncoder powered by yyjson

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages