Framework
SBJson/SBJson avatar
SBJson/SBJson

SBJson 5 review: chunk-based JSON parsing in Objective-C

This framework implements a strict JSON parser and generator in Objective-C.

3,715 stars674 forksObjective-CBSD-3-Clause

At a glance

What is it?
SBJson 5 parses UTF-8 JSON in chunks and hands each root-level document to a block, which suits streamed feeds and oversized arrays. The README also states the project is inactive, so adoption is a judgement about a stable, frozen codebase.
Who is it for?
Adopt SBJson 5 when you need incremental Objective-C parsing of a stream or a very large top-level array, and you accept that the README labels the project inactive with maintenance only as time allows. Do not adopt it if you need a parser that validates the whole document before returning values, or if you want an actively developed dependency; the last push was on 2026-05-18, and the release before v5.0.4 was v5.0.3 in 2020.
Can I use it commercially?
Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 135 days ago.
What is it written in?
Mainly Objective-C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem SBJson 5 targets: data that arrives before the document ends

A conventional JSON parser wants the complete document in memory, then returns one object graph. That model breaks down in two situations the SBJson README names directly. The first is a slow connection where a large document downloads over time: a whole-document parser cannot produce anything until the last byte lands. The second is a document too large to keep in memory at all. SBJson 5 is built around the opposite assumption. The README describes its number one feature as stream and chunk-based operation: you feed the parser one or more chunks of UTF-8 data and it calls a block you supply for each root-level document or array, or optionally for each top-level entry in each root-level array.

The intended audience is Objective-C code that consumes line-delimited JSON feeds, long arrays, or progressive downloads. The README gives Twitter's feed as the example of one JSON document per line. If your application already holds a small, fully downloaded JSON blob and just wants a dictionary, the chunk machinery is overhead you do not need. The value only appears when latency or memory pressure is real.

How the chunk parser works: blocks, return codes and three parser variants

The mechanism is a parser object that accumulates state across calls. You construct it with a value block and an error block, then call parse: with each chunk of NSData. The return value tells you where the parser stands: the README's example shows SBJson5ParserWaitingForData after a chunk that leaves the document open, and SBJson5ParserComplete once the closing bracket arrives. The block is invoked during the parse: call, before it returns.

Three factory methods cover different input shapes. parserWithBlock:errorHandler: handles a single root document. multiRootParserWithBlock:errorHandler: treats the input as many consecutive documents, which is what a per-line feed looks like. unwrapRootArrayParserWithBlock:errorHandler: takes a single top-level array and calls the block once per element, so a gigantic array can be processed item by item without materialising the whole thing. JSON types map onto Objective-C types in a fixed table: null to NSNull, string to NSString, array to NSMutableArray, object to NSMutableDictionary, true and false to NSNumber booleans, and numbers to NSNumber. The README notes that integers use long long or unsigned long long when they fit, to avoid rounding errors, while all other numbers go through double with the rounding that implies.

The design has a cost the README states plainly. Because values are handed over before the document is fully seen, parts of a document can be returned as if they were correct and then a later part turns out to be malformed. A whole-document parser cannot make that mistake. This is the central trade-off, and it is an architectural consequence rather than a bug.

Installing SBJson 5 and parsing your first chunked document

The repository ships two podspecs, SBJson.podspec and SBJson5.podspec, and the README carries a Carthage compatibility badge. The README does not spell out a step-by-step install, so the podspec files and the Xcode project are the places to look. The README's own minimal example defines a value block and an error block, then creates a parser:

objc
SBJson5ValueBlock block = ^(id v, BOOL *stop) {
    BOOL isArray = [v isKindOfClass:[NSArray class]];
    NSLog(@"Found: %@", isArray ? @"Array" : @"Object");
};

SBJson5ErrorBlock eh = ^(NSError* err) {
    NSLog(@"OOPS: %@", err);
    exit(1);
};

Feed it a partial document and the block stays silent. The README's example parses the bytes of "[true," and the call returns SBJson5ParserWaitingForData, because the array is not closed. Send the remainder and the block fires once, with the parse: call returning SBJson5ParserComplete. That two-call sequence is the whole idea in miniature: state lives in the parser, and your block is the only place results appear.

If your input is a per-line feed rather than one document, swap the factory for the multi-root variant:

objc
id parser = [SBJson5Parser multiRootParserWithBlock:block
                                       errorHandler:eh];

The README's own example feeds the four bytes "[]{}" twice and reports the block firing four times, once for each document.

Where SBJson 5 is the wrong tool

The streaming trade-off is the first limitation, and it is not hypothetical: any consumer that must reject a document as a unit before acting on it is a poor fit. If you write rows to a database as the block fires and the tail of the document is malformed, you have already committed partial work. SBJson 5 gives you no rollback story here, and the README does not document one.

There is a second constraint in the feature list: a maximum nesting level for all input, defaulting to 32 and configurable. Deeply nested structures beyond that depth are rejected rather than parsed. For typical API payloads this is invisible, but generated or adversarial input can exceed it, and raising the limit trades safety for reach.

