@@ -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