46

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?

4 Answers 4

102

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.

Sign up to request clarification or add additional context in comments.

1 Comment

what if out is an array and you don't know how many elements are there in json.
33

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

Comments

5

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.

Comments

2

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
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.