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:
<code class="json">{ "I": 3, "S": { "phone": { "sales": "2223334444" } } }</code>
<code class="go">type A struct { I int64 S string `sql:"type:json"` }</code>
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:
<code class="go">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 < typ.NumField(); i++ { f := typ.Field(i) // Check if the field has a "json" tag if f.Tag.Get("json") == "" { continue } // Retrieve the field value fv := val.Field(i) // Unmarshall the JSON data into the field as a string if err := json.Unmarshal(data, &fv); err != nil { log.Fatal(err) } } fmt.Println(a) }</code>
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:
<code class="go">// 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) }</code>
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.
The above is the detailed content of How to Customize JSON Unmarshaling with Reflection in Go?. For more information, please follow other related articles on the PHP Chinese website!