-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathstd_json.go
123 lines (109 loc) · 3.08 KB
/
std_json.go
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
122
123
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package jsonutils
import (
"encoding/json"
"fmt"
"reflect"
"yunion.io/x/pkg/gotypes"
)
func tryStdUnmarshal(s *sJsonUnmarshalSession, jo JSONObject, v reflect.Value, unmarshalFunc func(s *sJsonUnmarshalSession, value reflect.Value) error) error {
u := IsImplementStdUnmarshaler(v)
if u != nil {
return u.UnmarshalJSON([]byte(jo.String()))
}
return unmarshalFunc(s, v)
}
func IsImplementStdUnmarshaler(v reflect.Value) json.Unmarshaler {
return indirectStdUnmarshaler(v)
}
func indirectStdUnmarshaler(v reflect.Value) json.Unmarshaler {
v0 := v
haveAddr := false
// If v is a named type and is addressable,
// start with its address, so that if the type has pointer methods,
// we find them
if v.Kind() != reflect.Ptr && v.Type().Name() != "" && v.CanAddr() {
haveAddr = true
v = v.Addr()
}
for {
// Load value from interface, but only if the result will be
// usefully addressable.
if v.Kind() == reflect.Interface && !v.IsNil() {
e := v.Elem()
if e.Kind() == reflect.Ptr && !e.IsNil() && e.Elem().Kind() == reflect.Ptr {
haveAddr = false
v = e
continue
}
}
if v.Kind() != reflect.Ptr {
break
}
// Prevent infinite loop if v is an interface pointing to its own address:
// var v interface{}
// v = &v
if v.Elem().Kind() == reflect.Interface && v.Elem().Elem() == v {
v = v.Elem()
break
}
if v.IsNil() {
v.Set(reflect.New(v.Type().Elem()))
}
if v.Type().NumMethod() > 0 && v.CanInterface() {
if u, ok := v.Interface().(json.Unmarshaler); ok {
return u
}
}
if haveAddr {
v = v0 // restore original value after round-trip Value.Addr().Elem()
haveAddr = false
} else {
v = v.Elem()
}
}
return nil
}
func IsImplementStdMarshaler(v reflect.Value) json.Marshaler {
return indirectStdMarshaler(v)
}
func indirectStdMarshaler(v reflect.Value) json.Marshaler {
if v.Kind() == reflect.Ptr && v.IsNil() {
return nil
}
if v.Type().NumMethod() > 0 && v.CanInterface() {
if u, ok := v.Interface().(json.Marshaler); ok {
return u
}
}
return nil
}
func tryStdMarshal(v reflect.Value, marshalFunc func(v reflect.Value) JSONObject) JSONObject {
if v.Type() != gotypes.TimeType {
m := IsImplementStdMarshaler(v)
if m != nil {
data, err := m.MarshalJSON()
if err != nil {
panic(fmt.Sprintf("MarshalJSON of %q error: %v", v.String(), err))
}
jo, err := Parse(data)
if err != nil {
panic(fmt.Sprintf("Parse data %q to json of %q error: %v", data, v.String(), err))
}
return jo
}
}
return marshalFunc(v)
}