zzzJSON

December 25, 2020 · View on GitHub

简体中文

Abstract

  • Fastest parsing and serialization speed.

  • Wrote by pure C, no ambiguous grammar, easy to understand, compile, use and modify.

  • Only one header file.

  • Tencent is used on a large scale.

Parse on use

  • Like copy on write, zzzJSON only parse numbers when using get APIs.
  • zzzJSON only judge whether it is a correct number string when parsing JSON string into tree structure.

Benchmarks and Measurements

Simulate nativejson-benchmark

Conformance

BenchmarkDescription
Parse ValidationUse JSON_checker test suite to test whether the library can identify valid and invalid JSONs. (fail01.json is excluded as it is relaxed in [RFC7159]. fail18.json is excluded as depth of JSON is not specified.)
Parse Double66 JSONs, each with a decimal value in an array, are parsed. The parsed double values are compared to the correct answer.
Parse String9 JSONs, each with a string value in an array, are parsed. The parsed strings are compared to the correct answer.
Roundtrip27 condensed JSONs are parsed and stringified. The results are compared to the original JSONs.

Performance

BenchmarkDescription
ParseParse in-memory JSON into tree structure.
StringifySerialize tree structure into condensed JSON in memory.

Results

environment

CPUOSCompilerOptimization
i7-6700 (3.40GHZ)Ubuntu 18.04(WSL)G++-7.3O3

conformance

zzzJSON pass all tests.(soure code: conformance_test.cpp)

performance

No Number JSON

NameParse(ms)Stringify(ms)All(ms)
cjson162122284
gason60321398
jsoncpp8305561588
nlohmannjson9943121414
picojson8242951214
rapidjson12892224
rapidjsonfp14791245
rapidjsonstr12789225
taocppjson7242611070
zzzjson8656149

JSON From nativejson-benchmark

NameParse(ms)Stringify(ms)All(ms)
arduinojson77925807
cjson53183230
gason88893
jsoncpp88126221
nlohmannjson622491
parson52277336
picojson54108165
rapidjson111523
rapidjsonfp201739
rapidjsonstr241237
taocppjson342966
zzzjson18722

JSON From Taobao

NameParse(ms)Stringify(ms)All(ms)
arduinojson236287
cjson382360
gason95163
jsoncpp6751126
nlohmannjson7229105
parson4885141
picojson543596
rapidjson181228
rapidjsonfp181430
rapidjsonstr161229
taocppjson472886
zzzjson14723

All JSON Above

NameParse(ms)Stringify(ms)All(ms)
arduinojson20565052508
cjson243345596
gason63431500
jsoncpp451431961
nlohmannjson518217840
parson3098571214
picojson405387862
rapidjson97119220
rapidjsonfp113126245
rapidjsonstr116121240
taocppjson278207561
zzzjson9559166

Random Short JSON

NameParse(ms)Stringify(ms)All(ms)
cjson100107200
jsoncpp136127284
nlohmannjson17758242
picojson9495205
rapidjson313956
rapidjsonfp361963
rapidjsonstr373560
taocppjson9050143
zzzjson261638

Random Long JSON

NameParse(ms)Stringify(ms)All(ms)
cjson8167155
jsoncpp130112244
nlohmannjson19942254
picojson12793222
rapidjson241745
rapidjsonfp282446
rapidjsonstr302448
taocppjson10034149
zzzjson20929

You can use the commands below to get the results above

./build.sh all

Example

#include "zzzjson.h"
#include <stdio.h>

// DeepCopy
void GetAndSet(Value *srcv, Value *desv)
{
    // GetType
    const JSONType *t;
    t = Type(srcv);
    if (t == 0)
        return;
    switch (*t)
    {
    case JSONTYPEARRAY:
    {
        // DeepCopy Array
        SetArray(desv);
        Value *next = Begin(srcv);
        while (next != 0)
        {
            Value *v = NewValue(desv->A);
            GetAndSet(next, v);
            if (ArrayAddFast(desv, v) != True)
                return;
            next = Next(next);
        }
        break;
    }
    case JSONTYPEOBJECT:
    {
        // DeepCopy Object
        SetObj(desv);
        Value *next = Begin(srcv);
        while (next != 0)
        {
            Value *v = NewValue(desv->A);
            SetKeyFast(v, GetKey(next));
            GetAndSet(next, v);
            if (ObjAddFast(desv, v) != True)
                return;
            next = Next(next);
        }
        break;
    }
    case JSONTYPEBOOL:
    {
        // Copy Bool 
        const bool *b = GetBool(srcv);
        if (b == 0)
            return;
        SetBool(desv, *b);
        break;
    }
    case JSONTYPENULL:
    {
        // Copy nil
        if (IsNull(srcv) == False)
            return;
        SetNull(desv);
        break;
    }

    case JSONTYPESTRING:
    {
        // DeepCopy String
        const char *str = GetStr(srcv);
        if (str == 0)
            return;
        if (SetStrFast(desv, str) != True)
            return;
        break;
    }
    case JSONTYPENUMBER:
    {
        // DeepCopy Number
        const char *str = GetNumStr(srcv);
        if (str == 0)
            return;
        if (SetNumStrFast(desv, str) != True)
            return;
        break;
    }
    }
}
int main()
{
    const char *src_json = "[{\"key\":true},false,{\"key1\":true},[null,false,[],true],[\"\",123,\"str\"],null]";
    // New Allocator
    Allocator *A = NewAllocator();
    // New Value
    Value *src_v = NewValue(A);
    Value *des_v = NewValue(A);
    // Parse
    BOOL ret = ParseFast(src_v, src_json);
    if (ret != True)
    {
        printf("ParseFast Fail!\n");
        return 1;
    }
    // DeepCopy
    GetAndSet(src_v, des_v);
    // Stringify
    const char *des_json = Stringify(des_v);
    printf("src_json:%s\n", src_json);
    if (des_json != 0)
        printf("des_json:%s\n", des_json);
    // Free Allocator
    ReleaseAllocator(A);
    return 0;
}

