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

时间:2016-01-05 18:38:22

标签: json go

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

以下是我所追求的一个例子。我想要它打印"你好拉尔夫"四次,但它打印" 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())
        }
    }
}

2 个答案:

答案 0 :(得分:0)

使用指针接收器作为unmarshal方法。如果使用值接收器,则当方法返回时,对接收器的更改将丢失。

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

答案 1 :(得分:0)

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

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