A arbitrary JSON parser for golang.
- Standalone: implement the parser independently for
ECMA-404 The JSON Data Interchange Standard
. - Fast: about two times faster than the package
go-simplejson
which use nativeencoding/json
library. - Lightweight: only about 500 rows code for parser include UTF-16 pairs covert to UTF-8 bytes.
go get github.com/acrazing/cheapjson
package main
import (
"github.com/acrazing/cheapjson"
"encoding/json"
)
func main() {
// Unmarshal a bytes slice
value, err := cheapjson.Unmarshal([]byte("{\"hello\":\"world\", \"int\":12345}"))
if err != nil {
panic(err)
}
// type check
if !value.IsObject() {
panic("parse error")
}
// get a child field
str := value.Get("hello")
// get as string
println(str.String()) // world
// get as int
println(value.Get("int").Int()) // 12345
// And any else you can do:
_ = value.Float() // returns float64
_ = value.Array() // returns []*Value
_ = value.Object() // returns map[string]*Value
// WARNING: any of the upon value extract operate
// need to check the type at first as follow:
if value.IsObject() {
// value is a object, and then you can operate:
_ = value.Object()
}
// And there are more type checks
_ = value.IsObject()
_ = value.IsArray()
_ = value.IsNumber() // if is float or int, returns true
_ = value.IsInt() // just check is int
_ = value.IsTrue()
_ = value.IsFalse()
_ = value.IsBool()
_ = value.IsNull()
_ = value.IsString()
// And you can manipulate a value
value = cheapjson.NewValue()
value.AsObject(nil) // set as a object
_ = value.AddField("hello") // if a value is a object, you can call this, else will panic
value.AsArray(nil) // set as a array
elem := value.AddElement() // if a value is a array, yu can call this, else will panic
elem.AsInt(12) // as a int
elem.AsFloat(232)
elem.AsBool(true)
elem.AsNull()
// And you can get a deep path by:
field := elem.Get("hello", "world", "deep", "3")
_ = field.Value()
// Or set a deep path
// The different between Get and Ensure is that the Get
// just returns the exists field, if the path does not exist
// will return nil, and it will covert the path to integer
// if the node is an array, and the Ensure will force the
// path to be an object, and if the target path does not exist
// will auto generate it as a empty node.
value.Ensure("hello", "world", "deep", "3").AsInt(3)
// And you can dump a value to raw struct
data := value.Value()
// and this could be json marshal
_, _ = json.Marshal(data)
}
See parser_test.go, compare with go-simplejson, which
use the native encoding/json
library to unmarshal a json. The result is:
- NormalInput(small): about 1.6 times faster
- BigInput: about 4.4 times faster
- DeepInput: about 7 times faster
go test -bench=. -v ./parser_test.go
# 2017/07/22 12:48:45 big input size: 92338772, normal input size: 763, deep input size: 33976002
# === RUN TestUnmarshal
# --- PASS: TestUnmarshal (0.00s)
# === RUN TestSimpleJson
# --- PASS: TestSimpleJson (1.62s)
# BenchmarkUnmarshalBigInput-4 5 358595392 ns/op
# BenchmarkSimpleJsonBigInput-4 1 1560047078 ns/op
# BenchmarkUnmarshalNormalInput-4 200000 5372 ns/op
# BenchmarkSimpleJsonNormalInput-4 200000 8593 ns/op
# BenchmarkUnmarshalDeepInput-4 30 42870590 ns/op
# BenchmarkSimpleJsonDeepInput-4 5 305351224 ns/op
# PASS
# ok command-line-arguments 18.314s
MIT
- more unit test.
- test the performance about make buffer before handle a string, (will walk the string twice).