@@ -6,6 +6,7 @@ package json
66
77import (
88 "fmt"
9+ "reflect"
910
1011 "github.com/go-json-experiment/json/internal"
1112 "github.com/go-json-experiment/json/internal/jsonflags"
@@ -245,20 +246,47 @@ func WithUnmarshalers(v *Unmarshalers) Options {
245246 return (* unmarshalersOption )(v )
246247}
247248
249+ // WithOption constructs a user-defined option value.
250+ // Later occurrences of an option of a particular type overrides
251+ // prior occurrences of an option of the exact same type.
252+ // The type T must be a declared type in a package or
253+ // a pointer to such a type.
254+ //
255+ // A user-defined option can be constructed using:
256+ //
257+ // var v mypkg.MyType = ...
258+ // opts := json.WithOption(v)
259+ //
260+ // The option value can be retrieved using:
261+ //
262+ // v, ok := json.GetOption(opts, json.WithOption[mypkg.MyType])
263+ func WithOption [T any ](v T ) Options {
264+ t := reflect .TypeFor [T ]()
265+ if t .PkgPath () == "" && (t .Kind () != reflect .Pointer || t .Elem ().PkgPath () == "" ) {
266+ panic (fmt .Sprintf ("%T must a declared type or a pointer to such a type" , v ))
267+ }
268+ // TODO: Limit this to non-interface types?
269+ return userOption [T ]{v }
270+ }
271+
248272// These option types are declared here instead of "jsonopts"
249273// to avoid a dependency on "reflect" from "jsonopts".
250274type (
251275 marshalersOption Marshalers
252276 unmarshalersOption Unmarshalers
277+ userOption [T any ] struct { v T }
253278)
254279
255280func (* marshalersOption ) JSONOptions (internal.NotForPublicUse ) {}
256281func (* unmarshalersOption ) JSONOptions (internal.NotForPublicUse ) {}
282+ func (userOption [T ]) JSONOptions (internal.NotForPublicUse ) {}
283+ func (userOption [T ]) key () any { return reflect .TypeFor [T ]() }
284+ func (v userOption [T ]) val () any { return v .v }
257285
258286// Inject support into "jsonopts" to handle these types.
259287func init () {
260288 jsonopts .GetUnknownOption = func (src * jsonopts.Struct , zero jsonopts.Options ) (any , bool ) {
261- switch zero .(type ) {
289+ switch zero := zero .(type ) {
262290 case * marshalersOption :
263291 if ! src .Flags .Has (jsonflags .Marshalers ) {
264292 return (* Marshalers )(nil ), false
@@ -269,6 +297,14 @@ func init() {
269297 return (* Unmarshalers )(nil ), false
270298 }
271299 return src .Unmarshalers .(* Unmarshalers ), true
300+ case interface {
301+ key () any
302+ val () any
303+ }: // implemented by [userOption]
304+ if v , ok := src .UserValues [zero .key ()]; ok {
305+ return v , true
306+ }
307+ return zero .val (), false
272308 default :
273309 panic (fmt .Sprintf ("unknown option %T" , zero ))
274310 }
@@ -281,6 +317,14 @@ func init() {
281317 case * unmarshalersOption :
282318 dst .Flags .Set (jsonflags .Unmarshalers | 1 )
283319 dst .Unmarshalers = (* Unmarshalers )(src )
320+ case interface {
321+ key () any
322+ val () any
323+ }: // implemented by [userOption]
324+ if dst .UserValues == nil {
325+ dst .UserValues = make (map [any ]any )
326+ }
327+ dst .UserValues [src .key ()] = src .val ()
284328 default :
285329 panic (fmt .Sprintf ("unknown option %T" , src ))
286330 }
0 commit comments