如何解组不一致的JSON字段,该字段可以是字符串*或*字符串数组? [英] How to Unmarshal an inconsistent JSON field that can be a string *or* an array of string?
问题描述
我在解组一些我无法控制的Json时遇到了麻烦. 在一个字段中,有99%的时间是字符串,但偶尔是数组.
I am having trouble Unmarshalling some Json I don't have control over. There is one field that 99% of the time is a string but occasionally is an array.
type MyListItem struct {
Date string `json:"date"`
DisplayName string `json:"display_name"`
}
type MyListings struct {
CLItems []MyListItem `json:"myitems"`
}
var mylist MyListings
err = json.Unmarshal(jsn, &mylist)
if err != nil {
fmt.Print("JSON:\n%s\n error:%v\n", string(jsn),err)
return
}
Json如下:
{
"date": "30 Apr",
"display_name": "Mr Smith"
},
{
"date": "30 Apr",
"display_name": ["Mr Smith", "Mr Jones"],
}
错误:json:无法将数组解组到字符串类型的Go结构字段MyListItem.display_name
error: json: cannot unmarshal array into Go struct field MyListItem.display_name of type string
推荐答案
使用 json.RawMessage 捕获变化的字段.
Use json.RawMessage to capture the varying field.
使用json-"名称对解码器隐藏DisplayName
字段.顶级JSON解码后,应用程序将填充此字段.
Use the json "-" name to hide the DisplayName
field from decoder. The application will fill this field after the top-level JSON is decoded.
type MyListItem struct {
Date string `json:"date"`
RawDisplayName json.RawMessage `json:"display_name"`
DisplayName []string `json:"-"`
}
解组顶级JSON:
var li MyListItem
if err := json.Unmarshal(data, &li); err != nil {
// handle error
}
根据原始数据的类型取消对显示名称的编组:
Unmarshal the display name depending on the type of the raw data:
if len(li.RawDisplayName) > 0 {
switch li.RawDisplayName[0] {
case '"':
if err := json.Unmarshal(li.RawDisplayName, &li.DisplayName); err != nil {
// handle error
}
case '[':
var s []string
if err := json.Unmarshal(li.RawDisplayName, &s); err != nil {
// handle error
}
// Join arrays with "&" per OP's comment on the question.
li.DisplayName = strings.Join(s, "&")
}
}
将以上内容合并到for循环中以处理MyListings
:
Incorporate the above into a for loop to handle MyListings
:
var listings MyListings
if err := json.Unmarshal([]byte(data), &listings); err != nil {
// handle error
}
for i := range listings.CLItems {
li := &listings.CLItems[i]
if len(li.RawDisplayName) > 0 {
switch li.RawDisplayName[0] {
case '"':
if err := json.Unmarshal(li.RawDisplayName, &li.DisplayName); err != nil {
// handle error
}
case '[':
var s []string
if err := json.Unmarshal(li.RawDisplayName, &s); err != nil {
// handle error
}
li.DisplayName = strings.Join(s, "&")
}
}
}
如果数据模型中有多个位置,值可以是字符串或[]字符串,则将逻辑封装为类型可能会有所帮助.在 json.Unmarshaler 接口的实现中解析JSON数据.
If there's more than one place in the data model where a value can be a string or []string, it can be helpful to encapsulate the logic in a type. Parse the JSON data in an implementation of the json.Unmarshaler interface.
type multiString string
func (ms *multiString) UnmarshalJSON(data []byte) error {
if len(data) > 0 {
switch data[0] {
case '"':
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
*ms = multiString(s)
case '[':
var s []string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
*ms = multiString(strings.Join(s, "&"))
}
}
return nil
}
像这样使用它:
type MyListItem struct {
Date string `json:"date"`
DisplayName multiString `json:"display_name"`
}
type MyListings struct {
CLItems []MyListItem `json:"myitems"`
}
var listings MyListings
if err := json.Unmarshal([]byte(data), &listings); err != nil {
log.Fatal(err)
}
这篇关于如何解组不一致的JSON字段,该字段可以是字符串*或*字符串数组?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!