0

I did a bit of searching for similar posts, but Go JSON unmarshalling is a hot topic and I couldn't see anything specifically for my question among all the other posts.

Is there a way to add/register JSON unmarshalling logic for an existing type -- one defined by an external library?

Example:

import (
    "go.mongodb.org/mongo-driver/bson/primitive"
)

type SomeDBModel struct {
    Created primitive.DateTime
}

# NOTE: primitive.DateTime is an int64 and has implemented MarshalJSON,
# but not UnmarshalJSON.
# It marshals into an RFC 3339 datetime string; I'd like to be able to
# also unmarshal from RFC 3339 strings.

Is there some way I can register an unmarshalling function for primitive.DateTime objects to Go's default JSON unmarshaller? I'd rather not embed primitive.DateTime into a wrapper struct.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
crunk1
  • 2,560
  • 1
  • 26
  • 32
  • @Flimzy Why marked as a dupe? That question/answer uses struct embedding, when I'm asking "without embedding". – crunk1 Mar 25 '20 at 08:40
  • "Is there some way I can register an unmarshalling function for primitive.DateTime " -- No. This would be a serious violation of Go's data protection model. Embedding is the only way, aside from much more cumbersome wrapping methods. – Jonathan Hall Mar 25 '20 at 08:43
  • From one of my comments below: There are other (un)marshallers that solve this using registries, such as BSON (un)marshalling in mongo's go driver: godoc.org/go.mongodb.org/mongo-driver/bson/bsoncodec#Registry. Are there similar paradigms or support libs for JSON? – crunk1 Mar 25 '20 at 08:46
  • Not within the standard library. A library which implements such behavior would qualify as "a much more cumbersome wrapping method". – Jonathan Hall Mar 25 '20 at 08:49
  • @Flimzy, thanks for at least humoring me. I assumed probably not, but thought I would ask. Would like to see registry support in the future. – crunk1 Mar 25 '20 at 08:52
  • I would wager a large percentage of my personal wealth that it will never be added to the standard library. But there's nothing stopping you/anyone from creating their own JSON library which does that. – Jonathan Hall Mar 25 '20 at 09:02

1 Answers1

1

The only way to alter the default (un)marshaler of a type - or add missing functionality - is to create a custom type and write your own methods like so:

type myDateTime primitive.DateTime // custom-type

//
// re-use the MarshalJSON() that comes with `primitive.DateTime`
//
func (t myDateTime) MarshalJSON() ([]byte, error) { return primitive.DateTime(t).MarshalJSON() }

//
// fill in the missing UnmarshalJSON for your custom-type
//
func (t *myDateTime) UnmarshalJSON(b []byte) (err error) {

    var pt time.Time // use time.Time as it comes with `UnmarshalJSON`
    err = pt.UnmarshalJSON(b)
    if err != nil {
        return
    }
    *t = myDateTime(
        primitive.NewDateTimeFromTime(pt),
    )
    return
}

And to use in your own types:

type SomeDBModel struct {
    Created myDateTime // instead of `primitive.DateTime`
}

Working playground example

colm.anseo
  • 19,337
  • 4
  • 43
  • 52
  • 1
    I've thought about doing this, but there are a couple issues: 1) I don't want to have to implement EVERY method for the underlying type (in this case, primitive.DateTime isn't that bad), and 2) the bson library has a map of types -> default BSON encoders/decoders, so creating my own type will cause issues with BSON marshalling/unmarshalling. – crunk1 Mar 25 '20 at 04:10
  • You can use the native type, and move the custom (un)marshalers to the `SomeDBModel` level - converting any problematic fields there. But no matter what way you slice/dice it, the custom logic has be triggered somewhere by a type conversion. – colm.anseo Mar 25 '20 at 04:21
  • @crunk1 There is no "I want" in Go. – Volker Mar 25 '20 at 07:08
  • @Volker very helpful. There are other (un)marshallers that solve this using registries, such as BSON (un)marshalling in mongo's go driver: https://godoc.org/go.mongodb.org/mongo-driver/bson/bsoncodec#Registry. My question was if any one knew if there is something similar for JSON. – crunk1 Mar 25 '20 at 08:37