英文:
Unmarshalling JSON string into a constant
问题
在我的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代码中的常量,因为这样可以很好地强制进行类型检查和一致性,当我传递它们时。
英文:
In my Go code I have the following situation:
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"`
}
My expected JSON would look like the following:
{
"operator": "EQUALS",
"field": "name",
"values": [ "John", "Doe" ]
}
Can I create a mapping such that json.Unmarshal will set the right operator constant in the Filter struct?
I am aware of the Unmarshaler interface but I don't think this can really be used on a constant value..
I would really like to keep the constants in my go code since this nicely enforces type checking and consistency when I pass them around.
答案1
得分: 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
英文:
The encoding/json Unmarshaler
interface will work with the Operator
type, but it must have a pointer as its receiver:
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
}
The JSON decoder will take the address of the Operator
field from the Filter
struct and invoke the UnmarshalJSON
method on it.
Note that you could implement the more generic encoding/TextUnmarshaler
instead, by changing UnmarshalJSON
to UnmarshalText
above.
Here is a playground example: http://play.golang.org/p/szcnC6L86u
Arguably it might be simpler to use a string base type for Operator instead: http://play.golang.org/p/FCCg1NOeYw
答案2
得分: 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("Unknown 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)
}
在不使用包装器的情况下,我没有找到如何避免无限递归。
这个示例展示了如何解组,但你很可能也希望在编组时应用它,以便从带有整数操作符的对象生成正确的 JSON。
你可能还想创建一个映射而不是使用常量。或者两者都创建,创建一个映射并手动填充它,包括常量和它们的字符串等效项。
英文:
You can wrap your object in an Unmarshaler.
ex:
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)
}
Without using a wrapper, I didn't find how not to fall in an infinite recursion.
This example shows how to unmarshal but you most likely want to apply it to Marshal as well in order to have a correct json with Operator strings from your object with integer operator.
You might also want to create a map instead of const. Or both, create a map and manually populate it with your const and their string equivalent.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论