Encoding/Decoding multi-type fields in JSON golang

China☆狼群 提交于 2020-01-24 00:45:10

问题


I am trying to create a struct where a field can hold data of a few particular types, say int, string and a CustomType. I want to decode/encode this struct to/from JSON. How can we achieve this in go/golang?

For example, I have a struct for the following definition:

type MyData struct {
  Name  string                                    `json:"name"`
  Value int32                                     `json:"value"`
  Param <can be either int, string or CustomType> `json:"param"`
}

Where CustomType is

type CustomType struct {
  Custom bool `json:"custom"`
}

Let's say I need to unmarshal the following JSONs to the above struct MyData:

{
  "name": "Hello",
  "value": 32
  "param": "World"
}

And this one:

{
  "name": "Hello",
  "value": 32
  "param": 100
}

And this one also:

{
  "name": "Hello",
  "value": 32
  "param": {
     "custom": true
  }
}

How do I achieve this?

Can I define my own MarshalJSON and UnmarshalJSON on MyData and achieve this?

Or is there a way of defining a custom type, say IntOrStringOrCustom and define MyData as

type MyData struct {
  Name  string              `json:"name"`
  Value int32               `json:"value"`
  Param IntOrStringOrCustom `json:"param"`
}

and then define MarshalJSON and UnmarshalJSON on IntOrStringOrCustom?

I have also seen json.RawMessage. Can we use it somehow here?

Issue with using interface{} is that I will have to write the encoding/decoding logic everywhere, where I am trying to use this data. Or is there an elegant way of doing this with interface{}?


回答1:


UPDATED. interface gets encoded and decoded to JSON well automatically. If you wish to control types, you may add special UnmarshalJSON and perform checks in it:

type TheParam interface{}

type MyData struct {
    Name  string   `json:"name"`
    Value int32    `json:"value"`
    Param TheParam `json:"param"`
}

type myData MyData

func (m *MyData) UnmarshalJSON(b []byte) error {
    var mm myData
    if err := json.Unmarshal(b, &mm); err != nil {
        return err
    }
    switch mm.Param.(type) {
    case float64, string, map[string]interface{}:
        *m = MyData(mm)
        return nil
    default:
        return InvalidFieldTypeError{value: mm.Param}
    }
    return nil
}

Type InvalidFieldTypeError may be convenient to return such class of errors and can be defined as:

type InvalidFieldTypeError struct {
    value interface{}
}

func (e InvalidFieldTypeError) Error() string {
    return fmt.Sprintf("Field type '%T' is not valid for MyData", e.value)
}

The whole example: https://play.golang.org/p/MuW6gwSAKi

Also I'd like to recommend this article https://attilaolah.eu/2013/11/29/json-decoding-in-go/



来源:https://stackoverflow.com/questions/46279048/encoding-decoding-multi-type-fields-in-json-golang

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!