在Go中实现基于字段值的动态JSON序列化与反序列化,可以使用json.RawMessage结合类型断言。以下是具体实现:
package main
import (
"encoding/json"
"fmt"
)
type General struct {
Type string `json:"type"`
LookUp []LookUpElement `json:"lookup"`
}
type LookUpElement struct {
RawData json.RawMessage `json:"-"`
Name string `json:"name"`
Data interface{} `json:"-"`
}
type Human struct {
Name string `json:"name"`
Feature string `json:"feature"`
}
type Animal struct {
Name string `json:"name"`
Type string `json:"type"`
Example string `json:"example"`
}
// 自定义UnmarshalJSON实现动态反序列化
func (l *LookUpElement) UnmarshalJSON(data []byte) error {
// 先解析出name字段
var temp map[string]interface{}
if err := json.Unmarshal(data, &temp); err != nil {
return err
}
l.Name = temp["name"].(string)
l.RawData = data
// 根据name字段动态解析
switch l.Name {
case "human":
var human Human
if err := json.Unmarshal(data, &human); err != nil {
return err
}
l.Data = human
case "animal":
var animal Animal
if err := json.Unmarshal(data, &animal); err != nil {
return err
}
l.Data = animal
default:
l.Data = temp
}
return nil
}
// 自定义MarshalJSON实现动态序列化
func (l LookUpElement) MarshalJSON() ([]byte, error) {
if l.Data != nil {
return json.Marshal(l.Data)
}
return l.RawData, nil
}
func main() {
str := `{ "type": "living", "lookup": [ { "name": "human", "feature": "think" }, { "name": "animal", "type": "carnivores", "example": "lion" }, { "name": "animal", "type": "herbivores", "example": "zebra" } ] }`
// 反序列化
var general General
if err := json.Unmarshal([]byte(str), &general); err != nil {
panic(err)
}
// 访问解析后的数据
for _, item := range general.LookUp {
switch v := item.Data.(type) {
case Human:
fmt.Printf("Human: %+v\n", v)
case Animal:
fmt.Printf("Animal: %+v\n", v)
}
}
// 序列化
output, err := json.MarshalIndent(general, "", " ")
if err != nil {
panic(err)
}
fmt.Println("Serialized JSON:")
fmt.Println(string(output))
}
另一种更灵活的方法是使用mapstructure库:
package main
import (
"encoding/json"
"fmt"
"github.com/mitchellh/mapstructure"
)
type General struct {
Type string `json:"type"`
LookUp []interface{} `json:"lookup"`
}
type Human struct {
Name string `json:"name" mapstructure:"name"`
Feature string `json:"feature" mapstructure:"feature"`
}
type Animal struct {
Name string `json:"name" mapstructure:"name"`
Type string `json:"type" mapstructure:"type"`
Example string `json:"example" mapstructure:"example"`
}
func main() {
str := `{ "type": "living", "lookup": [ { "name": "human", "feature": "think" }, { "name": "animal", "type": "carnivores", "example": "lion" }, { "name": "animal", "type": "herbivores", "example": "zebra" } ] }`
// 反序列化
var data map[string]interface{}
if err := json.Unmarshal([]byte(str), &data); err != nil {
panic(err)
}
lookup := data["lookup"].([]interface{})
var result []interface{}
for _, item := range lookup {
m := item.(map[string]interface{})
switch m["name"] {
case "human":
var human Human
mapstructure.Decode(m, &human)
result = append(result, human)
case "animal":
var animal Animal
mapstructure.Decode(m, &animal)
result = append(result, animal)
}
}
general := General{
Type: data["type"].(string),
LookUp: result,
}
// 访问数据
for _, item := range general.LookUp {
switch v := item.(type) {
case Human:
fmt.Printf("Human: %+v\n", v)
case Animal:
fmt.Printf("Animal: %+v\n", v)
}
}
// 序列化
output, err := json.MarshalIndent(general, "", " ")
if err != nil {
panic(err)
}
fmt.Println("Serialized JSON:")
fmt.Println(string(output))
}
使用标准库的反射方案:
package main
import (
"encoding/json"
"fmt"
"reflect"
)
type General struct {
Type string `json:"type"`
LookUp []interface{} `json:"lookup"`
}
func DynamicUnmarshal(data []byte) (*General, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
var general General
if err := json.Unmarshal(raw["type"], &general.Type); err != nil {
return nil, err
}
var lookups []json.RawMessage
if err := json.Unmarshal(raw["lookup"], &lookups); err != nil {
return nil, err
}
for _, lookup := range lookups {
var base map[string]interface{}
if err := json.Unmarshal(lookup, &base); err != nil {
return nil, err
}
switch base["name"] {
case "human":
var human Human
if err := json.Unmarshal(lookup, &human); err != nil {
return nil, err
}
general.LookUp = append(general.LookUp, human)
case "animal":
var animal Animal
if err := json.Unmarshal(lookup, &animal); err != nil {
return nil, err
}
general.LookUp = append(general.LookUp, animal)
}
}
return &general, nil
}
func DynamicMarshal(general *General) ([]byte, error) {
var lookups []interface{}
for _, item := range general.LookUp {
v := reflect.ValueOf(item)
switch v.Type().Name() {
case "Human":
lookups = append(lookups, item.(Human))
case "Animal":
lookups = append(lookups, item.(Animal))
}
}
result := map[string]interface{}{
"type": general.Type,
"lookup": lookups,
}
return json.MarshalIndent(result, "", " ")
}
这些示例展示了如何基于"name"字段实现动态的JSON处理,第一种方案通过自定义UnmarshalJSON和MarshalJSON方法提供了最完整的控制,第二种方案使用mapstructure库简化了映射过程,第三种方案使用反射提供了另一种实现思路。