Customizing JSON Unmarshaling with Reflection
In Go, unmarshalling JSON into a struct is a straightforward process. However, when dealing with fields that have custom tags, such as json:"some_field", the standard unmarshalling mechanism may not suffice.
One approach to handle this scenario is to use reflection. By inspecting the struct's fields using reflection, we can check if a field has a specific tag and if so, handle its unmarshalling accordingly.
In this particular case, we want to ensure that a field with the json tag is unmarshalled into a string field as-is. This allows us to handle JSON objects or arrays within our Go struct.
Example Scenario
Consider the following JSON data and Go struct:
{
"I": 3,
"S": {
"phone": {
"sales": "2223334444"
}
}
}
type A struct {
I int64
S string `sql:"type:json"`
}
Our goal is to unmarshal the "S" field as a string, preserving its nested JSON structure.
Solution Using Reflection
The following code demonstrates how to achieve this using reflection:
func main() {
a := A{}
// Unmarshal the JSON data into a byte slice
var data []byte
// Iterate over the fields of the struct
typ := reflect.TypeOf(a)
val := reflect.ValueOf(a)
for i := 0; i In this approach, we manually inspect each field of the struct using reflection to determine if it has the "json" tag. If it does, we unmarshal the JSON data into the field as a string.
Alternative Solution with Custom Marshaler and Unmarshaler
Another option is to implement a custom type, such as RawString, which implements the json.Marshaler and json.Unmarshaler interfaces. This allows for more flexibility and control over the unmarshalling process.
This approach is demonstrated in the following code:
// RawString is a raw encoded JSON object.
// It implements Marshaler and Unmarshaler and can
// be used to delay JSON decoding or precompute a JSON encoding.
type RawString string
// MarshalJSON returns *m as the JSON encoding of m.
func (m *RawString) MarshalJSON() ([]byte, error) {
return []byte(*m), nil
}
// UnmarshalJSON sets *m to a copy of data.
func (m *RawString) UnmarshalJSON(data []byte) error {
if m == nil {
return errors.New("RawString: UnmarshalJSON on nil pointer")
}
*m = RawString(data)
return nil
}
const data = `{"i":3, "S":{"phone": {"sales": "2223334444"}}}`
type A struct {
I int64
S RawString `sql:"type:json"`
}
func main() {
a := A{}
err := json.Unmarshal([]byte(data), &a)
if err != nil {
log.Fatal("Unmarshal failed", err)
}
fmt.Println("Done", a)
}
By implementing our own type, we can customize the unmarshalling process and avoid the need for reflection, resulting in a cleaner and more efficient solution.
Disclaimer: All resources provided are partly from the Internet. If there is any infringement of your copyright or other rights and interests, please explain the detailed reasons and provide proof of copyright or rights and interests and then send it to the email: [email protected] We will handle it for you as soon as possible.
Copyright© 2022 湘ICP备2022001581号-3