为了账号安全,请及时绑定邮箱和手机立即绑定

如何为从 Go 中的标量派生的类型实现 UnmarshalJSON?

如何为从 Go 中的标量派生的类型实现 UnmarshalJSON?

Go
aluckdog 2021-12-06 19:13:19
我有一个简单的类型,它在 Go 中实现了子类型整数常量到字符串的转换,反之亦然。我希望能够将 JSON 中的字符串自动解组为这种类型的值。我不能,因为 UnmarshalJSON 没有给我返回或修改标量值的方法。它需要一个结构,其成员由 UnmarshalJSON 设置。",string" 方法不适用于内置标量类型以外的其他类型。有没有办法为派生的标量类型正确实现 UnmarshalJSON?这是我所追求的一个例子。我希望它打印“Hello Ralph”四次,但它打印“Hello Bob”四次,因为 PersonID 没有改变。package mainimport (    "encoding/json"    "fmt")type PersonID intconst (    Bob PersonID = iota    Jane    Ralph    Nobody = -1)var nameMap = map[string]PersonID{    "Bob":    Bob,    "Jane":   Jane,    "Ralph":  Ralph,    "Nobody": Nobody,}var idMap = map[PersonID]string{    Bob:    "Bob",    Jane:   "Jane",    Ralph:  "Ralph",    Nobody: "Nobody",}func (intValue PersonID) Name() string {    return idMap[intValue]}func Lookup(name string) PersonID {    return nameMap[name]}func (intValue PersonID) UnmarshalJSON(data []byte) error {    // The following line is not correct    intValue = Lookup(string(data))    return nil}type MyType struct {    Person   PersonID `json: "person"`    Count    int      `json: "count"`    Greeting string   `json: "greeting"`}func main() {    var m MyType    if err := json.Unmarshal([]byte(`{"person": "Ralph", "count": 4, "greeting": "Hello"}`), &m); err != nil {        fmt.Println(err)    } else {        for i := 0; i < m.Count; i++ {            fmt.Println(m.Greeting, m.Person.Name())        }    }}
查看完整描述

2 回答

?
江户川乱折腾

TA贡献1851条经验 获得超5个赞

对解组方法使用指针接收器。如果使用值接收器,则在方法返回时对接收器的更改将丢失。


unmarshal 方法的参数是 JSON 文本。解组 JSON 文本以获得删除所有 JSON 引用的纯字符串。


func (intValue *PersonID) UnmarshalJSON(data []byte) error {

  var s string

  if err := json.Unmarshal(data, &s); err != nil {

    return err

  }

  *intValue = Lookup(s)

  return nil

}

JSON 标记与示例 JSON 之间存在不匹配。我更改了 JSON 以匹配标签,但您可以通过其他方式更改它。


if err := json.Unmarshal([]byte(`{"person": "Ralph", "count": 4, "greeting": "Hello"}`), &m); err != nil {

playground example


查看完整回答
反对 回复 2021-12-06
?
扬帆大鱼

TA贡献1799条经验 获得超9个赞

这是基于我的评论的答案。我不确定这正是您想要做的,因为您的一些问题措辞让我感到困惑,但基本思想是将解组和转换分成两个不同的步骤。首先将原始数据解组为兼容类型,然后转换为另一种类型或丰富您已经拥有的类型,如下例所示。欢迎您在自定义实现中隐藏此行为UnmarshalJSON如果你愿意,但我个人建议不要这样做。这是我的两个原因;1) 它只是与 Go 的显式冗长编码风格不一致 2) 我鄙视高度混淆的包/库/语言,它们为你做这样的事情,因为迟早它会咬你的屁股,比添加 1 花费更多在几个地方有一行额外的代码(比如几个小时试图调试对你来说毫无意义的东西)。


type MyType struct {

    Id   PersonID

    Name     string   `json: "name"` 

    Count    int      `json: "count"`

    Greeting string   `json: "greeting"`

}



func main() {

    var m MyType

    if err := json.Unmarshal([]byte(`{"name": "Ralph", "count": 4, "greeting": "Hello"}`), &m); err != nil {

        fmt.Println(err)

    } else {

        m.Id = Lookup(m.Name) // see this isn't unmarshalling

        // better to take the data as is and do transformation separate

        for i := 0; i < m.Count; i++ {

            fmt.Println(m.Greeting, m.Person.Name())

        }

    }

}


查看完整回答
反对 回复 2021-12-06
  • 2 回答
  • 0 关注
  • 172 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信