Number handling is the third edge. Integers that fit in long long or unsigned long long round-trip exactly; everything else goes through double. A parser that preserves decimal precision by default, or one that can be configured for arbitrary-precision numbers, would be the better choice for financial or scientific payloads where the README's stated rounding errors matter.

The project's own status is the fourth consideration. The README carries a repostatus badge reading Inactive, described as having reached a stable, usable state with support and maintenance provided as time allows. The last push was on 2026-05-18, and the release before v5.0.4 (2026-05-15) was v5.0.3 in January 2020. Treat the codebase as frozen and expect to own your own fixes.

SBJson 5 against NSJSONSerialization

The obvious alternative in an Objective-C or Swift project is NSJSONSerialization, the JSON support built into Foundation. The difference is architectural rather than cosmetic. NSJSONSerialization takes complete NSData or a stream and produces a finished object graph; it is a whole-document parser, so it validates the entire payload before you receive anything. That is exactly the correctness property the SBJson README warns you give up.

The trade runs the other way for size and latency. NSJSONSerialization has no equivalent of unwrapRootArrayParserWithBlock:, so a single top-level array of millions of entries becomes one NSMutableArray in memory. SBJson 5's unwrap variant hands you one element at a time. For a line-delimited feed, multiRootParserWithBlock: also has no direct counterpart in Foundation.

SBJson 5 adds two writer options Foundation does not expose in the same way: sorting dictionary keys so output is consistent across writes, and human-readable output with newlines and indents. The README also notes that v3, v4 and v5 can be installed side by side in one application, because public symbols carry the major version number. That matters for a large app that cannot migrate every call site at once, and it is a concrete reason to pick SBJson over the built-in parser even where both would work.

Fuzzing, licence and the cost of a frozen dependency

The README reports a fuzzing run: AFL++ 4.34c on the sbjson binary for over 24 hours, with no crashes found, and the author notes the hangs reported from manual parsing attempts could not be reproduced. The repository includes fuzz.sh and shell.nix to repeat it, with the shell building AFL++ from source so it runs on macOS without the System V IPC issues the packaged version has. This is the strongest maintenance signal available, and it is a one-off exercise rather than a continuous pipeline. It is also not a correctness proof for your inputs.

Upgrade cost is low in one direction and high in another. Low, because the API surface is small and the versioning scheme lets v3, v4 and v5 coexist, so migration can be incremental. High, because between v5.0.3 in 2020 and v5.0.4 in May 2026 there were roughly six years with no release, and the v5.0.4 notes describe only miscellaneous bug fixes. If you hit a defect, the fix is yours to write and carry.

The licence is BSD-3-Clause, a permissive licence. That generally allows use in closed-source products provided the copyright notice and licence text are retained, but the obligations depend on how you distribute the binary. Read the LICENSE file in the repository and get your own legal review; nothing here is legal advice.

Editorial conclusion

Adopt SBJson 5 when you need incremental Objective-C parsing of a stream or a very large top-level array, and you accept that the README labels the project inactive with maintenance only as time allows. Do not adopt it if you need a parser that validates the whole document before returning values, or if you want an actively developed dependency; the last push was on 2026-05-18, and the release before v5.0.4 was v5.0.3 in 2020. Verify first that the streaming trade-off is acceptable for your data, that your input respects the default max nesting level of 32, and that the BSD-3-Clause licence notice is carried into your distribution.

Frequently asked questions

What does a JSON parser like SBJson 5 actually do?

It turns UTF-8 JSON text into Objective-C objects: null becomes NSNull, strings become NSString, arrays become NSMutableArray, objects become NSMutableDictionary, booleans become NSNumber, and numbers become NSNumber. SBJson 5 does this incrementally, calling a block you supply as documents complete.

Is SBJson 5 still maintained?

The README carries a repostatus badge reading Inactive, saying the project has reached a stable, usable state with support and maintenance provided as time allows. The last push was on 2026-05-18, and the release before v5.0.4 was v5.0.3 in January 2020.

How do I install SBJson 5 in an iOS or macOS project?

The repository ships SBJson.podspec and SBJson5.podspec for CocoaPods and the README carries a Carthage compatibility badge. The README does not give a full step-by-step install, so the podspec files and the Xcode project are the places to check.

Can SBJson 5 parse a JSON array that is too large to fit in memory?

Yes. The unwrapRootArrayParserWithBlock:errorHandler: factory takes a single top-level array and calls your block once per element, so you do not have to hold the whole array. The multi-root variant does the same for streams of consecutive documents, such as one JSON document per line.

What are the risks of streaming JSON parsing with SBJson 5?

The README warns that some parts of a document can be returned as if they were correct before a later part turns out to be malformed, because the parser does not see the whole input first. There is also a maximum nesting level, defaulting to 32 and configurable, beyond which input is rejected.

Official sources

  1. Issues
  2. License: BSD-3-Clause
  3. README
  4. Releases
  5. SBJson/SBJson on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/sbjson-sbjson.svg)](https://hysenlabs.com/projects/sbjson-sbjson)