猿问

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

我有一个简单的类型,它在 Go 中实现了子类型整数常量到字符串的转换,反之亦然。我希望能够将 JSON 中的字符串自动解组为这种类型的值。我不能,因为 UnmarshalJSON 没有给我返回或修改标量值的方法。它需要一个结构,其成员由 UnmarshalJSON 设置。",string" 方法不适用于内置标量类型以外的其他类型。有没有办法为派生的标量类型正确实现 UnmarshalJSON?


这是我所追求的一个例子。我希望它打印“Hello Ralph”四次,但它打印“Hello Bob”四次,因为 PersonID 没有改变。


package main


import (

    "encoding/json"

    "fmt"

)


type PersonID int


const (

    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())

        }

    }

}


aluckdog
浏览 173回答 2
2回答

江户川乱折腾

对解组方法使用指针接收器。如果使用值接收器,则在方法返回时对接收器的更改将丢失。unmarshal 方法的参数是 JSON 文本。解组 JSON 文本以获得删除所有 JSON 引用的纯字符串。func (intValue *PersonID) UnmarshalJSON(data []byte) error {&nbsp; var s string&nbsp; if err := json.Unmarshal(data, &s); err != nil {&nbsp; &nbsp; return err&nbsp; }&nbsp; *intValue = Lookup(s)&nbsp; return nil}JSON 标记与示例 JSON 之间存在不匹配。我更改了 JSON 以匹配标签,但您可以通过其他方式更改它。if err := json.Unmarshal([]byte(`{"person": "Ralph", "count": 4, "greeting": "Hello"}`), &m); err != nil {playground example

扬帆大鱼

这是基于我的评论的答案。我不确定这正是您想要做的,因为您的一些问题措辞让我感到困惑,但基本思想是将解组和转换分成两个不同的步骤。首先将原始数据解组为兼容类型,然后转换为另一种类型或丰富您已经拥有的类型,如下例所示。欢迎您在自定义实现中隐藏此行为UnmarshalJSON如果你愿意,但我个人建议不要这样做。这是我的两个原因;1) 它只是与 Go 的显式冗长编码风格不一致 2) 我鄙视高度混淆的包/库/语言,它们为你做这样的事情,因为迟早它会咬你的屁股,比添加 1 花费更多在几个地方有一行额外的代码(比如几个小时试图调试对你来说毫无意义的东西)。type MyType struct {&nbsp; &nbsp; Id&nbsp; &nbsp;PersonID&nbsp; &nbsp; Name&nbsp; &nbsp; &nbsp;string&nbsp; &nbsp;`json: "name"`&nbsp;&nbsp; &nbsp; Count&nbsp; &nbsp; int&nbsp; &nbsp; &nbsp; `json: "count"`&nbsp; &nbsp; Greeting string&nbsp; &nbsp;`json: "greeting"`}func main() {&nbsp; &nbsp; var m MyType&nbsp; &nbsp; if err := json.Unmarshal([]byte(`{"name": "Ralph", "count": 4, "greeting": "Hello"}`), &m); err != nil {&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(err)&nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; m.Id = Lookup(m.Name) // see this isn't unmarshalling&nbsp; &nbsp; &nbsp; &nbsp; // better to take the data as is and do transformation separate&nbsp; &nbsp; &nbsp; &nbsp; for i := 0; i < m.Count; i++ {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(m.Greeting, m.Person.Name())&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}
随时随地看视频慕课网APP

相关分类

Go
我要回答