json - 如何解组可以是字符串 * 或 * 字符串数组的不一致 JSON 字段?
问题描述
我在解组一些我无法控制的 Json 时遇到问题。有一个字段 99% 的时间是一个字符串,但偶尔是一个数组。
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 struct 字段 MyListItem.display_name
解决方案
使用json.RawMessage捕获变化的字段。
使用 json "-" 名称对DisplayName
解码器隐藏字段。应用程序将在顶级 JSON 解码后填充此字段。
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
}
根据原始数据的类型解组显示名称:
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
:
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 数据。
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)
}
这是将值作为字符串切片而不是作为单个字符串获取值的代码,其中值由 & 连接。
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 '[':
if err := json.Unmarshal(data, (*[]string)(ms)); err != nil {
return err
}
}
}
return nil
}
推荐阅读
- pyspark - 将大型数据框导出到 Power BI 可以使用的单个文件的最佳方式
- c++ - 如何使用 Handler 和 boost::asio::async_result
- html - 在外部样式表文件上分配样式的问题
- delphi - Indy TCP 客户端-服务器不适用于 OpenSSL
- r - 预测包不适用于 Databricks(R 版 3.5.2)
- java - 我可以在同一个 jvm 上使用 kryo 输出对完全相同的类的实例进行深度比较吗?
- cron - 如何从今天或 X 日期开始每 3 周创建一个 cron 计划
- python - 即使在成功连接并在 kafka 消费者控制台中获取消息后,也无法使用来自 kafka 主题的消息(使用 Python)
- verilog - 关于 Verilog 中的 $monitor,粗体线是什么意思?
- javascript - 如何要求用户打开他们的蓝牙?使用 javascript