英文:
Convert JSON key value pair into single field value in Go
问题
我有一个类似以下的 JSON 数据,其中 value 可以是整数或字符串:
{
"data": [
{
"Name": "a_name",
"value": 1
},
{
"Name": "b_name",
"value": "val"
},
{
"Name": "c_name",
"value": 2
}
]
}
现在我想将该 JSON 转换为以下结构,只提取 a_name
和 b_name
的值:
type Data struct {
AName int `json:"a_name"`
BName string `json:"b_name"`
}
我可以通过以下方式实现:
import (
"encoding/json"
"fmt"
)
type TmpData struct {
Data []struct {
Name string `json:"Name"`
Value interface{} `json:"value"`
} `json:"data"`
}
type ExpectedData struct {
AName int `json:"a_name"`
BName string `json:"b_name"`
}
func main() {
data := `{
"data": [
{
"Name": "a_name",
"value": 1
},
{
"Name": "b_name",
"value": "val"
},
{
"Name": "c_name",
"value": 2
}
]
}`
tmpData := &TmpData{}
json.Unmarshal([]byte(data), tmpData)
ans := &ExpectedData{}
for _, d := range tmpData.Data {
if d.Name == "a_name" {
ans.AName = int(d.Value.(float64))
} else if d.Name == "b_name" {
ans.BName = d.Value.(string)
}
}
fmt.Println(ans)
}
有没有更好的解决方案?
英文:
I have a json like following, where value can be int or string
{
"data": [
{
"Name": "a_name",
"value": 1
},
{
"Name": "b_name",
"value": "val"
},
{
"Name": "c_name",
"value": 2
}
]
}
Now I want to convert that json into following struct, like only extract a_name
and b_name
value.
type Data struct {
AName int `json: "a_name"`
BName string `json: "b_name"`
}
I can do it by following way
import (
"encoding/json"
"fmt"
)
type TmpData struct {
Data []struct {
Name string `json:"Name"`
Value interface{} `json:"value"`
} `json:"data"`
}
type ExpectedData struct {
AName int `json: "a_name"`
BName string `json: "b_name"`
}
func main() {
data := `{
"data": [
{
"Name": "a_name",
"value": 1
},
{
"Name": "b_name",
"value": "val"
},
{
"Name": "c_name",
"value": 2
}
]
}`
tmpData := &TmpData{}
json.Unmarshal([]byte(data), tmpData)
ans := &ExpectedData{}
for _, d := range tmpData.Data {
if d.Name == "a_name" {
ans.AName = int(d.Value.(float64))
} else if d.Name == "b_name" {
ans.BName = d.Value.(string)
}
}
fmt.Println(ans)
}
Is there any better solution for this?
答案1
得分: 2
使用标准的JSON解组(un-marshalling)是不可能的,除非你为你的Data
类型编写一个自定义的解组器(un-marshaller)。
关键在于将value
的类型定义为interface{}
,这样就可以在b_name
记录中存储多种类型的值。
func (d *Data) UnmarshalJSON(data []byte) error {
var result Details
if err := json.Unmarshal(data, &result); err != nil {
return err
}
for _, value := range result.Data {
switch value.Name {
// 当使用interface{}进行解组时,json包会假设为float64类型
case "a_name":
v, ok := value.Value.(float64)
if !ok {
return fmt.Errorf("a_name的数据类型为%T,但期望为float64", value.Value)
}
d.AName = int(v)
case "b_name":
v, ok := value.Value.(string)
if !ok {
return fmt.Errorf("b_name的数据类型为%T,但期望为string", value.Value)
}
d.BName = v
}
}
return nil
}
Playground - https://go.dev/play/p/GrXKAE87d1F
英文:
Not possible with the standard JSON un-marshalling unless you write a custom un-marshaller for your Data
type.
The key here is to define the type for value
to be an interface{}
, so that multiple types could be stored in your b_name
record.
func (d *Data) UnmarshalJSON(data []byte) error {
var result Details
if err := json.Unmarshal(data, &result); err != nil {
return err
}
for _, value := range result.Data {
switch value.Name {
// The json package will assume float64 when Unmarshalling with an interface{}
case "a_name":
v, ok := (value.Value).(float64)
if !ok {
return fmt.Errorf("a_name got data of type %T but wanted float64", value.Value)
}
d.AName = int(v)
case "b_name":
v, ok := (value.Value).(string)
if !ok {
return fmt.Errorf("b_name got data of type %T but wanted string", value.Value)
}
d.BName = v
}
}
return nil
}
Playground - https://go.dev/play/p/GrXKAE87d1F
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论