英文:
How to specify default values when parsing JSON in Go
问题
我想在Go语言中解析一个JSON对象,但是希望为未给定的字段指定默认值。例如,我有以下结构体类型:
type Test struct {
A string
B string
C string
}
A、B和C的默认值分别为"a"、"b"和"c"。这意味着当我解析以下JSON时:
{"A": "1", "C": 3}
我希望得到以下结构体:
Test{A: "1", B: "b", C: "3"}
是否可以使用内置的encoding/json
包实现这个功能?否则,是否有任何具有此功能的Go库可用?
英文:
I want to parse a JSON object in Go, but want to specify default values for fields that are not given. For example, I have the struct type:
type Test struct {
A string
B string
C string
}
The default values for A, B, and C, are "a", "b", and "c" respectively. This means that when I parse the json:
{"A": "1", "C": 3}
I want to get the struct:
Test{A: "1", B: "b", C: "3"}
Is this possible using the built-in package encoding/json
? Otherwise, is there any Go library that has this functionality?
答案1
得分: 92
这可以通过使用encoding/json实现:在调用json.Unmarshal
时,您不需要给它一个空结构体,可以给它一个带有默认值的结构体。
对于您的示例:
var example []byte = []byte(`{"A": "1", "C": "3"}`)
out := Test{
A: "default a",
B: "default b",
// C的默认值将是"",即字符串的空值
}
err := json.Unmarshal(example, &out) // <--
if err != nil {
panic(err)
}
fmt.Printf("%+v", out)
运行此示例将返回{A:1 B:default b C:3}
。
正如您所看到的,json.Unmarshal(example, &out)
将JSON解组为out
,覆盖了JSON中指定的值,但保持其他字段不变。
英文:
This is possible using encoding/json: when calling json.Unmarshal
, you do not need to give it an empty struct, you can give it one with default values.
For your example:
var example []byte = []byte(`{"A": "1", "C": "3"}`)
out := Test{
A: "default a",
B: "default b",
// default for C will be "", the empty value for a string
}
err := json.Unmarshal(example, &out) // <--
if err != nil {
panic(err)
}
fmt.Printf("%+v", out)
Running this example returns {A:1 B:default b C:3}
.
As you can see, json.Unmarshal(example, &out)
unmarshals the JSON into out
, overwriting the values specified in the JSON, but leaving the other fields unchanged.
答案2
得分: 27
如果您有一个Test
结构体的列表或映射,那么接受的答案就不再适用了,但可以通过添加一个UnmarshalJSON
方法来轻松扩展它:
func (t *Test) UnmarshalJSON(data []byte) error {
type testAlias Test
test := &testAlias{
B: "default B",
}
err := json.Unmarshal(data, test)
if err != nil {
return err
}
*t = Test(*test)
return nil
}
testAlias
是为了防止对UnmarshalJSON
的递归调用。这是因为新类型没有定义方法。
链接:https://play.golang.org/p/qiGyjRbNHg
英文:
In case u have a list or map of Test
structs the accepted answer is not possible anymore but it can easily be extended with a UnmarshalJSON method:
func (t *Test) UnmarshalJSON(data []byte) error {
type testAlias Test
test := &testAlias{
B: "default B",
}
err := json.Unmarshal(data, test)
if err != nil {
return err
}
*t = Test(*test)
return nil
}
The testAlias is needed to prevent recursive calls to UnmarshalJSON. This works because a new type has no methods defined.
答案3
得分: 4
你也可以实现UnmarshalJSON
接口并在其中设置默认值。
func (test *Test) UnmarshalJSON(data []byte) error {
test.A = "1"
test.B = "2"
test.C = "3"
type tempTest Test
return json.Unmarshal(data, (*tempTest)(test))
}
需要使用tempTest
类型来避免递归调用UnmarshalJSON
。
英文:
You can also implement the UnmarshalJSON
interface and set the default values in it.
func (test *Test) UnmarshalJSON(data []byte) error {
test.A = "1"
test.B = "2"
test.C = "3"
type tempTest Test
return json.Unmarshal(data, (*tempTest)(test))
}
The type tempTest
is needed so that UnmarshalJSON
is not called recursively.
答案4
得分: 1
json.Unmarshal
与默认值一起使用简单而干净,就像Christian和JW给出的答案一样,但它也有一些缺点。
- 首先,它将字段的默认值与解析逻辑紧密地联系在一起。可以想象,我们希望让用户代码在后续设置其默认值;但现在,默认值必须在解组之前设置。
- 第二个缺点是它只适用于简单的情况。如果我们的Options结构体具有其他结构体的切片或映射,我们无法以这种方式填充默认值。
另一个选项是使用指针字段的默认值
type Test struct {
A *string
B *string
C *string
}
js := []byte(`{"A": "1", "C": "3"}`)
var t Test
if err := json.Unmarshal(js, &t); err != nil {
fmt.Println(err)
}
if t.B == nil {
var defaultB = "B"
t.B = &defaultB
}
英文:
The json.Unmarshal
with a default value is simple and clean like the answers given by Christian and JW., but it has some downsides.
- First, it strongly ties the default values of fields with the parsing logic. It's conceivable that we want to let user code down the line set its defaults; right now, the defaults have to be set before unmarshaling.
- The second downside is that it only works in simple cases. If our Options struct has a slice or map of other structs, we can't populate defaults this way.
Another option is Default values with pointer fields
type Test struct {
A *string
B *string
C *string
}
js := []byte(`{"A": "1", "C": "3"}`)
var t Test
if err := json.Unmarshal(js, &t); err != nil {
fmt.Println(err)
}
if t.B == nil {
var defaultB = "B"
t.B = &defaultB
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论