How to specify default values when parsing JSON in Go

huangapple go评论76阅读模式
英文:

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(`{&quot;A&quot;: &quot;1&quot;, &quot;C&quot;: &quot;3&quot;}`)

out := Test{
	A: &quot;default a&quot;,
	B: &quot;default b&quot;,
	// default for C will be &quot;&quot;, the empty value for a string
}
err := json.Unmarshal(example, &amp;out) // &lt;--
if err != nil {
	panic(err)
}
fmt.Printf(&quot;%+v&quot;, out)

Running this example returns {A:1 B:default b C:3}.

As you can see, json.Unmarshal(example, &amp;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 := &amp;testAlias{
    B: &quot;default B&quot;,
  }

  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.

https://play.golang.org/p/qiGyjRbNHg

答案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 = &quot;1&quot;
    test.B = &quot;2&quot;
    test.C = &quot;3&quot;

    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(`{&quot;A&quot;: &quot;1&quot;, &quot;C&quot;: &quot;3&quot;}`)

	var t Test
	if err := json.Unmarshal(js, &amp;t); err != nil {
		fmt.Println(err)
	}

	if t.B == nil {
		var defaultB = &quot;B&quot;
		t.B = &amp;defaultB
	}

huangapple
  • 本文由 发表于 2015年5月26日 04:18:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/30445479.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定