Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to specify default values when parsing JSON in Go

Tags:

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?

like image 575
JW. Avatar asked May 25 '15 20:05

JW.


3 Answers

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.

like image 84
JW. Avatar answered Nov 10 '22 15:11

JW.


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.

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

like image 44
Christian Avatar answered Nov 10 '22 15:11

Christian


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
    }
like image 1
zangw Avatar answered Nov 10 '22 13:11

zangw