| // Copyright 2015 go-swagger maintainers |
| // |
| // 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 swag |
| |
| import ( |
| "bytes" |
| "encoding/json" |
| "log" |
| "reflect" |
| "strings" |
| "sync" |
| |
| "github.com/mailru/easyjson/jlexer" |
| "github.com/mailru/easyjson/jwriter" |
| ) |
| |
| // nullJSON represents a JSON object with null type |
| var nullJSON = []byte("null") |
| |
| // DefaultJSONNameProvider the default cache for types |
| var DefaultJSONNameProvider = NewNameProvider() |
| |
| const comma = byte(',') |
| |
| var closers = map[byte]byte{ |
| '{': '}', |
| '[': ']', |
| } |
| |
| type ejMarshaler interface { |
| MarshalEasyJSON(w *jwriter.Writer) |
| } |
| |
| type ejUnmarshaler interface { |
| UnmarshalEasyJSON(w *jlexer.Lexer) |
| } |
| |
| // WriteJSON writes json data, prefers finding an appropriate interface to short-circuit the marshaller |
| // so it takes the fastest option available. |
| func WriteJSON(data interface{}) ([]byte, error) { |
| if d, ok := data.(ejMarshaler); ok { |
| jw := new(jwriter.Writer) |
| d.MarshalEasyJSON(jw) |
| return jw.BuildBytes() |
| } |
| if d, ok := data.(json.Marshaler); ok { |
| return d.MarshalJSON() |
| } |
| return json.Marshal(data) |
| } |
| |
| // ReadJSON reads json data, prefers finding an appropriate interface to short-circuit the unmarshaller |
| // so it takes the fastes option available |
| func ReadJSON(data []byte, value interface{}) error { |
| if d, ok := value.(ejUnmarshaler); ok { |
| jl := &jlexer.Lexer{Data: data} |
| d.UnmarshalEasyJSON(jl) |
| return jl.Error() |
| } |
| if d, ok := value.(json.Unmarshaler); ok { |
| return d.UnmarshalJSON(data) |
| } |
| return json.Unmarshal(data, value) |
| } |
| |
| // DynamicJSONToStruct converts an untyped json structure into a struct |
| func DynamicJSONToStruct(data interface{}, target interface{}) error { |
| // TODO: convert straight to a json typed map (mergo + iterate?) |
| b, err := WriteJSON(data) |
| if err != nil { |
| return err |
| } |
| if err := ReadJSON(b, target); err != nil { |
| return err |
| } |
| return nil |
| } |
| |
| // ConcatJSON concatenates multiple json objects efficiently |
| func ConcatJSON(blobs ...[]byte) []byte { |
| if len(blobs) == 0 { |
| return nil |
| } |
| |
| last := len(blobs) - 1 |
| for blobs[last] == nil || bytes.Equal(blobs[last], nullJSON) { |
| // strips trailing null objects |
| last = last - 1 |
| if last < 0 { |
| // there was nothing but "null"s or nil... |
| return nil |
| } |
| } |
| if last == 0 { |
| return blobs[0] |
| } |
| |
| var opening, closing byte |
| var idx, a int |
| buf := bytes.NewBuffer(nil) |
| |
| for i, b := range blobs[:last+1] { |
| if b == nil || bytes.Equal(b, nullJSON) { |
| // a null object is in the list: skip it |
| continue |
| } |
| if len(b) > 0 && opening == 0 { // is this an array or an object? |
| opening, closing = b[0], closers[b[0]] |
| } |
| |
| if opening != '{' && opening != '[' { |
| continue // don't know how to concatenate non container objects |
| } |
| |
| if len(b) < 3 { // yep empty but also the last one, so closing this thing |
| if i == last && a > 0 { |
| if err := buf.WriteByte(closing); err != nil { |
| log.Println(err) |
| } |
| } |
| continue |
| } |
| |
| idx = 0 |
| if a > 0 { // we need to join with a comma for everything beyond the first non-empty item |
| if err := buf.WriteByte(comma); err != nil { |
| log.Println(err) |
| } |
| idx = 1 // this is not the first or the last so we want to drop the leading bracket |
| } |
| |
| if i != last { // not the last one, strip brackets |
| if _, err := buf.Write(b[idx : len(b)-1]); err != nil { |
| log.Println(err) |
| } |
| } else { // last one, strip only the leading bracket |
| if _, err := buf.Write(b[idx:]); err != nil { |
| log.Println(err) |
| } |
| } |
| a++ |
| } |
| // somehow it ended up being empty, so provide a default value |
| if buf.Len() == 0 { |
| if err := buf.WriteByte(opening); err != nil { |
| log.Println(err) |
| } |
| if err := buf.WriteByte(closing); err != nil { |
| log.Println(err) |
| } |
| } |
| return buf.Bytes() |
| } |
| |
| // ToDynamicJSON turns an object into a properly JSON typed structure |
| func ToDynamicJSON(data interface{}) interface{} { |
| // TODO: convert straight to a json typed map (mergo + iterate?) |
| b, err := json.Marshal(data) |
| if err != nil { |
| log.Println(err) |
| } |
| var res interface{} |
| if err := json.Unmarshal(b, &res); err != nil { |
| log.Println(err) |
| } |
| return res |
| } |
| |
| // FromDynamicJSON turns an object into a properly JSON typed structure |
| func FromDynamicJSON(data, target interface{}) error { |
| b, err := json.Marshal(data) |
| if err != nil { |
| log.Println(err) |
| } |
| return json.Unmarshal(b, target) |
| } |
| |
| // NameProvider represents an object capabale of translating from go property names |
| // to json property names |
| // This type is thread-safe. |
| type NameProvider struct { |
| lock *sync.Mutex |
| index map[reflect.Type]nameIndex |
| } |
| |
| type nameIndex struct { |
| jsonNames map[string]string |
| goNames map[string]string |
| } |
| |
| // NewNameProvider creates a new name provider |
| func NewNameProvider() *NameProvider { |
| return &NameProvider{ |
| lock: &sync.Mutex{}, |
| index: make(map[reflect.Type]nameIndex), |
| } |
| } |
| |
| func buildnameIndex(tpe reflect.Type, idx, reverseIdx map[string]string) { |
| for i := 0; i < tpe.NumField(); i++ { |
| targetDes := tpe.Field(i) |
| |
| if targetDes.PkgPath != "" { // unexported |
| continue |
| } |
| |
| if targetDes.Anonymous { // walk embedded structures tree down first |
| buildnameIndex(targetDes.Type, idx, reverseIdx) |
| continue |
| } |
| |
| if tag := targetDes.Tag.Get("json"); tag != "" { |
| |
| parts := strings.Split(tag, ",") |
| if len(parts) == 0 { |
| continue |
| } |
| |
| nm := parts[0] |
| if nm == "-" { |
| continue |
| } |
| if nm == "" { // empty string means we want to use the Go name |
| nm = targetDes.Name |
| } |
| |
| idx[nm] = targetDes.Name |
| reverseIdx[targetDes.Name] = nm |
| } |
| } |
| } |
| |
| func newNameIndex(tpe reflect.Type) nameIndex { |
| var idx = make(map[string]string, tpe.NumField()) |
| var reverseIdx = make(map[string]string, tpe.NumField()) |
| |
| buildnameIndex(tpe, idx, reverseIdx) |
| return nameIndex{jsonNames: idx, goNames: reverseIdx} |
| } |
| |
| // GetJSONNames gets all the json property names for a type |
| func (n *NameProvider) GetJSONNames(subject interface{}) []string { |
| n.lock.Lock() |
| defer n.lock.Unlock() |
| tpe := reflect.Indirect(reflect.ValueOf(subject)).Type() |
| names, ok := n.index[tpe] |
| if !ok { |
| names = n.makeNameIndex(tpe) |
| } |
| |
| var res []string |
| for k := range names.jsonNames { |
| res = append(res, k) |
| } |
| return res |
| } |
| |
| // GetJSONName gets the json name for a go property name |
| func (n *NameProvider) GetJSONName(subject interface{}, name string) (string, bool) { |
| tpe := reflect.Indirect(reflect.ValueOf(subject)).Type() |
| return n.GetJSONNameForType(tpe, name) |
| } |
| |
| // GetJSONNameForType gets the json name for a go property name on a given type |
| func (n *NameProvider) GetJSONNameForType(tpe reflect.Type, name string) (string, bool) { |
| n.lock.Lock() |
| defer n.lock.Unlock() |
| names, ok := n.index[tpe] |
| if !ok { |
| names = n.makeNameIndex(tpe) |
| } |
| nme, ok := names.goNames[name] |
| return nme, ok |
| } |
| |
| func (n *NameProvider) makeNameIndex(tpe reflect.Type) nameIndex { |
| names := newNameIndex(tpe) |
| n.index[tpe] = names |
| return names |
| } |
| |
| // GetGoName gets the go name for a json property name |
| func (n *NameProvider) GetGoName(subject interface{}, name string) (string, bool) { |
| tpe := reflect.Indirect(reflect.ValueOf(subject)).Type() |
| return n.GetGoNameForType(tpe, name) |
| } |
| |
| // GetGoNameForType gets the go name for a given type for a json property name |
| func (n *NameProvider) GetGoNameForType(tpe reflect.Type, name string) (string, bool) { |
| n.lock.Lock() |
| defer n.lock.Unlock() |
| names, ok := n.index[tpe] |
| if !ok { |
| names = n.makeNameIndex(tpe) |
| } |
| nme, ok := names.jsonNames[name] |
| return nme, ok |
| } |