将JSON字符串反序列化为常量

7
在我的Go代码中,我有以下情况:
type Operator int
const (
    UNKNOWN  Operator = iota
    EQUALS
    CONTAINS
    BETWEEN
    DISTANCE
)

type Filter struct {
    Field string      `json:"field"`
    Operator Operator `json:"operator"`
    Values []string   `json:"values"`
}

我期望的JSON应该看起来像下面这样:

{
    "operator": "EQUALS",
    "field": "name",
    "values": [ "John", "Doe" ]
}

我能否创建一个映射,使得 json.Unmarshal 可以设置 Filter 结构体中正确的操作符常量? 我知道 Unmarshaler 接口,但我不认为它可以用于常量值。 我真的很想在我的 Go 代码中保留常量,因为这样可以在传递常量时很好地执行类型检查和一致性。
2个回答

7
"

encoding/json Unmarshaler接口可以与Operator类型一起使用,但其必须具有指针作为其接收器:

"
func (o *Operator) UnmarshalJSON(b []byte) error {
    str := strings.Trim(string(b), `"`)

    switch {
        case str == "CONTAINS":
            *o = CONTAINS

        default:
            *o = UNKNOWN
            // or return an error...
    }

    return nil
}

JSON解码器将从Filter结构中获取Operator字段的地址,并在其上调用UnmarshalJSON方法。
请注意,您可以通过将UnmarshalJSON更改为上面的UnmarshalText来实现更通用的encoding/TextUnmarshaler。
这是一个游乐场示例:http://play.golang.org/p/szcnC6L86u 可以说,使用字符串基础类型代替Operator可能会更简单:http://play.golang.org/p/FCCg1NOeYw

使用操作符指针并在UnmarshalJSON中取消引用它,正是我今天早上想到的主意。非常好用,感谢提到编码/TextUnmarshaler。 - Friedrich Große

3
您可以将您的对象包装在一个 Unmarshaler 中。 例如:
package main

import (
        "encoding/json"
        "fmt"
)

type Operator int

const (
        UNKNOWN Operator = iota
        EQUALS
        CONTAINS
        BETWEEN
        DISTANCE
)

type Filter struct {
        Field       string   `json:"field"`
        RawOperator string   `json:"operator"`
        Operator    Operator `json:"-"`
        Values      []string `json:"values"`
}

type FilterUnmarshaler struct {
        Filter
}

func (f *FilterUnmarshaler) UnmarshalJSON(data []byte) error {
        if err := json.Unmarshal(data, &f.Filter); err != nil {
                return err
        }
        switch f.RawOperator {
        case "UNKOWN":
                f.Operator = UNKNOWN
        case "EQUALS":
                f.Operator = EQUALS
        case "CONTAINS":
                f.Operator = CONTAINS
        case "BETWEEN":
                f.Operator = BETWEEN
        case "DISTANCE":
                f.Operator = DISTANCE
        default:
                return fmt.Errorf("Unkown operator %s", f.RawOperator)
        }
        return nil
}

func main() {
        rawJson := []byte(`
{
    "operator": "BETWEEN",
    "field": "name",
    "values": [ "John", "Doe" ]
}
`)
        val := &FilterUnmarshaler{}
        if err := json.Unmarshal(rawJson, val); err != nil {
                panic(err)
        }
        fmt.Printf("%#v\n", val)
}

没有使用wrapper,我找不到如何避免陷入无限递归的方法。

这个例子展示了如何解组,但你很可能也希望将其应用于编组,以便从具有整数运算符的对象中获得正确的json操作字符串。

你可能还想创建一个映射而不是常量。或者两者都要,创建一个映射并手动填充你的常量及其字符串等效项。


Unmarshaler的想法很好,但当Filter封装在另一个应该被解析的对象中时,我想我会遇到麻烦。无论如何,感谢您的答复。 - Friedrich Große

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接