package jsoninfo import ( "encoding/json" "fmt" "reflect" ) // UnmarshalStrictStruct function: // * Unmarshals struct fields, ignoring UnmarshalJSON(...) and fields without 'json' tag. // * Correctly handles StrictStruct func UnmarshalStrictStruct(data []byte, value StrictStruct) error { decoder, err := NewObjectDecoder(data) if err != nil { return err } return value.DecodeWith(decoder, value) } type ObjectDecoder struct { Data []byte remainingFields map[string]json.RawMessage } func NewObjectDecoder(data []byte) (*ObjectDecoder, error) { var remainingFields map[string]json.RawMessage if err := json.Unmarshal(data, &remainingFields); err != nil { return nil, fmt.Errorf("Failed to unmarshal extension properties: %v\nInput: %s", err, data) } return &ObjectDecoder{ Data: data, remainingFields: remainingFields, }, nil } // DecodeExtensionMap returns all properties that were not decoded previously. func (decoder *ObjectDecoder) DecodeExtensionMap() map[string]json.RawMessage { return decoder.remainingFields } func (decoder *ObjectDecoder) DecodeStructFieldsAndExtensions(value interface{}) error { reflection := reflect.ValueOf(value) if reflection.Kind() != reflect.Ptr { panic(fmt.Errorf("Value %T is not a pointer", value)) } if reflection.IsNil() { panic(fmt.Errorf("Value %T is nil", value)) } reflection = reflection.Elem() for (reflection.Kind() == reflect.Interface || reflection.Kind() == reflect.Ptr) && !reflection.IsNil() { reflection = reflection.Elem() } reflectionType := reflection.Type() if reflectionType.Kind() != reflect.Struct { panic(fmt.Errorf("Value %T is not a struct", value)) } typeInfo := GetTypeInfo(reflectionType) // Supported fields fields := typeInfo.Fields remainingFields := decoder.remainingFields for fieldIndex, field := range fields { // Fields without JSON tag are ignored if !field.HasJSONTag { continue } // Get data fieldData, exists := remainingFields[field.JSONName] if !exists { continue } // Unmarshal if field.TypeIsUnmarshaller { fieldType := field.Type isPtr := false if fieldType.Kind() == reflect.Ptr { fieldType = fieldType.Elem() isPtr = true } fieldValue := reflect.New(fieldType) if err := fieldValue.Interface().(json.Unmarshaler).UnmarshalJSON(fieldData); err != nil { if field.MultipleFields { i := fieldIndex + 1 if i < len(fields) && fields[i].JSONName == field.JSONName { continue } } return fmt.Errorf("Error while unmarshalling property '%s' (%s): %v", field.JSONName, fieldValue.Type().String(), err) } if !isPtr { fieldValue = fieldValue.Elem() } reflection.FieldByIndex(field.Index).Set(fieldValue) // Remove the field from remaining fields delete(remainingFields, field.JSONName) } else { fieldPtr := reflection.FieldByIndex(field.Index) if fieldPtr.Kind() != reflect.Ptr || fieldPtr.IsNil() { fieldPtr = fieldPtr.Addr() } if err := json.Unmarshal(fieldData, fieldPtr.Interface()); err != nil { if field.MultipleFields { i := fieldIndex + 1 if i < len(fields) && fields[i].JSONName == field.JSONName { continue } } return fmt.Errorf("Error while unmarshalling property '%s' (%s): %v", field.JSONName, fieldPtr.Type().String(), err) } // Remove the field from remaining fields delete(remainingFields, field.JSONName) } } return nil }