Skip to content
/ oneof Public

A Go package for marshaling and unmarshaling Go interfaces using github.com/go-json-experiment/json

License

Notifications You must be signed in to change notification settings

dhoelle/oneof

Repository files navigation

oneof

Go Reference Build Status

Package oneof enables marshaling and unmarshaling of Go interface values using the Go JSON V2 experiment (github.com/go-json-experiment/json).

By default, marshaling and unmarshaling an interface value will fail:

var s1 fmt.Stringer = crypto.SHA256
b, _ := json.Marshal(s1) // b == []byte("5")

var s2 fmt.Stringer
err := json.Unmarshal(b, &s2)
fmt.Println(err)
// Output:
// json: cannot unmarshal JSON string into Go value of type fmt.Stringer: cannot derive concrete type for non-empty interface

oneof's MarshalFunc and UnmarshalFunc encode matching Go values alongside a type discriminator, which enables round-trip marshaling and unmarshaling:

// Implementations of fmt.Stringer that our program will marshal and unmarshal,
// keyed by an option type
opts := map[string]fmt.Stringer{
  "crypto.Hash": crypto.Hash(0),
  "net.IP":      net.IP{},
  "url.URL":     &url.URL{},
}

marshalFunc := oneof.MarshalFunc(opts, nil)
var s1 fmt.Stringer = crypto.SHA256
b, _ := json.Marshal(s1, json.WithMarshalers(marshalFunc))
// b == []byte(`{"_type": "crypto.Hash", "_value": 5}`)

unmarshalFunc := oneof.UnmarshalFunc(opts, nil)
var s2 fmt.Stringer
_ = json.Unmarshal(b, &s2, json.WithUnmarshalers(unmarshalFunc))
fmt.Printf("unmarshaled type = %T\n", s2)
fmt.Printf("string output = %s\n", s2.String())
// Output:
// unmarshaled type: crypto.Hash
// string output: SHA-256

Default encoding

By default, MarshalFunc encodes known Go values into a JSON object where:

  • The value of the key "_type" is the type discriminator, and,
  • The value of the key "_value" is the default JSON-encoding of the Go value

For example, given options:

opts := map[string]fmt.Stringer{
  "crypto.Hash": crypto.Hash(0),
  "net.IP":      net.IP{},
  "url.URL":     &url.URL{},
}

... the Go value crypto.SHA256 (which encodes to the JSON number 5), would be encoded by MarshalFunc as:

{
  "_type": "crypto.Hash",
  "_value": 5
}

... and the Go value &url.URL{Scheme: "https", Host: "example.com"}, which encodes to a JSON object, would be encoded by MarshalFunc as:

{
  "_type": "url.URL",
  "_value": {
    "Scheme": "https",
    "Host": "example.com"
    // other url.URL fields omitted
  }
}

Custom encoding

WrappedValue is the interface implemented by containers which can marshal and unmarshal Go types including type information. Configure the WrappedValue used by MarshalFunc and UnmarshalFunc by setting Config.WrapFunc:

cfg := oneof.Config{
  WrapFunc: oneof.WrapNested,
}
marshalFunc := oneof.MarshalFunc(opts, cfg)

If Config.WrapFunc is unset, MarshalFunc and UnmarshalFunc default to WrapNested, which wraps encoded values under the "_value" key.

The oneof package also defines WrapInline, which inlines the fields of JSON object values, e.g.,:

{
  "_type": "url.URL",
  "Scheme": "https",
  "Host": "example.com"
  // other url.URL fields omitted
}

For finer-grained control, you can create your own WrappedValue type, or use CustomValueWrapper (whose method Wrap can be used as Config.WrapFunc)

See the WrapFunc and CustomValueWrapper examples.

Handling missing keys

If oneof encounters a Go type for which there is no matching option key while marshaling, it will return an error.

You can override this behavior by setting Config.ReplaceMissingTypeFunc:

cfg := &oneof.Config{
  ReplaceMissingTypeFunc: func(v any) string {
    return fmt.Sprintf("MISSING_%T", v)
  },
}

With the above Config, MarshalFunc will use the string produced by ReplaceMissingTypeFunc as the option type for missing values, like:

{
  "_type": "MISSING_*crypto.Hash",
  "_value": 5
},

Note

UnmarshalFunc will likely fail to unmarshal output produced by ReplaceMissingTypeFunc. If you need to marshal and unmarshal a Go type, include it in the option set.

About

A Go package for marshaling and unmarshaling Go interfaces using github.com/go-json-experiment/json

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages