diff --git a/ygot/render.go b/ygot/render.go index 06b57bf22..95d888e33 100644 --- a/ygot/render.go +++ b/ygot/render.go @@ -665,12 +665,26 @@ func leavesToNotifications(leaves map[*path]interface{}, ts int64, pfx *gnmiPath return []*gnmipb.Notification{n}, nil } +// EncodeTypedValueOpt is an interface implemented by arguments to +// the EncodeTypedValueOpt function. +type EncodeTypedValueOpt interface { + // IsMarshal7951Arg is a market method. + IsEncodeTypedValueOpt() +} + // EncodeTypedValue encodes val into a gNMI TypedValue message, using the specified encoding // type if the value is a struct. -func EncodeTypedValue(val interface{}, enc gnmipb.Encoding) (*gnmipb.TypedValue, error) { +func EncodeTypedValue(val interface{}, enc gnmipb.Encoding, opts ...EncodeTypedValueOpt) (*gnmipb.TypedValue, error) { + jc := &RFC7951JSONConfig{} + for _, opt := range opts { + if cfg, ok := opt.(*RFC7951JSONConfig); ok { + jc = cfg + } + } + switch v := val.(type) { case GoStruct: - return marshalStruct(v, enc) + return marshalStruct(v, enc, jc) case GoEnum: en, err := EnumName(v) if err != nil { @@ -731,7 +745,7 @@ func EncodeTypedValue(val interface{}, enc gnmipb.Encoding) (*gnmipb.TypedValue, // marshalStruct encodes the struct s according to the encoding specified by enc. It // is returned as a TypedValue gNMI message. -func marshalStruct(s GoStruct, enc gnmipb.Encoding) (*gnmipb.TypedValue, error) { +func marshalStruct(s GoStruct, enc gnmipb.Encoding, cfg *RFC7951JSONConfig) (*gnmipb.TypedValue, error) { if reflect.ValueOf(s).IsNil() { return nil, nil } @@ -750,7 +764,8 @@ func marshalStruct(s GoStruct, enc gnmipb.Encoding) (*gnmipb.TypedValue, error) } case gnmipb.Encoding_JSON_IETF: // We always prepend the module name when marshalling within a Notification. - j, err = ConstructIETFJSON(s, &RFC7951JSONConfig{AppendModuleName: true}) + cfg.AppendModuleName = true + j, err = ConstructIETFJSON(s, cfg) encfn = func(s string) *gnmipb.TypedValue { return &gnmipb.TypedValue{Value: &gnmipb.TypedValue_JsonIetfVal{[]byte(s)}} } @@ -946,6 +961,10 @@ type RFC7951JSONConfig struct { // Marshal7951. func (*RFC7951JSONConfig) IsMarshal7951Arg() {} +// IsEncodeTypedValueOpt marks the RFC7951JSONConfig struct as a valid option to +// EncodeTypedValue. +func (*RFC7951JSONConfig) IsEncodeTypedValueOpt() {} + // ConstructIETFJSON marshals a supplied GoStruct to a map, suitable for // handing to json.Marshal. It complies with the convention for marshalling // to JSON described by RFC7951. The supplied args control options corresponding diff --git a/ygot/render_test.go b/ygot/render_test.go index f269c1c20..981a15d89 100644 --- a/ygot/render_test.go +++ b/ygot/render_test.go @@ -3786,6 +3786,7 @@ func TestEncodeTypedValue(t *testing.T) { name string inVal interface{} inEnc gnmipb.Encoding + inArgs []EncodeTypedValueOpt want *gnmipb.TypedValue wantErrSubstring string }{{ @@ -3907,6 +3908,18 @@ func TestEncodeTypedValue(t *testing.T) { "f2mod:config": { "f2": "hello" } +}`)}}, + }, { + name: "struct val - with PreferShadowPath", + inVal: &renderExample{ + Str: String("test-string"), + }, + inEnc: gnmipb.Encoding_JSON_IETF, + inArgs: []EncodeTypedValueOpt{ + &RFC7951JSONConfig{PreferShadowPath: true}, + }, + want: &gnmipb.TypedValue{Value: &gnmipb.TypedValue_JsonIetfVal{[]byte(`{ + "srt": "test-string" }`)}}, }, { name: "struct val - internal json", @@ -3941,7 +3954,7 @@ func TestEncodeTypedValue(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := EncodeTypedValue(tt.inVal, tt.inEnc) + got, err := EncodeTypedValue(tt.inVal, tt.inEnc, tt.inArgs...) if diff := errdiff.Substring(err, tt.wantErrSubstring); diff != "" { t.Fatalf("did not get expected error, %s", diff) }