encoding/encode.go
func ToStringInterfaceMap(tagName string, input interface{}, properties ...string) (map[string]interface{}, error) {
	vmap := imap(make(map[string]interface{}))
	s := structs.New(input)
	s.TagName = tagName
	if len(properties) == 0 {
		properties = s.Names()
	}

	for _, field := range s.Fields() {
		if !field.IsExported() {
			continue
		}

		if !stringInSlice(field.Name(), properties) {
			continue
		}

		fieldName, opts := parseTag(field.Tag(tagName))
		squash, omitempty, include := opts.Has("squash"), opts.Has("omitempty"), opts.Has("include")
		if !squash && (fieldName == "" || fieldName == "-") {
			continue
		}

		val := field.Value()

		if omitempty {
			if field.IsZero() {
				continue
			}
			if z, ok := val.(isZeroer); ok && z.IsZero() {
				continue
			}
			if z, ok := val.(isEmptier); ok && z.IsEmpty() {
				continue
			}
		}

		kind := field.Kind()
		if kind == reflect.Ptr {
			v := reflect.ValueOf(val)
			if v.IsNil() {
				if fieldName != "" && fieldName != "-" {
					vmap.Set(fieldName, nil)
				}
				continue
			}
			elem := v.Elem()
			kind = elem.Kind()
			val = elem.Interface()
		}

		if (squash || include) && kind == reflect.Struct {
			var newProperties []string
			for _, prop := range properties {
				if strings.HasPrefix(prop, field.Name()+".") {
					newProperties = append(newProperties, strings.TrimPrefix(prop, field.Name()+"."))
				}
			}
			m, err := ToStringInterfaceMap(tagName, val, newProperties...)
			if err != nil {
				return nil, err
			}

			var prefix string
			if !squash {
				prefix = fieldName + "."
			}

			for k, v := range m {
				vmap.Set(prefix+k, v)
			}
			continue
		}

		vmap.Set(fieldName, val)
	}
	return vmap, nil
}
func ToStringInterfaceMap(tagName string, input interface{}, properties ...string) (map[string]interface{}, error) {
	vmap := imap(make(map[string]interface{}))
	s := structs.New(input)
	s.TagName = tagName
	if len(properties) == 0 {
		properties = s.Names()
	}

	for _, field := range s.Fields() {
		if !field.IsExported() {
			continue
		}

		if !stringInSlice(field.Name(), properties) {
			continue
		}

		fieldName, opts := parseTag(field.Tag(tagName))
		squash, omitempty, include := opts.Has("squash"), opts.Has("omitempty"), opts.Has("include")
		if !squash && (fieldName == "" || fieldName == "-") {
			continue
		}

		val := field.Value()

		if omitempty {
			if field.IsZero() {
				continue
			}
			if z, ok := val.(isZeroer); ok && z.IsZero() {
				continue
			}
			if z, ok := val.(isEmptier); ok && z.IsEmpty() {
				continue
			}
		}

		kind := field.Kind()
		if kind == reflect.Ptr {
			v := reflect.ValueOf(val)
			if v.IsNil() {
				if fieldName != "" && fieldName != "-" {
					vmap.Set(fieldName, nil)
				}
				continue
			}
			elem := v.Elem()
			kind = elem.Kind()
			val = elem.Interface()
		}

		if (squash || include) && kind == reflect.Struct {
			var newProperties []string
			for _, prop := range properties {
				if strings.HasPrefix(prop, field.Name()+".") {
					newProperties = append(newProperties, strings.TrimPrefix(prop, field.Name()+"."))
				}
			}
			m, err := ToStringInterfaceMap(tagName, val, newProperties...)
			if err != nil {
				return nil, err
			}

			var prefix string
			if !squash {
				prefix = fieldName + "."
			}

			for k, v := range m {
				vmap.Set(prefix+k, v)
			}
			continue
		}

		vmap.Set(fieldName, val)
	}
	return vmap, nil
}
// ToStringInterfaceMap encodes fields tagged with tagName in input into map[string]interface{}. Optional argument properties specifies fields to encode.