diff --git a/encoder.go b/encoder.go index 52f2c10..04d5d39 100644 --- a/encoder.go +++ b/encoder.go @@ -11,8 +11,9 @@ type encoderFunc func(reflect.Value) string // Encoder encodes values from a struct into url.Values. type Encoder struct { - cache *cache - regenc map[reflect.Type]encoderFunc + cache *cache + regenc map[reflect.Type]encoderFunc + keyOriginalPath bool } // NewEncoder returns a new Encoder with defaults. @@ -26,7 +27,7 @@ func NewEncoder() *Encoder { func (e *Encoder) Encode(src interface{}, dst map[string][]string) error { v := reflect.ValueOf(src) - return e.encode(v, dst) + return e.encode(v, dst, "") } // RegisterEncoder registers a converter for encoding a custom type. @@ -40,6 +41,14 @@ func (e *Encoder) SetAliasTag(tag string) { e.cache.tag = tag } +// KeyOriginalPath causes the keys of nested struct fields to keep their full original +// name consisting of each field name in its path separated with a period. +// +// Allows from nested struct encoded values to be decoded again using schema.Decode. +func (e *Encoder) KeyOriginalPath(keyOriginalPath bool) { + e.keyOriginalPath = keyOriginalPath +} + // isValidStructPointer test if input value is a valid struct pointer. func isValidStructPointer(v reflect.Value) bool { return v.Type().Kind() == reflect.Ptr && v.Elem().IsValid() && v.Elem().Type().Kind() == reflect.Struct @@ -75,7 +84,7 @@ func isZero(v reflect.Value) bool { return v.Interface() == z.Interface() } -func (e *Encoder) encode(v reflect.Value, dst map[string][]string) error { +func (e *Encoder) encode(v reflect.Value, dst map[string][]string, prefix string) error { if v.Kind() == reflect.Ptr { v = v.Elem() } @@ -91,10 +100,13 @@ func (e *Encoder) encode(v reflect.Value, dst map[string][]string) error { if name == "-" { continue } + if prefix != "" && e.keyOriginalPath { + name = prefix + "." + name + } // Encode struct pointer types if the field is a valid pointer and a struct. if isValidStructPointer(v.Field(i)) && !e.hasCustomEncoder(v.Field(i).Type()) { - err := e.encode(v.Field(i).Elem(), dst) + err := e.encode(v.Field(i).Elem(), dst, name) if err != nil { errors[v.Field(i).Elem().Type().String()] = err } @@ -115,7 +127,7 @@ func (e *Encoder) encode(v reflect.Value, dst map[string][]string) error { } if v.Field(i).Type().Kind() == reflect.Struct { - err := e.encode(v.Field(i), dst) + err := e.encode(v.Field(i), dst, name) if err != nil { errors[v.Field(i).Type().String()] = err } diff --git a/encoder_test.go b/encoder_test.go index 092f0de..120d6f8 100644 --- a/encoder_test.go +++ b/encoder_test.go @@ -523,3 +523,37 @@ func TestRegisterEncoderWithPtrType(t *testing.T) { valExists(t, "DateStart", ss.DateStart.time.String(), vals) valExists(t, "DateEnd", "", vals) } + +func TestEncoderKeyOriginalPath(t *testing.T) { + type outter struct { + Inner inner + InnerPtr *inner + } + type nested struct { + Outter outter + } + + nest := nested{ + Outter: outter{ + Inner: inner{ + F12: 12, + }, + InnerPtr: &inner{ + F12: 12, + }, + }, + } + + encoder := NewEncoder() + encoder.KeyOriginalPath(true) + + vals := map[string][]string{} + err := encoder.Encode(nest, vals) + + t.Log(vals) + + noError(t, err) + valsLength(t, 2, vals) + valExists(t, "Outter.Inner.F12", "12", vals) + valExists(t, "Outter.InnerPtr.F12", "12", vals) +}