json unmarshal hack

shadowing types in unmarshaling

SEAN K.H. LIAO

json unmarshal hack

shadowing types in unmarshaling

go json unmarshal

Sometimes you want to perform some extra validatoon logic in unmarshaling json, or you want to override some types. Here's one way to do it

validation

You want to modify some fields, perform some extra calcs but the json unmarshaling bits remain the same?

You shadow the type, losing its methods (UnmarshalJSON), use the standard unmarshaling logic on it, then copy the data back and perform your modifications.

No need for awkward MyInts in your struct

 1package main
 2
 3import (
 4        "encoding/json"
 5        "fmt"
 6)
 7
 8func main() {
 9        j := []byte(`{"int": -5, "string": "foobar"}`)
10
11        type PlainS S
12
13        var plainS PlainS
14        _ = json.Unmarshal(j, &plainS)
15        fmt.Println(plainS)            //{-5 0 foobar}
16
17        var s S
18        _ = json.Unmarshal(j, &s)
19        fmt.Println(s)                 // {1 -50 foobar}
20
21}
22
23type S struct {
24        Int           int
25        calculatedInt int
26        String        string
27}
28
29func (s *S) UnmarshalJSON(b []byte) error {
30        type S2 S
31        var s2 S2
32        err := json.Unmarshal(b, &s2)
33        if err != nil {
34                return err
35        }
36
37        // perform modifications
38        s2.calculatedInt = s2.Int * 10
39        if s2.Int < 0 {
40                s2.Int = 1
41        }
42        *s = S(s2)
43        return nil
44}

type overrides

Sometimes you just need to override some unmarshaling logic for some fields but you can't or don't have access to add UnmarshalJSON to the types.

You shadow the type, you then create a new type embedding the shadowed type with the overrides you want.

 1package main
 2
 3import (
 4        "encoding/json"
 5        "fmt"
 6)
 7
 8func main() {
 9        j := []byte(`{"int": 5, "string": "foobar"}`)
10
11        type PlainS S
12
13        var plainS PlainS
14        _ = json.Unmarshal(j, &plainS)
15        fmt.Println(plainS)            //{ foobar}
16
17        var s S
18        _ = json.Unmarshal(j, &s)
19        fmt.Println(s)                 // {five foobar}
20
21}
22
23type S struct {
24        Int    string
25        String string
26}
27
28func (s *S) UnmarshalJSON(b []byte) error {
29        type S2 S
30        type S3 struct {
31                S2
32                Int int
33        }
34        var s3 S3
35        err := json.Unmarshal(b, &s3)
36        if err != nil {
37                return err
38        }
39
40        // perform type convs
41        if s3.Int == 5 {
42                s3.S2.Int = "five"
43        }
44        *s = S(s3.S2)
45        return nil
46}