Golang解析JSON响应,然后将字段名转换为snake_case。

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

Golang unmarshal JSON response, then convert the field name into snake_case

问题

我想要获取具有PascalCase格式的源的带有json标签的数据,并将其保存到我的数据库中。但在进入数据库之前,我想将PascalCase格式更改为snake_case格式。

我的问题似乎与此问题相反(https://stackoverflow.com/questions/63248152/golang-unmarshal-an-json-response-then-marshal-with-struct-field-names)。但是,我想在name字段中使用snake_case而不是PascalCase。

以下是我编写的示例代码:

package main

import (
	"encoding/json"
	"log"
)

// models data to save in DB
type (
	Person struct {
		FirstName string      `json:"FirstName"`
		LastName  string      `json:"LastName"`
		Children  []ChildData `json:"Children,omitempty"`
	}
	ChildData struct {
		ChildName string `json:"ChildName"`
		Age       int    `json:"Age"`
		FavColor  string `json:"FavColor"`
	}
	PersonOut struct {
		FirstName string      `json:"first_name"`
		LastName  string      `json:"last_name"`
		Children  []ChildData `json:"children,omitempty"`
	}
	ChildDataOut struct {
		ChildName string `json:"child_name"`
		Age       int    `json:"age"`
		FavColor  string `json:"fav_color"`
	}
)

// grisha is data from fetch API
var grisha = map[string]interface{}{
	"FirstName": "Grisha",
	"LastName":  "Jeager",
	"Children": []map[string]interface{}{
		{
			"ChildName": "Zeke",
			"Age":       2,
			"FavColor":  "blue",
		}, {
			"ChildName": "Eren",
			"Age":       3,
			"FavColor":  "red",
		},
	},
}

func getJsonfromAPI(data map[string]interface{}) []byte {
	grishaJson, _ := json.MarshalIndent(grisha, "", "    ")
	return grishaJson
}

func parsingJSON(jsonInput []byte) {
	var person Person
	json.Unmarshal(jsonInput, &person)

	out := PersonOut(person)
	payload2, err := json.MarshalIndent(out, "", "    ")
	if err != nil {
		panic(err)
	}
	log.Println(string(payload2))
}

func main() {
	data := getJsonfromAPI(grisha)
	parsingJSON(data)
}

我想要的结果是:

{
    "first_name": "Grisha",
    "last_name": "Jeager",
    "children": [
        {
            "child_name": "Zeke",
            "age": 2,
            "fav_color": "blue"
        },
        {
            "child_name": "Eren",
            "age": 3,
            "fav_color": "red"
        }
    ]
}

但是结果在嵌套字段中仍然是PascalCase:

{
    "first_name": "Grisha",
    "last_name": "Jeager",
    "children": [
        {
            "ChildName": "Zeke",
            "Age": 2,
            "FavColor": "blue"
        },
        {
            "ChildName": "Eren",
            "Age": 3,
            "FavColor": "red"
        }
    ]
}

我想知道如何将嵌套结构的字段名从解组JSON中转换为snake_case。

英文:

I want to get data with json tag whose source has PascalCase format and save it to my database. But before going into the database, I want to change the PascalCase format to snake_case format.

My question seems to be the opposite of this question (https://stackoverflow.com/questions/63248152/golang-unmarshal-an-json-response-then-marshal-with-struct-field-names). But instead of having PascalCase, I want to have snake_case in the name field

Here is the sample code that i wrote:

package main
import (
"encoding/json"
"log"
)
// models data to save in DB
type (
Person struct {
FirstName string      `json:"FirstName"`
LastName  string      `json:"LastName"`
Children  []ChildData `json:"Children,omitempty"`
}
ChildData struct {
ChildName string `json:"ChildName"`
Age       int    `json:"Age"`
FavColor  string `json:"FavColor"`
}
PersonOut struct {
FirstName string      `json:"first_name"`
LastName  string      `json:"last_name"`
Children  []ChildData `json:"children,omitempty"`
}
ChildDataOut struct {
ChildName string `json:"child_name"`
Age       int    `json:"age"`
FavColor  string `json:"fav_Color"`
}
)
// grisha is data from fetch API
var grisha = map[string]interface{}{
"FirstName": "Grisha",
"LastName":  "Jeager",
"Children": []map[string]interface{}{
{
"ChildName": "Zeke",
"Age":       2,
"FavColor":  "blue",
}, {
"ChildName": "Eren",
"Age":       3,
"FavColor":  "red",
},
},
}
func getJsonfromAPI(data map[string]interface{}) []byte {
grishaJson, _ := json.MarshalIndent(grisha, "", "    ")
return grishaJson
}
func parsingJSON(jsonInput []byte) {
var person Person
json.Unmarshal(jsonInput, &person)
out := PersonOut(person)
payload2, err := json.MarshalIndent(out, "", "    ")
if err != nil {
panic(err)
}
log.Println(string(payload2))
}
func main() {
data := getJsonfromAPI(grisha)
parsingJSON(data)
}

The result that I want is like

 {
"first_name": "Grisha",
"last_name": "Jeager",
"children": [
{
"child_name": "Zeke",
"age": 2,
"fav_color": "blue"
},
{
"child_name": "Eren",
"age": 3,
"fav_color": "red"
}
]
}

but the result is still having PascalCase in the nested field:

 {
"first_name": "Grisha",
"last_name": "Jeager",
"children": [
{
"ChildName": "Zeke",
"Age": 2,
"FavColor": "blue"
},
{
"ChildName": "Eren",
"Age": 3,
"FavColor": "red"
}
]
}

I was wondering how to convert field name for the nested structure from unmarshal JSON.

答案1

得分: 4

将一个结构体转换为另一个结构体只有在它们的底层类型(忽略标签)完全相同的情况下才能成功。如果底层类型不相同,则无法进行转换。

以结构体S1S2为例,在下面的代码片段中,它们的底层类型是相同的,因此可以相互转换:

type S1 struct {
    Field T1
}
type S2 struct {
    Field T1
}
type T1 string

_ = S2(S1{}) // 可以转换

然而,在下面的代码片段中,它们的底层类型不相同,因此无法相互转换:

type S1 struct {
    Field T1
}
type S2 struct {
    Field T2
}
type T1 string
type T2 string

_ = S2(S1{}) // 无法将类型为S1的值转换为类型S2

要了解更多详细信息,请阅读有关转换的规范:https://go.dev/ref/spec#Conversions


因此,要在两个底层类型不相同的结构体之间进行转换,您必须逐个字段地手动进行转换。但是,在JSON编组的情况下,您可以通过实现json.Marshaler接口并让各个实例“自行转换”来使代码更加简洁。

type Person struct {
	FirstName string      `json:"FirstName"`
	LastName  string      `json:"LastName"`
	Children  []ChildData `json:"Children"`
}

func (p Person) MarshalJSON() ([]byte, error) {
	type T struct {
		FirstName string      `json:"first_name"`
		LastName  string      `json:"last_name"`
		Children  []ChildData `json:"children"`
	}

	return json.Marshal(T(p))
}

type ChildData struct {
	ChildName string `json:"ChildName"`
	Age       int    `json:"Age"`
	FavColor  string `json:"FavColor"`
}

func (d ChildData) MarshalJSON() ([]byte, error) {
	type T struct {
		ChildName string `json:"child_name"`
		Age       int    `json:"age"`
		FavColor  string `json:"fav_color"`
	}

	return json.Marshal(T(d))
}

https://go.dev/play/p/UGJY5p490Gs

英文:

Converting one struct to another works only if their their underlying types (ignoring tags) are identical. If the underlying types are not identical you cannot do the conversion.

Take for example the structs S1 and S2, in the following snippet their underlying types are identical and you can convert one to the other:

type S1 struct {
    Field T1
}
type S2 struct {
    Field T1
}
type T1 string

_ = S2(S1{}) // ok

However, in the next snippet their underlying types are NOT identical and therefore you cannot convert one to the other:

type S1 struct {
    Field T1
}
type S2 struct {
    Field T2
}
type T1 string
type T2 string

_ = S2(S1{}) // cannot convert S1{} (value of type S1) to type S2

For more details read the spec on conversions here: https://go.dev/ref/spec#Conversions


So to convert between two struct whose underlying types are NOT identical you have to manually do the conversion field by field. However, in the case of JSON marshaling, you can make this a bit nicer by implementing the json.Marshaler interface and have the individual instances "convert themselves".

type Person struct {
	FirstName string      `json:"FirstName"`
	LastName  string      `json:"LastName"`
	Children  []ChildData `json:"Children"`
}

func (p Person) MarshalJSON() ([]byte, error) {
	type T struct {
		FirstName string      `json:"first_name"`
		LastName  string      `json:"last_name"`
		Children  []ChildData `json:"children"`
	}

	return json.Marshal(T(p))
}

type ChildData struct {
	ChildName string `json:"ChildName"`
	Age       int    `json:"Age"`
	FavColor  string `json:"FavColor"`
}

func (d ChildData) MarshalJSON() ([]byte, error) {
	type T struct {
		ChildName string `json:"child_name"`
		Age       int    `json:"age"`
		FavColor  string `json:"fav_color"`
	}

	return json.Marshal(T(d))
}

https://go.dev/play/p/UGJY5p490Gs

答案2

得分: 0

我们需要:

  1. 将Person映射到PersonOut
  2. 将ChildData映射到ChildDataOut
func childDataToChildDataOut(childData ChildData) ChildDataOut {
	return ChildDataOut{
		ChildName: childData.ChildName,
		Age:       childData.Age,
		FavColor:  childData.FavColor,
	}
}

func personToPersonOut(person Person) PersonOut {
	var children []ChildDataOut

	for _, childData := range person.Children {
		childDataOut := childDataToChildDataOut(childData)
		children = append(children, childDataOut)
	}

	return PersonOut{
		FirstName: person.FirstName,
		LastName:  person.LastName,
		Children:  children,
	}
}
英文:

https://go.dev/play/p/l4O8KBNrm1T

We need to:

  1. Mapping Person to PersonOut
  2. Mapping ChildData to ChildDataOut
func childDataToChildDataOut(childData ChildData) ChildDataOut {
	return ChildDataOut{
		ChildName: childData.ChildName,
		Age:       childData.Age,
		FavColor:  childData.FavColor,
	}
}

func personToPersonOut(person Person) PersonOut {
	var children []ChildDataOut

	for _, childData := range person.Children {
		childDataOut := childDataToChildDataOut(childData)
		children = append(children, childDataOut)
	}

	return PersonOut{
		FirstName: person.FirstName,
		LastName:  person.LastName,
		Children:  children,
	}
}

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

发表评论

匿名网友

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

确定