Golang encoding/json
Conveniently, the builtin types are ready for json serialization/deserialization without any additional work.
Documentation
official docs https://pkg.go.dev/encoding/json@go1.18.3 intro https://go.dev/blog/json
Basics
serialize
type User struct { Id: int Name: string } user := User{1, "will"} bytes, err := json.Marshall(&user)deserialize
var mapping map[string]int var serialized := []byte(`{"a": 1}`) json.Unmarshall(serialized, &mapping)Customizing serialization/deserialization
type User struct { Name string } // User.Name serialized as '--${Name}--' func (u *User) MarshalJSON() ([]byte, error) { return json.Marshal(&struct { Name string }{ Name: fmt.Sprintf("--%s--", u.Name), }) } // User.Name deserialized back to '${Name}' func (u *User) UnmarshalJSON(data []byte) error { aux := &struct { Name string }{} json.Unmarshal(data, &aux) u.Name = strings.Replace(aux.Name, "-", "", -1) return nil } func main() { // serialize user := User{"will"} serialized, _ := json.Marshal(&user) fmt.Println(string(serialized)) // deserialize var roundtrip_user User json.Unmarshal(serialized, &roundtrip_user) fmt.Println(roundtrip_user) }
Deserializing
Builtins
// string, int var name string json.Unmarshal([]byte(`"vaderd"`), &name) fmt.Println(name) // vaderd // array, slice var items [2]string json.Unmarshal([]byte(`["abc", "def"]`), &items) fmt.Println(items) // [abc def]Arbitrary JSON
You can parse arbitrary json objects using
interface{}
.projects_json := []byte(`{"Projects": [{"Name": "a"}, {"Name": "b"}]}`) var projects interface{} json.Unmarshal(projects_json, &projects) projects_casted := projects.(map[string]interface{}) // type-assertion -- if type does not match, panics fmt.Println(projects_casted["Projects"]) // [map[Name:a] map[Name:b]]Json-Object to Struct
type User struct { id int Name string } var user User data := []byte(`{"id": 123, "Name": "vaderd"}`) json.Unmarshal(data, &user) fmt.Println(user.Name)Unlike the serialization behaviour, even un-exported fields will be set on the object.
You assume the JSON object knows what it wants set.type User struct { id int Name string } func (u *User) Id() int { return u.id } var user2 User data := []byte(`{"id": 123, "Name": "vaderd"}`) json.Unmarshal(data, &user2) fmt.Println(user.Id()) // 123Custom Deserialization
Define an ad-hoc intermediate struct, and then modify it's values as-required.
type User struct { id int Name string Age int } func (u *User) UnmarshalJSON(data []byte) error { aux := &struct { Name string Age int }{} json.Unmarshal(data, &aux) u.Name = fmt.Sprintf("--%s--", aux.Name) u.Age = aux.Age return nil } func main() { var user User json.Unmarshal([]byte(`{"Name": "will", "Age": 123}`), &user) fmt.Println(user.Name) // "--will--" fmt.Println(user.Age) // 123 }The same type-aliasing tricks can be used as
MarshalJSON()
to inherit all fields,
and only modify some of them.type User struct { id int Name string Age int } func (u *User) UnmarshalJSON(data []byte) error { type Alias User aux := &struct { *Alias }{ Alias: (*Alias)(u), } json.Unmarshal(data, &aux) u.Name = fmt.Sprintf("--%s--", aux.Name) return nil } func main() { var user User json.Unmarshal([]byte(`{"Name": "will", "Age": 123}`), &user) fmt.Println(user.Name) // '--will--' fmt.Println(user.Age) // 123 }
Serializing
Builtins
// string, int bytes, _ := json.Marshal("vaderd") fmt.Println(string(bytes)) // '"vaderd"' // array, slice names := []string{"maize", "sprout"} bytes, _ := json.Marshal(names) fmt.Println(string(bytes)) // '["maize", "sprout"]'Struct to Json-Object
Only exported fields will be serialized by default.
If you'd like to keep private fields, you can define aMarshalJSON()
method (see Custom Serialization below).type User struct { id int Name string } user := User{id: 1, Name: "will"} bytes, _ := json.Marshal(user) fmt.Println(string(bytes)) // '{"Name": "will"}'Custom Serialization
If you need more control over serialization, you can define a
MarshalJSON()
method.It's common to define an ad-hoc struct type to represent your JSON object.
You can use struct tags to assign different names to your fields when encoded in json.type User struct { id int Name string } func (u User) MarshalJSON() ([]byte, error) { return json.Marshal(&struct { Id int `json:"id"` // struct-tag alters the key within the encoded json obj Name string `json:"name"` }{ Id: u.id, Name: u.Name, }) } user := User{1, "will"} bytes, _ := json.Marshal(user) fmt.Println(string(bytes)) // '{"id": 1, "name": "will"}'If you're only changing some fields of a struct, you can inherit it, and override only the methods you want to change.
type User struct { id int Name string } func (u *User) MarshalJSON() ([]byte, error) { type Alias User return json.Marshal(&struct { Name string `json:"Name"` *Alias }{ Name: fmt.Sprintf("--%s--", u.Name), Alias: (*Alias)(u), }) } user := User{1, "will"} bytes, _ := json.Marshal(&user) fmt.Println(string(bytes)) // {"Name": "--will--"}Note that if you do this, you can't change the name of a field, or you'll end up with both fields
type User struct { id int Name string } func (u *User) MarshalJSON() ([]byte, error) { type Alias User return json.Marshal(&struct { Name string `json:"name"` *Alias }{ Name: fmt.Sprintf("--%s--", u.Name), Alias: (*Alias)(u), }) } user := User{1, "will"} bytes, _ := json.Marshal(&user) fmt.Println(string(bytes)) // {"Name": "--will--", "name": "will"}