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-simplejsonwhich use nativeencoding/jsonlibrary. - 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).