File: unmarshal.go

package info (click to toggle)
golang-github-getkin-kin-openapi 0.1.0+git20181119.fa639d0-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye, buster, sid
  • size: 480 kB
  • sloc: makefile: 2
file content (121 lines) | stat: -rw-r--r-- 3,475 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
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
}