While it’s not perfect, I love the encoding/json
package in Go’s standard library.
However, as you can see from the video above, it does have its drawbacks. And while
none of the performance issues or bugs have been an issue for me in the past, the work
done on this new version does simplify something I run into frequently, which is
dealing with custom encoding and decoding for standard types.
I deal with a lot of “other people’s API’s”, so I don’t get to make the rules, I just have
to follow them. I get a lot of API’s where instead of using JSON booleans for boolean values,
they use "Y"
or "N"
. And instead of using a consistent format for dates, it’ll use
times with odd formats or a mix of ISO 8601 dates (with no time) on some fields and a full
time on others.
With the current encoding/json
in the standard library, both of these cases require me
to make a new type and implement json.Marshaler
or json.Unmarshaler
. The experimental replacement has a couple of features that really simplify this.
Dealing with Date formats
First, the easy one is dealing with date formats. Rather than having to make a new JSONDate
type and implementing Marshaler
and/or Unmarshaler
, you can now just specify the format in the struct tag:
type Pet struct {
Name string `json:"name"`
DOB time.Time `json:"dob,format:'2006-01-02'"`
}
Dealing with pseudo-booleans
To deal with formatting booleans as Y
or N
, you can use the MarshalFuncV2 or UnmarshalFuncV2 as necessary.
boolEncoder := json.MarshalFuncV2(func(e *jsontext.Encoder, b bool, opts json.Options) error {
if b {
e.WriteToken(jsontext.String("Y"))
} else {
e.WriteToken(jsontext.String("N"))
}
return nil
})
if err := json.MarshalWrite(out, result, json.WithMarshalers(boolEncoder)); err != nil {
...
}
Encoding a boolean as Y/N
And decoding looks similar.
boolDecoder := json.UnmarshalFuncV2(func(d *jsontext.Decoder, b *bool, opts json.Options) error {
*b = false
t, err := d.ReadToken()
if err != nil {
return err
}
if t.Kind() != '"' {
return fmt.Errorf("expected string got %c", t.Kind())
}
if t.String() == "Y" || t.String() == "y" {
*b = true
}
return nil
})
if err := json.UnmarshalRead(in, &result, json.WithUnmarshalers(boolDecoder)); err != nil {
...
}
Final Thoughts
These aren’t the only great things about the working experiment. It’s not even a formal proposal just yet, but I look forward to the conversation getting started. One disappointing thing about the video is that he says if the proposal doesn’t get off the ground, then this project gets abandoned. I certainly hope that’s not the case - even if it’s not chosen as a replacement for encoding/json
, I think there’s enough value here to keep the project going.