Convert JSON key value pair into single field value in Go

huangapple go评论89阅读模式
英文:

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_nameb_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

huangapple
  • 本文由 发表于 2022年2月21日 19:46:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/71205801.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定