英文:
Go: Unmarshalling JSON with multiple types
问题
我遇到了一个问题,无法将JSON响应解组为结构体。问题是邮政编码可以返回为字符串或整数。我该如何编写一个解组方法来检查邮政编码是否为整数,并强制将其存储为字符串?
结构体:
type CustomerAddress struct {
Line1 string `json:"line1"`
City string `json:"city"`
State string `json:"state"`
Zip string `json:"zip"`
IsPrimaryAddress string `json:"isPrimaryAddress"`
}
示例JSON:
"address": [
{
"line1": "555 ADDRESS PLACE",
"city": "DALLAS",
"state": "TX",
"isPrimaryAddress": "Y",
"zip": 55555
}
]
解组后,结果应该成功将邮政编码转换为字符串:
"address": [
{
"line1": "555 ADDRESS PLACE",
"city": "DALLAS",
"state": "TX",
"isPrimaryAddress": "Y",
"zip": "55555"
}
]
作为一种尝试,我尝试使用了一个ZipWrapper
。
type CustomerAddress struct {
Line1 string `json:"line1"`
City string `json:"city"`
State string `json:"state"`
Zip ZipWrapper `json:"zip"`
IsPrimaryAddress string `json:"isPrimaryAddress"`
}
type ZipWrapper struct {
Zip string
}
func (w *ZipWrapper) UnmarshalJSON(data []byte) (err error) {
if zip, err := strconv.Atoi(string(data)); err == nil {
w.Zip = strconv.Itoa(zip)
return nil
}
return json.Unmarshal(data, &w.Zip)
}
这个方法几乎可以工作,但是邮政编码现在是CustomerAddress
中的一个嵌套结构,这不是我想要的:
"address": [
{
"line1": "555 ADDRESS PLACE",
"city": "DALLAS",
"state": "TX",
"isPrimaryAddress": "Y",
"zip": {
"Zip": "55555"
}
}
]
有什么想法吗?我觉得这是一个相对简单的任务,但我是一个完全的Go新手,还没有完全理解解组的工作原理。
英文:
I'm having an issue unmarshalling a JSON response into a struct. The problem I'm having is that the zip code can either return as a string or an integer. How do I write an unmarshal method to check if the zip is an int and force it to store it as a string?
Struct:
type CustomerAddress struct {
Line1 string `json:"line1"`
City string `json:"city"`
State string `json:"state"`
Zip string `json:"zip"`
IsPrimaryAddress string `json:"isPrimaryAddress"`
}
Example Json:
address": [
{
"line1": "555 ADDRESS PLACE",
"city": "DALLAS",
"state": "TX",
"isPrimaryAddress": "Y",
"zip": 55555
}
]
After unmarshalling, the result should have the zip successfully converted into a string:
address": [
{
"line1": "555 ADDRESS PLACE",
"city": "DALLAS",
"state": "TX",
"isPrimaryAddress": "Y",
"zip": "55555"
}
]
As an attempt, I tried to use a ZipWrapper.
type CustomerAddress struct {
Line1 string `json:"line1"`
City string `json:"city"`
State string `json:"state"`
Zip ZipWrapper `json:"zip"`
IsPrimaryAddress string `json:"isPrimaryAddress"`
}
type ZipWrapper struct {
Zip string
}
func (w *ZipWrapper ) UnmarshalJSON(data []byte) (err error) {
if zip, err := strconv.Atoi(string(data)); err == nil {
w.Zip = strconv.Itoa(zip)
return nil
}
return json.Unmarshal(data, &w.Zip)
}
This almost worked except the zip is now a nested struct within CustomerAddress which is not what I want:
address": [
{
"line1": "555 ADDRESS PLACE",
"city": "DALLAS",
"state": "TX",
"isPrimaryAddress": "Y",
"zip": {
"Zip": "55555"
}
}
]
Any ideas? I feel like this is a relatively easy task but I'm a complete Go noob and haven't fully wrapped my head around how Unmarshalling works.
答案1
得分: 12
json
包提供了json.Number
类型来实现这个功能:
type CustomerAddress struct {
Line1 string `json:"line1"`
City string `json:"city"`
State string `json:"state"`
Zip json.Number `json:"zip"`
IsPrimaryAddress string `json:"isPrimaryAddress"`
}
如果你需要在没有嵌套结构的情况下自己实现,你可以像json.Number
一样声明类型,使用string
作为底层类型:
type ZipWrapper string
func (w *ZipWrapper) UnmarshalJSON(data []byte) (err error) {
if len(data) > 1 && data[0] == '"' && data[len(data)-1] == '"' {
data = data[1 : len(data)-1]
}
if _, err := strconv.Atoi(string(data)); err != nil {
return err
}
*w = ZipWrapper(string(data))
return nil
}
链接:https://play.golang.org/p/PIKSh2c6Mm
英文:
The json
package provides the json.Number
type to do this:
type CustomerAddress struct {
Line1 string `json:"line1"`
City string `json:"city"`
State string `json:"state"`
Zip json.Number `json:"zip"`
IsPrimaryAddress string `json:"isPrimaryAddress"`
}
https://play.golang.org/p/PIKSh2c6Mm
If you needed to do this yourself without the nested struct, you can declare the type the same way as json.Number
, with string
as the underlying type
type ZipWrapper string
func (w *ZipWrapper) UnmarshalJSON(data []byte) (err error) {
if len(data) > 1 && data[0] == '"' && data[len(data)-1] == '"' {
data = data[1 : len(data)-1]
}
if _, err := strconv.Atoi(string(data)); err != nil {
return err
}
*w = ZipWrapper(string(data))
return nil
}
答案2
得分: 2
Jim在另一个答案中所说的关于将ZipWrapper定义为字符串的意思是,你可以采用与之前相同的方法,但不需要嵌套的结构体。
例如,将字段定义为:
Zip ZipWrapper `json:"zip"`
然后将ZipWrapper
定义为:
type ZipWrapper string
你的UnmarshalJSON
函数可以像这样:
func (w *ZipWrapper) UnmarshalJSON(data []byte) (err error) {
if zip, err := strconv.Atoi(string(data)); err == nil {
str := strconv.Itoa(zip)
*w = ZipWrapper(str)
return nil
}
var str string
err = json.Unmarshal(data, &str)
if err != nil {
return err
}
return json.Unmarshal([]byte(str), w)
}
这是一个可工作的Go playground示例:
https://play.golang.org/p/IlJJRP3x1w
英文:
What Jim is saying in the other answer about defining ZipWrapper as a string is that you can take the same approach you were taking, but without the nested Struct.
Like, define the field like this:
Zip ZipWrapper `json:"zip"`
But then ZipWrapper
is defined like:
type ZipWrapper string
Your UnmarshalJSON
function can be like this:
func (w *ZipWrapper) UnmarshalJSON(data []byte) (err error) {
if zip, err := strconv.Atoi(string(data)); err == nil {
str := strconv.Itoa(zip)
*w = ZipWrapper(str)
return nil
}
var str string
err = json.Unmarshal(data, &str)
if err != nil {
return err
}
return json.Unmarshal([]byte(str), w)
}
Here's a working Go playground:
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论