【问题标题】:Unmarshal 2 different structs in a slice在一个切片中解组 2 个不同的结构
【发布时间】:2018-02-09 02:40:45
【问题描述】:

我输入的 json 数据是这样的(无法更改,来自外部资源):

[{
   "Url": "test.url",
   "Name": "testname"
},{ 
   "FormName": "Test - 2018",
   "FormNumber": 43,
   "FormSlug": "test-2018"
}]

我有两个结构总是匹配数组中的数据:

type UrlData struct{
  "Url"  string `json:Url` 
  "Name" string `json:Name` 
}

type FormData struct{
  "FormName"  string `json:FormName` 
  "FormNumber" string `json:FormNumber` 
  "FormSlug" string `json:FormSlug`
}

显然下面的代码将不起作用,但是否可以在顶层(或其他)声明如下内容:

type ParallelData [
 urlData UrlData
 formData FormData
]

【问题讨论】:

  • 就像关于结构标签的一般说明,a)它们在写入时是无效的,它应该是例如json:"Url"(注意引号)和 b) 它们完全没有必要,JSON 中字段的默认名称是结构中字段的名称,因此显式给出相同名称的结构标记是多余的。跨度>

标签: arrays go struct


【解决方案1】:

使用两步过程进行解组。首先,解组任意 JSON 列表,然后将该列表的第一个和第二个元素解组为各自的类型。

您可以在名为 UnmarshalJSON 的方法中实现该逻辑,从而实现the json.Unmarshaler interface。这将为您提供您正在寻找的复合类型:

type ParallelData struct {
    UrlData  UrlData
    FormData FormData
}

// UnmarshalJSON implements json.Unmarshaler.
func (p *ParallelData) UnmarshalJSON(b []byte) error {
    var records []json.RawMessage
    if err := json.Unmarshal(b, &records); err != nil {
        return err
    }

    if len(records) < 2 {
        return errors.New("short JSON array")
    }

    if err := json.Unmarshal(records[0], &p.UrlData); err != nil {
        return err
    }

    if err := json.Unmarshal(records[1], &p.FormData); err != nil {
        return err
    }

    return nil
}

在操场上试试看:https://play.golang.org/p/QMn_rbJj-P-

【讨论】:

  • 很好的答案,输出完全符合预期!感谢彼得和其他所有回复的人!
【解决方案2】:

我认为Answer of Peter 很棒。

选项 1:

type ParallelData [
 urlData UrlData
 formData FormData
]

如果您需要上述结构,则可以将其定义为

type UrlData struct {
    Url  string `json:"Url,omitempty"`
    Name string `json:"Name,omitempty"`
}
type FormData struct {
    FormName   string `json:"FormName,omitempty"`
    FormNumber string `json:"FormNumber,omitempty"`
    FormSlug   string `json:"FormSlug,omitempty"`
}
type ParallelData struct {
    UrlData  UrlData  `json:"UrlData,omitempty"`
    FormData FormData `json:"FormData,omitempty"`
}

在这种情况下,你的 json 看起来像

[  
   {  
      "UrlData":{  
         "Url":"test.url",
         "Name":"testname"
      }
   },
   {  
      "FormData":{  
         "FormName":"Test - 2018",
         "FormNumber":"43",
         "FormSlug":"test-2018"
      }
   }
]

选项 2:

您已提供以下 json:

[  
   {  
      "Url":"test.url",
      "Name":"testname"
   },
   {  
      "FormName":"Test - 2018",
      "FormNumber":43,
      "FormSlug":"test-2018"
   }
]

如果你的 json 真的像,那么你可以使用下面的struct

type UrlData struct {
    Url  string `json:Url`
    Name string `json:Name`
}

type FormData struct {
    FormName   string `json:FormName`
    FormNumber int    `json:FormNumber`
    FormSlug   string `json:FormSlug`
}

type ParallelData struct {
    UrlData
    FormData
}

对于这两个选项,您可以像这样解组您的 json

var parallelData []ParallelData
err := json.Unmarshal([]byte(str), &parallelData)
if err != nil {
    panic(err)
}
fmt.Println(parallelData)

请参阅 playground

中的选项 1

请参阅 playground 中的选项 2

【讨论】:

  • 正如我对已删除答案的评论,当编组和协议禁止omitempty 时(即它需要存在空字符串),这将导致麻烦。而且它并不是真正的改变输入的选择。
  • @leafbebop,如果我从struct 中删除omitempty,你会怎么想。这也可以正常工作。正确的?因为,OP 正在从 json 编组到 struct。没关系。对吗?
  • 你可以试试。我认为它会同时打印结构并变得更加混乱。
  • @leafbebop,有没有其他方法可以使用两个结构并在没有omitempty的情况下获得干净的json?
  • @AbuHanifa, FormName *string 'json:"FormName,omitempty"' 你也可以使用这个。然后如果你设置FormName=nil,它将在 JSON 中被忽略。如果您设置FormName="",它将是JSON。
【解决方案3】:

您可以解组为 map[string]interface{},例如:

type ParallelData map[string]interface{}

func main() {
    textBytes := []byte(`[
    {
      "Url": "test.url",
      "Name": "testname" 
    },
    {
      "FormName": "Test - 2018",
      "FormNumber": 43,
      "FormSlug": "test-2018"
    }]`)
    var acc []ParallelData
    json.Unmarshal(textBytes, &acc)

    fmt.Printf("%+v", acc)
}

输出:

=> [map[Url:test.url Name:testname] map[FormName:Test - 2018 FormNumber:43 FormSlug:test-2018]]

Playground

【讨论】:

  • 它不使用这两个结构。我认为最好删除定义以避免混淆?
  • 感谢您的回复。但是有没有可能把这个想法更进一步,并以某种方式将每个映射的接口转换为它们各自的结构?
  • 我认为你可以通过某种重新编组,但有什么意义呢?
  • 我正在使用 AWS Step Functions 和 lambda 包自动编组 json 输入。不幸的是,这是通过并行步进函数将数据传递给我的代码的方式。这就是为什么理想情况下它需要是一个顶级定义的数组,但这似乎在 golang 中是不可能的......
  • 我仍然不清楚为什么你需要结构以及为什么 array []map[string]interface{} 不适合你。
猜你喜欢
  • 2017-07-23
  • 1970-01-01
  • 1970-01-01
  • 2019-04-12
  • 2023-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多