121 lines
3.4 KiB
Go
121 lines
3.4 KiB
Go
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
|
|
}
|