英文:
Encoding/Decoding multi-type fields in JSON golang
问题
我正在尝试创建一个结构体,其中一个字段可以保存几种特定类型的数据,比如int
、string
和CustomType
。我想要将这个结构体与JSON进行解码/编码。在Go语言中,我们如何实现这个功能呢?
例如,我有一个如下定义的结构体:
type MyData struct {
Name string `json:"name"`
Value int32 `json:"value"`
Param <可以是int、string或CustomType> `json:"param"`
}
其中CustomType
定义如下:
type CustomType struct {
Custom bool `json:"custom"`
}
假设我需要将以下JSON解组到上述MyData
结构体中:
{
"name": "Hello",
"value": 32,
"param": "World"
}
还有这个:
{
"name": "Hello",
"value": 32,
"param": 100
}
以及这个:
{
"name": "Hello",
"value": 32,
"param": {
"custom": true
}
}
我该如何实现这个功能呢?
我可以在MyData
上定义自己的MarshalJSON
和UnmarshalJSON
方法来实现吗?
或者是否有一种定义自定义类型(比如IntOrStringOrCustom
)并在MyData
中使用IntOrStringOrCustom
字段的方法,然后在IntOrStringOrCustom
上定义MarshalJSON
和UnmarshalJSON
方法的方式呢?
我还看到了json.RawMessage
,我们能在这里使用它吗?
使用interface{}
的问题是,我将不得不在每个使用这些数据的地方编写编码/解码逻辑。是否有一种优雅的方式可以使用interface{}
来解决这个问题?
英文:
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
得分: 4
更新。interface
可以自动编码和解码为JSON。如果你希望控制类型,可以添加特殊的UnmarshalJSON
方法并在其中进行检查:
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 struct {
value interface{}
}
func (e InvalidFieldTypeError) Error() string {
return fmt.Sprintf("Field type '%T' is not valid for MyData", e.value)
}
InvalidFieldTypeError
类型可以方便地返回此类错误,并可以定义为:
type InvalidFieldTypeError struct {
value interface{}
}
func (e InvalidFieldTypeError) Error() string {
return fmt.Sprintf("Field type '%T' is not valid for MyData", e.value)
}
完整示例:https://play.golang.org/p/MuW6gwSAKi
另外,我想推荐这篇文章:https://attilaolah.eu/2013/11/29/json-decoding-in-go/
英文:
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/
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论