【问题标题】:GoLang structure doesn't unmarshal properly when using a custom unmarshal for a nested struct对嵌套结构使用自定义解组时,GoLang 结构无法正确解组
【发布时间】:2018-09-12 11:24:57
【问题描述】:

我们需要为嵌套在多个其他不需要自定义解组器的结构中的结构使用自定义解组器。我们有很多类似于下面定义的B 结构的结构(类似于嵌套A)。代码的输出为true false 0(预期为true false 2)。有什么想法吗?


Go Playground 示例 here.

package main

import (
    "fmt"
    "encoding/json"
)

type A struct {
    X bool `json:"x"`
    Y bool `json:"y"`
}

type B struct {
    A
    Z int `json:"z"`
}

func (a *A) UnmarshalJSON(bytes []byte) error {
    var aa struct {
        X string `json:"x"`
        Y string `json:"y"`
    }
    json.Unmarshal(bytes, &aa)

    a.X = aa.X == "123"
    a.Y = aa.Y == "abc"
    return nil
}

const myJSON = `{"x": "123", "y": "fff", "z": 2}`

func main() {
    var b B
    json.Unmarshal([]byte(myJSON), &b)
    fmt.Print(b.X," ",b.Y," ",b.Z)
}

编辑:问题被标记为重复 here 但将 A 设为显式字段会使我们的 API 混乱。同样在将A 设为显式字段后,结果为false false 2,所以它根本没有帮助。

【问题讨论】:

  • 为什么A 中有布尔值,而 JSON 中有字符串?如果您有匹配的类型,那么您根本不需要编写解组器。也许您可以为A 提供一个方法,该方法根据已有的字符串值返回布尔值?
  • @svsd 这只是一个示例,我们从前端收到一个字符串,但我们需要一个通过后端字符串识别的结构。不幸的是,匹配类型在这里不是一个选项。
  • (1) 使 B 嵌入另一个结构 C,其中包含 A 中未包含的字段 (2) 为 B 编写一个 UnmarshalJSON() 方法,将相同的 JSON 解组到 B.A 和 B.C。请参阅play.golang.org/p/roF6hKZJ8Bc 使用不在 A 中的字段定义另一个类型 C 的优点是您可以将其解组委托给 json 包。 (假设你有约束阻止你匹配 A 和 JSON 中的类型)
  • 刚刚根据评论添加了一个答案,因为该问题不再被标记为重复。

标签: json go struct nested unmarshalling


【解决方案1】:

由于B 嵌入AA.UnmarshalJSON() 暴露为B.UnmarshalJSON()。因此,B 实现了json.Unmarshaler,结果json.Unmarshal() 调用B.UnmarshalJSON(),它只解组A 的字段。这就是 B.Z 没有从 JSON 中设置的原因。

这是我能想到的最简单的方法,可以根据您不更改 A 中的数据类型的约束条件使其工作:

  1. 让 B 嵌入另一个结构 C,其中包含 A 中未包含的字段。
  2. 为 B 编写一个 UnmarshalJSON() 方法,将相同的 JSON 解组到 B.A 和 B.C。使用不在 A 中的字段定义另一个类型 C 的优点是您可以将解组委托给 json 包。

使用新的B.UnmarshalJSON() 方法,您现在可以完全控制对A 之外的字段进行解组。

type A struct {
    X bool `json:"x"`
    Y bool `json:"y"`
}

func (a *A) UnmarshalJSON(bytes []byte) error {
    // the special unmarshalling logic here
}

type C struct {
    Z int `json:"z"`
}

type B struct {
    A
    C
}

func (b *B) UnmarshalJSON(bytes []byte) error {
    if err := json.Unmarshal(bytes, &b.A); err != nil {
        return err
    }
    if err := json.Unmarshal(bytes, &b.C); err != nil {
        return err
    }
    return nil
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多