API

Short APIs

Allocator *NewAllocator();
void ReleaseAllocator(Allocator *root_alloc);

Value *NewValue(Allocator *alloc);
BOOL ParseFast(Value *v, const char *s);
BOOL ParseLen(Value *v, const char *s, SIZE len);
BOOL Parse(Value *v, const char *s);
const char *Stringify(const Value *v);

const char *GetStrFast(const Value *v, SIZE *len);
const char *GetUnEscapeStr(Value *v);
const char *GetStr(Value *v);
const char *GetNumFast(const Value *v, zj_SIZE *len);
const char *GetNumStr(Value *v);
const double *GetNum(Value *v);
const double *GetDouble(struct zj_Value *v);
const int *GetInt(struct zj_Value *v);
const long *GetLong(struct zj_Value *v);
const long long *GetLongLong(struct zj_Value *v);
const BOOL *GetBool(const Value *v);
BOOL IsNull(const Value *v);
const char *GetKey(Value *v);
const char *GetUnEscapeKey(Value *v);
const char *GetKeyFast(const Value *v, SIZE *len);
Value *ObjGet(const Value *v, const char *key);
Value *ObjGetLen(const Value *v, const char *key, SIZE len);
const JSONType *Type(const Value *v);
SIZE Size(const Value *v);
Value *ArrayGet(const Value *v, SIZE index);
Value *Begin(const Value *v);
Value *Next(const Value *v);

Value *Copy(const Value *v);
BOOL Move(Value *v);

BOOL SetNull(Value *v);
BOOL SetBool(Value *v, BOOL b);
BOOL SetNumStrFast(Value *v, const char *num);
BOOL SetNumStrLenFast(Value *v, const char *num, SIZE len);
BOOL SetNumStr(Value *v, const char *num);
BOOL SetNumStrLen(Value *v, const char *num, SIZE len);
BOOL SetNum(Value *v, const double d);
BOOL SetDouble(Value *v, const double d);
BOOL SetInt(Value *v, const int d);
BOOL SetLong(Value *v, const long d);
BOOL SetLongLong(Value *v, const long long d);
BOOL SetStrFast(Value *v, const char *str);
BOOL SetStrLenFast(Value *v, const char *str, SIZE len);
BOOL SetStr(Value *v, const char *str);
BOOL SetStrLen(Value *v, const char *str, SIZE len);
BOOL SetStrEscape(Value *v, const char *str);
BOOL SetStrLenEscape(Value *v, const char *str, SIZE len);
BOOL SetKeyFast(Value *v, const char *key);
BOOL SetKeyLenFast(Value *v, const char *key, SIZE len);
BOOL SetKey(Value *v, const char *key);
BOOL SetKeyLen(Value *v, const char *key, SIZE len);
BOOL SetKeyEscape(Value *v, const char *key);
BOOL SetKeyLenEscape(Value *v, const char *key, SIZE len);
BOOL SetArray(Value *v);
BOOL SetObj(Value *v);
BOOL SetFast(Value *v, Value *vv);
BOOL Set(Value *v, const Value *vv);
BOOL ObjAddFast(Value *v, Value *vv);
BOOL ObjAdd(Value *v, const Value *vv);
BOOL ArrayAddFast(Value *v, Value *vv);
BOOL ArrayAdd(Value *v, const Value *vv);

BOOL ArrayDel(Value *v, SIZE index);
BOOL ObjDel(Value *v, const char *key);

Long APIs

define “zj_SHORT_API 0” to use long APIs

#define zj_SHORT_API 0

