Share some tips on using JSON in Golang
- 2020-06-03 06:55:26
- OfStack
preface
Sometimes fields passed upstream are of type string, but we want to use Numbers instead. One json:",string" will suffice, but if you don't know these golang tricks, you'll have a hard time.
Reference: JSON and struct composition in Go
Temporarily ignore the struct field
type User struct {
Email string `json:"email"`
Password string `json:"password"`
// many more fields ...
}
Temporarily ignore the Password field
json.Marshal(struct {
*User
Password bool `json:"password,omitempty"`
}{
User: user,
})
Temporarily add additional fields
type User struct {
Email string `json:"email"`
Password string `json:"password"`
// many more fields ...
}
Temporarily ignore the Password field and add the token field
json.Marshal(struct {
*User
Token string `json:"token"`
Password bool `json:"password,omitempty"`
}{
User: user,
Token: token,
})
Temporary bonding of two struct
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})
One json was cut into two struct
json.Unmarshal([]byte(`{
"url": "attila@attilaolah.eu",
"title": "Attila's Blog",
"visitors": 6,
"page_views": 14
}`), &struct {
*BlogPost
*Analytics
}{&post, &analytics})
Temporarily rename the field struct
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 a number as a string
type TestObject struct {
Field1 int `json:",string"`
}
The corresponding json is {"Field1": "100"}
An error is reported if json is {"Field1": 100}
Tolerates string and number conversions
If you are using jsoniter, you can enable fuzzy mode to support JSON as passed by PHP.
import "github.com/json-iterator/go/extra"
extra.RegisterFuzzyDecoders()
This allows you to handle the problem of incorrect string and number types. Such as
var val string
jsoniter.UnmarshalFromString(`100`, &val)
And such as
json.Marshal(struct {
*User
Password bool `json:"password,omitempty"`
}{
User: user,
})
0
Tolerates empty arrays as objects
Another frustrating aspect of PHP is that if PHP array is empty, the serialization is []. But when it's not empty, it's serialized
{"key":"value"}
. We need to treat [] as a {}.
If you are using jsoniter, you can enable fuzzy mode to support the JSON passed by FROM PHP.
json.Marshal(struct {
*User
Password bool `json:"password,omitempty"`
}{
User: user,
})
1
This will support it
json.Marshal(struct {
*User
Password bool `json:"password,omitempty"`
}{
User: user,
})
2
time. Time is supported with MarshalJSON
golang will put by default
time.Time
Serialize as a string. If we want to do it the other way
time.Time
, you need to customize the type and define MarshalJSON.
type timeImplementedMarshaler time.Time
func (obj timeImplementedMarshaler) MarshalJSON() ([]byte, error) {
seconds := time.Time(obj).Unix()
return []byte(strconv.FormatInt(seconds, 10)), nil
}
MarshalJSON is called when serialized
json.Marshal(struct {
*User
Password bool `json:"password,omitempty"`
}{
User: user,
})
4
RegisterTypeEncoder is used to support ES114en.Time
jsoniter is able to customize JSON codec for type that is not defined by you. Such as for
time.Time
You can serialize with epoch int64
json.Marshal(struct {
*User
Password bool `json:"password,omitempty"`
}{
User: user,
})
5
See the implementation code for RegisterTimeAsInt64Codec for customization
Using MarshalText supports non-string as map for key
Although the JSON standard only supports string as map of key. But golang passed
MarshalText()
Interface so that other types can also be key for map. For example,
json.Marshal(struct {
*User
Password bool `json:"password,omitempty"`
}{
User: user,
})
6
big.Float realized MarshalText().
Using json RawMessage
If part of the json document does not have a standard format, we can save the original text information in string.
json.Marshal(struct {
*User
Password bool `json:"password,omitempty"`
}{
User: user,
})
7
Using json Number
By default, if yes
interface{}
The case for the corresponding number would be of type float64. If you enter a large number, this representation will compromise the accuracy. So you can
UseNumber()
To enable the
json.Number
To represent Numbers as strings.
decoder1 := json.NewDecoder(bytes.NewBufferString(`123`))
decoder1.UseNumber()
var obj1 interface{}
decoder1.Decode(&obj1)
should.Equal(json.Number("123"), obj1)
jsoniter supports this use of the standard library. At the same time, extended behavior allows Unmarshal to support UseNumber.
json.Marshal(struct {
*User
Password bool `json:"password,omitempty"`
}{
User: user,
})
9
Change the naming style of the field
Often the field name in JSON is different from the field name in Go. We can modify it with 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))
But 1 each field to set, too much trouble. If jsoniter is used, we can set the naming style with 1.
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))
Use private fields
Go's standard library only supports field of public. jsoniter has additional support for private's field. You need to use
SupportPrivateFields()
To turn on the switch.
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)
conclusion