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 (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.
CodePudding user response:
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
CodePudding user response:
https://go.dev/play/p/l4O8KBNrm1T
We need to:
- Mapping Person to PersonOut
- 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,
}
}