// must be above to include

#include "zzzjson.h"

struct zj_Allocator *zj_AllocatorNew();
void zj_ReleaseAllocator(struct zj_Allocator *root_alloc);

struct zj_Value *zj_NewValue(struct zj_Allocator *alloc);
bool zj_ParseFast(struct zj_Value *v, const char *s);
bool zj_ParseLen(struct zj_Value *v, const char *s, zj_SIZE len);
bool zj_Parse(struct zj_Value *v, const char *s);
const char *zj_Stringify(const struct zj_Value *v);

const char *zj_GetStrFast(const struct zj_Value *v, zj_SIZE *len);
const char *zj_GetUnEscapeStr(struct zj_Value *v);
const char *zj_GetStr(struct zj_Value *v);
const char *zj_GetNumFast(const struct zj_Value *v, zj_SIZE *len);
const char *zj_GetNumStr(struct zj_Value *v);
const double *zj_GetNum(struct zj_Value *v);
const double *zj_GetDouble(struct zj_Value *v);
const int *zj_GetInt(struct zj_Value *v);
const long *zj_GetLong(struct zj_Value *v);
const long long *zj_GetLongLong(struct zj_Value *v);
const bool *zj_GetBool(const struct zj_Value *v);
bool zj_IsNull(const struct zj_Value *v);
const char *zj_GetKey(struct zj_Value *v);
const char *zj_GetUnEscapeKey(struct zj_Value *v);
const char *zj_GetKeyFast(const struct zj_Value *v, zj_SIZE *len);
struct zj_Value *zj_ObjGet(const struct zj_Value *v, const char *key);
struct zj_Value *zj_ObjGetLen(const struct zj_Value *v, const char *key, zj_SIZE len);
const zj_JSONType *zj_Type(const struct zj_Value *v);
zj_SIZE zj_SizeOf(const struct zj_Value *v);
struct zj_Value *zj_ArrayGet(const struct zj_Value *v, zj_SIZE index);
struct zj_Value *zj_Begin(const struct zj_Value *v);
struct zj_Value *zj_Next(const struct zj_Value *v);

struct zj_Value *zj_Copy(const struct zj_Value *v);
bool zj_Move(struct zj_Value *v);

bool zj_SetNull(struct zj_Value *v);
bool zj_SetBool(struct zj_Value *v, bool b);
bool zj_SetNumStrFast(struct zj_Value *v, const char *num);
bool zj_SetNumStrLenFast(struct zj_Value *v, const char *num, zj_SIZE len);
bool zj_SetNumStr(struct zj_Value *v, const char *num);
bool zj_SetNumStrLen(struct zj_Value *v, const char *num, zj_SIZE len);
bool zj_SetNum(struct zj_Value *v, const double d);
bool zj_SetDouble(struct zj_Value *v, const double d);
bool zj_SetInt(struct zj_Value *v, const int n);
bool zj_SetLong(struct zj_Value *v, const long n);
bool zj_SetLongLong(struct zj_Value *v, const long long n);
bool zj_SetStrFast(struct zj_Value *v, const char *str);
bool zj_SetStrLenFast(struct zj_Value *v, const char *str, zj_SIZE len);
bool zj_SetStr(struct zj_Value *v, const char *str);
bool zj_SetStrLen(struct zj_Value *v, const char *str, zj_SIZE len);
bool zj_SetStrEscape(struct zj_Value *v, const char *str);
bool zj_SetStrLenEscape(struct zj_Value *v, const char *str, zj_SIZE len);
bool zj_SetKeyFast(struct zj_Value *v, const char *key);
bool zj_SetKeyLenFast(struct zj_Value *v, const char *key, zj_SIZE len);
bool zj_SetKey(struct zj_Value *v, const char *key);
bool zj_SetKeyLen(struct zj_Value *v, const char *key, zj_SIZE len);
bool zj_SetKeyEscape(struct zj_Value *v, const char *key);
bool zj_SetKeyLenEscape(struct zj_Value *v, const char *key, zj_SIZE len);
bool zj_SetArray(struct zj_Value *v);
bool zj_SetObj(struct zj_Value *v);
bool zj_SetFast(struct zj_Value *v, struct zj_Value *vv);
bool zj_Set(struct zj_Value *v, const struct zj_Value *vv);
bool zj_ObjAddFast(struct zj_Value *v, struct zj_Value *vv);
bool zj_ObjAdd(struct zj_Value *v, const struct zj_Value *vv);
bool zj_ArrayAddFast(struct zj_Value *v, struct zj_Value *vv);
bool zj_ArrayAdd(struct zj_Value *v, const struct zj_Value *vv);

bool zj_ArrayDel(struct zj_Value *v, zj_SIZE index);
bool zj_ObjDel(struct zj_Value *v, const char *key);