encoding/decode.go
func FromStringStringMap(tagName string, base interface{}, input map[string]string) (output interface{}, err error) {
	baseType := reflect.TypeOf(base)

	valType := baseType
	if baseType.Kind() == reflect.Ptr {
		valType = valType.Elem()
	}

	// If we get a pointer in, we'll return a pointer out
	valPtr := reflect.New(valType)
	val := valPtr.Elem()
	output = valPtr.Interface()

	defer func() {
		if err == nil && baseType.Kind() != reflect.Ptr {
			output = reflect.Indirect(reflect.ValueOf(output)).Interface()
		}
	}()

	for i := 0; i < valType.NumField(); i++ {
		field := valType.Field(i)

		if field.PkgPath != "" {
			continue
		}

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

		inputStr, fieldInInput := input[fieldName]

		fieldType := field.Type
		fieldKind := field.Type.Kind()

		isPointerField := fieldKind == reflect.Ptr
		if isPointerField {
			if inputStr == "null" {
				continue
			}
			fieldType = fieldType.Elem()
			fieldKind = fieldType.Kind()
		}

		var iface interface{}

		if (squash || include) && fieldKind == reflect.Struct {
			var subInput map[string]string

			if squash {
				subInput = input
			} else {
				subInput = make(map[string]string)
				for k, v := range input {
					if strings.HasPrefix(k, fieldName+".") {
						subInput[strings.TrimPrefix(k, fieldName+".")] = v
					}
				}

				if len(subInput) == 0 {
					continue
				}
			}

			subOutput, err := FromStringStringMap(tagName, val.Field(i).Interface(), subInput)
			if err != nil {
				return nil, err
			}
			val.Field(i).Set(reflect.ValueOf(subOutput))
			continue
		}

		if !fieldInInput || inputStr == "" {
			continue
		}

		switch fieldKind {
		case reflect.Struct, reflect.Array, reflect.Interface, reflect.Slice, reflect.Map:
			iface, err = unmarshalToType(fieldType, inputStr)
			if err != nil {
				return nil, err
			}
		default:
			iface = decodeToType(fieldKind, inputStr)
		}

		if v, ok := iface.(time.Time); ok {
			iface = v.UTC()
		}

		fieldVal := reflect.ValueOf(iface).Convert(fieldType)

		if isPointerField {
			fieldValPtr := reflect.New(fieldType)
			fieldValPtr.Elem().Set(fieldVal)
			val.Field(i).Set(fieldValPtr)
		} else {
			val.Field(i).Set(fieldVal)
		}
	}

	return output, nil
}
func FromStringStringMap(tagName string, base interface{}, input map[string]string) (output interface{}, err error) {
	baseType := reflect.TypeOf(base)

	valType := baseType
	if baseType.Kind() == reflect.Ptr {
		valType = valType.Elem()
	}

	// If we get a pointer in, we'll return a pointer out
	valPtr := reflect.New(valType)
	val := valPtr.Elem()
	output = valPtr.Interface()

	defer func() {
		if err == nil && baseType.Kind() != reflect.Ptr {
			output = reflect.Indirect(reflect.ValueOf(output)).Interface()
		}
	}()

	for i := 0; i < valType.NumField(); i++ {
		field := valType.Field(i)

		if field.PkgPath != "" {
			continue
		}

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

		inputStr, fieldInInput := input[fieldName]

		fieldType := field.Type
		fieldKind := field.Type.Kind()

		isPointerField := fieldKind == reflect.Ptr
		if isPointerField {
			if inputStr == "null" {
				continue
			}
			fieldType = fieldType.Elem()
			fieldKind = fieldType.Kind()
		}

		var iface interface{}

		if (squash || include) && fieldKind == reflect.Struct {
			var subInput map[string]string

			if squash {
				subInput = input
			} else {
				subInput = make(map[string]string)
				for k, v := range input {
					if strings.HasPrefix(k, fieldName+".") {
						subInput[strings.TrimPrefix(k, fieldName+".")] = v
					}
				}

				if len(subInput) == 0 {
					continue
				}
			}

			subOutput, err := FromStringStringMap(tagName, val.Field(i).Interface(), subInput)
			if err != nil {
				return nil, err
			}
			val.Field(i).Set(reflect.ValueOf(subOutput))
			continue
		}

		if !fieldInInput || inputStr == "" {
			continue
		}

		switch fieldKind {
		case reflect.Struct, reflect.Array, reflect.Interface, reflect.Slice, reflect.Map:
			iface, err = unmarshalToType(fieldType, inputStr)
			if err != nil {
				return nil, err
			}
		default:
			iface = decodeToType(fieldKind, inputStr)
		}

		if v, ok := iface.(time.Time); ok {
			iface = v.UTC()
		}

		fieldVal := reflect.ValueOf(iface).Convert(fieldType)

		if isPointerField {
			fieldValPtr := reflect.New(fieldType)
			fieldValPtr.Elem().Set(fieldVal)
			val.Field(i).Set(fieldValPtr)
		} else {
			val.Field(i).Set(fieldVal)
		}
	}

	return output, nil
}
// FromStringStringMap decodes input into output with the same type as base. Only fields tagged by tagName get decoded. Optional argument properties specifies fields to decode.