1 // Copyright 2015 go-swagger maintainers
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
25 "github.com/mailru/easyjson/jlexer"
26 "github.com/mailru/easyjson/jwriter"
29 // nullJSON represents a JSON object with null type
30 var nullJSON = []byte("null")
32 // DefaultJSONNameProvider the default cache for types
33 var DefaultJSONNameProvider = NewNameProvider()
35 const comma = byte(',')
37 var closers map[byte]byte
40 closers = map[byte]byte{
46 type ejMarshaler interface {
47 MarshalEasyJSON(w *jwriter.Writer)
50 type ejUnmarshaler interface {
51 UnmarshalEasyJSON(w *jlexer.Lexer)
54 // WriteJSON writes json data, prefers finding an appropriate interface to short-circuit the marshaller
55 // so it takes the fastest option available.
56 func WriteJSON(data interface{}) ([]byte, error) {
57 if d, ok := data.(ejMarshaler); ok {
58 jw := new(jwriter.Writer)
60 return jw.BuildBytes()
62 if d, ok := data.(json.Marshaler); ok {
63 return d.MarshalJSON()
65 return json.Marshal(data)
68 // ReadJSON reads json data, prefers finding an appropriate interface to short-circuit the unmarshaller
69 // so it takes the fastes option available
70 func ReadJSON(data []byte, value interface{}) error {
71 trimmedData := bytes.Trim(data, "\x00")
72 if d, ok := value.(ejUnmarshaler); ok {
73 jl := &jlexer.Lexer{Data: trimmedData}
74 d.UnmarshalEasyJSON(jl)
77 if d, ok := value.(json.Unmarshaler); ok {
78 return d.UnmarshalJSON(trimmedData)
80 return json.Unmarshal(trimmedData, value)
83 // DynamicJSONToStruct converts an untyped json structure into a struct
84 func DynamicJSONToStruct(data interface{}, target interface{}) error {
85 // TODO: convert straight to a json typed map (mergo + iterate?)
86 b, err := WriteJSON(data)
90 return ReadJSON(b, target)
93 // ConcatJSON concatenates multiple json objects efficiently
94 func ConcatJSON(blobs ...[]byte) []byte {
99 last := len(blobs) - 1
100 for blobs[last] == nil || bytes.Equal(blobs[last], nullJSON) {
101 // strips trailing null objects
104 // there was nothing but "null"s or nil...
112 var opening, closing byte
114 buf := bytes.NewBuffer(nil)
116 for i, b := range blobs[:last+1] {
117 if b == nil || bytes.Equal(b, nullJSON) {
118 // a null object is in the list: skip it
121 if len(b) > 0 && opening == 0 { // is this an array or an object?
122 opening, closing = b[0], closers[b[0]]
125 if opening != '{' && opening != '[' {
126 continue // don't know how to concatenate non container objects
129 if len(b) < 3 { // yep empty but also the last one, so closing this thing
130 if i == last && a > 0 {
131 if err := buf.WriteByte(closing); err != nil {
139 if a > 0 { // we need to join with a comma for everything beyond the first non-empty item
140 if err := buf.WriteByte(comma); err != nil {
143 idx = 1 // this is not the first or the last so we want to drop the leading bracket
146 if i != last { // not the last one, strip brackets
147 if _, err := buf.Write(b[idx : len(b)-1]); err != nil {
150 } else { // last one, strip only the leading bracket
151 if _, err := buf.Write(b[idx:]); err != nil {
157 // somehow it ended up being empty, so provide a default value
159 if err := buf.WriteByte(opening); err != nil {
162 if err := buf.WriteByte(closing); err != nil {
169 // ToDynamicJSON turns an object into a properly JSON typed structure
170 func ToDynamicJSON(data interface{}) interface{} {
171 // TODO: convert straight to a json typed map (mergo + iterate?)
172 b, err := json.Marshal(data)
177 if err := json.Unmarshal(b, &res); err != nil {
183 // FromDynamicJSON turns an object into a properly JSON typed structure
184 func FromDynamicJSON(data, target interface{}) error {
185 b, err := json.Marshal(data)
189 return json.Unmarshal(b, target)
192 // NameProvider represents an object capabale of translating from go property names
193 // to json property names
194 // This type is thread-safe.
195 type NameProvider struct {
197 index map[reflect.Type]nameIndex
200 type nameIndex struct {
201 jsonNames map[string]string
202 goNames map[string]string
205 // NewNameProvider creates a new name provider
206 func NewNameProvider() *NameProvider {
207 return &NameProvider{
209 index: make(map[reflect.Type]nameIndex),
213 func buildnameIndex(tpe reflect.Type, idx, reverseIdx map[string]string) {
214 for i := 0; i < tpe.NumField(); i++ {
215 targetDes := tpe.Field(i)
217 if targetDes.PkgPath != "" { // unexported
221 if targetDes.Anonymous { // walk embedded structures tree down first
222 buildnameIndex(targetDes.Type, idx, reverseIdx)
226 if tag := targetDes.Tag.Get("json"); tag != "" {
228 parts := strings.Split(tag, ",")
237 if nm == "" { // empty string means we want to use the Go name
241 idx[nm] = targetDes.Name
242 reverseIdx[targetDes.Name] = nm
247 func newNameIndex(tpe reflect.Type) nameIndex {
248 var idx = make(map[string]string, tpe.NumField())
249 var reverseIdx = make(map[string]string, tpe.NumField())
251 buildnameIndex(tpe, idx, reverseIdx)
252 return nameIndex{jsonNames: idx, goNames: reverseIdx}
255 // GetJSONNames gets all the json property names for a type
256 func (n *NameProvider) GetJSONNames(subject interface{}) []string {
258 defer n.lock.Unlock()
259 tpe := reflect.Indirect(reflect.ValueOf(subject)).Type()
260 names, ok := n.index[tpe]
262 names = n.makeNameIndex(tpe)
265 res := make([]string, 0, len(names.jsonNames))
266 for k := range names.jsonNames {
272 // GetJSONName gets the json name for a go property name
273 func (n *NameProvider) GetJSONName(subject interface{}, name string) (string, bool) {
274 tpe := reflect.Indirect(reflect.ValueOf(subject)).Type()
275 return n.GetJSONNameForType(tpe, name)
278 // GetJSONNameForType gets the json name for a go property name on a given type
279 func (n *NameProvider) GetJSONNameForType(tpe reflect.Type, name string) (string, bool) {
281 defer n.lock.Unlock()
282 names, ok := n.index[tpe]
284 names = n.makeNameIndex(tpe)
286 nme, ok := names.goNames[name]
290 func (n *NameProvider) makeNameIndex(tpe reflect.Type) nameIndex {
291 names := newNameIndex(tpe)
296 // GetGoName gets the go name for a json property name
297 func (n *NameProvider) GetGoName(subject interface{}, name string) (string, bool) {
298 tpe := reflect.Indirect(reflect.ValueOf(subject)).Type()
299 return n.GetGoNameForType(tpe, name)
302 // GetGoNameForType gets the go name for a given type for a json property name
303 func (n *NameProvider) GetGoNameForType(tpe reflect.Type, name string) (string, bool) {
305 defer n.lock.Unlock()
306 names, ok := n.index[tpe]
308 names = n.makeNameIndex(tpe)
310 nme, ok := names.jsonNames[name]