Golang encoding/json: Difference between revisions
(→Basics) |
|||
(18 intermediate revisions by the same user not shown) | |||
Line 11: | Line 11: | ||
|} | |} | ||
</blockquote><!-- Documentation --> | </blockquote><!-- Documentation --> | ||
= Basics = | |||
<blockquote> | |||
{{ expand | |||
| serialize | |||
| | |||
<syntaxhighlight lang="go"> | |||
type User struct { | |||
Id: int | |||
Name: string | |||
} | |||
user := User{1, "will"} | |||
bytes, err := json.Marshall(&user) | |||
</syntaxhighlight> | |||
}} | |||
{{ expand | |||
| deserialize | |||
| | |||
<syntaxhighlight lang="go"> | |||
var mapping map[string]int | |||
var serialized := []byte(`{"a": 1}`) | |||
json.Unmarshall(serialized, &mapping) | |||
</syntaxhighlight> | |||
}} | |||
{{ expand | |||
| Customizing serialization/deserialization | |||
| | |||
<syntaxhighlight lang="go"> | |||
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) | |||
} | |||
</syntaxhighlight> | |||
}} | |||
</blockquote><!-- Basics --> | |||
= Deserializing = | = Deserializing = | ||
Line 77: | Line 144: | ||
== Custom Deserialization == | == Custom Deserialization == | ||
<blockquote> | <blockquote> | ||
Define an ad-hoc intermediate struct, and then modify it's values as-required. | |||
<syntaxhighlight lang="go"> | |||
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 | |||
} | |||
</syntaxhighlight> | |||
{{ expand | |||
| The same type-aliasing tricks can be used as <code>MarshalJSON()</code> to inherit all fields,<br>and only modify some of them. | |||
| | |||
<syntaxhighlight lang="go"> | |||
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 | |||
} | |||
</syntaxhighlight> | |||
}} | |||
</blockquote><!-- Custom Deserialization --> | </blockquote><!-- Custom Deserialization --> | ||
</blockquote><!-- Deserializing --> | </blockquote><!-- Deserializing --> | ||
Line 129: | Line 252: | ||
func (u User) MarshalJSON() ([]byte, error) { | func (u User) MarshalJSON() ([]byte, error) { | ||
return json.Marshal(&struct { | return json.Marshal(&struct { | ||
Id int `json:"id"` | Id int `json:"id"` // struct-tag alters the key within the encoded json obj | ||
Name string `json:"name"` | Name string `json:"name"` | ||
}{ | }{ | ||
Line 139: | Line 262: | ||
user := User{1, "will"} | user := User{1, "will"} | ||
bytes, _ := json.Marshal(user) | bytes, _ := json.Marshal(user) | ||
fmt.Println(string(bytes)) | fmt.Println(string(bytes)) // '{"id": 1, "name": "will"}' | ||
</syntaxhighlight> | |||
{{ expand | |||
| If you're only changing some fields of a struct, you can inherit it, and override only the methods you want to change. | |||
| | |||
<syntaxhighlight lang="go"> | |||
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--"} | |||
</syntaxhighlight> | |||
}} | |||
{{ expand | |||
| Note that if you do this, you can't change the name of a field, or you'll end up with both fields | |||
| | |||
<syntaxhighlight lang="go"> | |||
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"} | |||
</syntaxhighlight> | </syntaxhighlight> | ||
}} | |||
</blockquote><!-- Custom Serialization --> | </blockquote><!-- Custom Serialization --> | ||
</blockquote><!-- Serializing --> | </blockquote><!-- Serializing --> |
Latest revision as of 20:08, 25 June 2022
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"}