json iterator

Fastest JSON parser ever

There are times we are passed with a field of string type, but we wish it to be int. If we know json:",string", then it should be a easy. Otherwise, it will take some serious time to do the conversion.

Rerference: http://attilaolah.eu/2014/09/10/json-and-struct-composition-in-go/

Ad-hoc ignore some field

type User struct {
    Email    string `json:"email"`
    Password string `json:"password"`
    // many more fields…
}

to ignore Password field

json.Marshal(struct {
    *User
    Password bool `json:"password,omitempty"`
}{
    User: user,
})

Ad-hoc add extra field

type User struct {
    Email    string `json:"email"`
    Password string `json:"password"`
    // many more fields…
}

Ignore the Password field and add new Token field

json.Marshal(struct {
    *User
    Token    string `json:"token"`
    Password bool `json:"password,omitempty"`
}{
    User: user,
    Token: token,
})

Ad-hoc combine two structs

type BlogPost struct {
    URL   string `json:"url"`
    Title string `json:"title"`
}

type Analytics struct {
    Visitors  int `json:"visitors"`
    PageViews int `json:"page_views"`
}

json.Marshal(struct{
    *BlogPost
    *Analytics
}{post, analytics})

Ad-hoc split one json into two

json.Unmarshal([]byte(`{
  "url": "[email protected]",
  "title": "Attila's Blog",
  "visitors": 6,
  "page_views": 14
}`), &struct {
  *BlogPost
  *Analytics
}{&post, &analytics})

Ad-hoc rename field

type CacheItem struct {
    Key    string `json:"key"`
    MaxAge int    `json:"cacheAge"`
    Value  Value  `json:"cacheValue"`
}

json.Marshal(struct{
    *CacheItem

    // Omit bad keys
    OmitMaxAge omit `json:"cacheAge,omitempty"`
    OmitValue  omit `json:"cacheValue,omitempty"`

    // Add nice keys
    MaxAge int    `json:"max_age"`
    Value  *Value `json:"value"`
}{
    CacheItem: item,

    // Set the int by value:
    MaxAge: item.MaxAge,

    // Set the nested struct by reference, avoid making a copy:
    Value: &item.Value,
})

Pass numbers as string

type TestObject struct {
	Field1 int    `json:",string"`
}

The corresponding json should be {"Field1": "100"}

For {"Field1": 100}, there will be error

String/number fuzzy conversion

If you are using jsoniter, enable fuzzy decoders can make life easier working with PHP.

import "github.com/json-iterator/go/extra"

extra.RegisterFuzzyDecoders()

Then, no matter the input is string or number, or the output is number or string, jsoniter will convert it for you. For example:

var val string
jsoniter.UnmarshalFromString(`100`, &val)

And

var val float32
jsoniter.UnmarshalFromString(`"1.23"`, &val)

[] as object

Another heart breaking “feature” of PHP is that, when array is empty, it is encoded as [] instead of {}.

If you are using jsoniter, enable fuzzy decoders will also fix this.

import "github.com/json-iterator/go/extra"

extra.RegisterFuzzyDecoders()

Then we can deal with []

var val map[string]interface{}
jsoniter.UnmarshalFromString(`[]`, &val)

Customize time.Time with MarshalJSON

The defeault implementation will encode time.Time as string. If we want to represent time in other format, we have to define a new type for time.Time, with MarshalJSON.

type timeImplementedMarshaler time.Time

func (obj timeImplementedMarshaler) MarshalJSON() ([]byte, error) {
	seconds := time.Time(obj).Unix()
	return []byte(strconv.FormatInt(seconds, 10)), nil
}

Marshal will invoke MarshalJSON on types defined them.

type TestObject struct {
	Field timeImplementedMarshaler
}
should := require.New(t)
val := timeImplementedMarshaler(time.Unix(123, 0))
obj := TestObject{val}
bytes, err := jsoniter.Marshal(obj)
should.Nil(err)
should.Equal(`{"Field":123}`, string(bytes))

Use RegisterTypeEncoder to customize time.Time

Unlike the standard library, jsoniter allow you to customize types defined by other guys. For example, we can use epoch int64 to encode a time.Time.

import "github.com/json-iterator/go/extra"

extra.RegisterTimeAsInt64Codec(time.Microsecond)
output, err := jsoniter.Marshal(time.Unix(1, 1002))
should.Equal("1000001", string(output))

If you want to do it yourself, reference RegisterTimeAsInt64Codec for more information.

Non string key map

Although JSON standard requires the key of map to be string, golang support more types as long as they defined MarshalText(). For example:

f, _, _ := big.ParseFloat("1", 10, 64, big.ToZero)
val := map[*big.Float]string{f: "2"}
str, err := MarshalToString(val)
should.Equal(`{"1":"2"}`, str)

Given big.Float is a type with MarshalText()

Use json.RawMessage

Some part of JSON might be lacking a uniform schema, we can keep the original JSON as it is.

type TestObject struct {
	Field1 string
	Field2 json.RawMessage
}
var data TestObject
json.Unmarshal([]byte(`{"field1": "hello", "field2": [1,2,3]}`), &data)
should.Equal(` [1,2,3]`, string(data.Field2))

Use json.Number

By default, number decoded into interface{} will be of type float64. If the input is large, the precision loss might be a problem. So we can UseNumber() to represent number as json.Number.

decoder1 := json.NewDecoder(bytes.NewBufferString(`123`))
decoder1.UseNumber()
var obj1 interface{}
decoder1.Decode(&obj1)
should.Equal(json.Number("123"), obj1)

jsoniter support this usage. And it extended the support to Unmarshal as will.

json := Config{UseNumber:true}.Froze()
var obj interface{}
json.UnmarshalFromString("123", &obj)
should.Equal(json.Number("123"), obj)

Field naming strategy

Most of the time, we want to different field names in JSON. We can use field tag:

output, err := jsoniter.Marshal(struct {
	UserName      string `json:"user_name"`
	FirstLanguage string `json:"first_language"`
}{
	UserName:      "taowen",
	FirstLanguage: "Chinese",
})
should.Equal(`{"user_name":"taowen","first_language":"Chinese"}`, string(output))

However, it is tedious. If you are using jsoniter, we can change all fields in one line.

import "github.com/json-iterator/go/extra"

extra.SetNamingStrategy(LowerCaseWithUnderscores)
output, err := jsoniter.Marshal(struct {
	UserName      string
	FirstLanguage string
}{
	UserName:      "taowen",
	FirstLanguage: "Chinese",
})
should.Nil(err)
should.Equal(`{"user_name":"taowen","first_language":"Chinese"}`, string(output))

Private fields

The standard library requries all fields appear in JSON to be public. If you use jsoniter, SupportPrivateFields() will remove this restriction.

import "github.com/json-iterator/go/extra"

extra.SupportPrivateFields()
type TestObject struct {
	field1 string
}
obj := TestObject{}
jsoniter.UnmarshalFromString(`{"field1":"Hello"}`, &obj)
should.Equal("Hello", obj.field1)

京ICP备16068519号