github.com/altipla-consulting/ravendb-go-client@v0.1.3/json_util.go (about) 1 package ravendb 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "strconv" 8 "strings" 9 ) 10 11 // Note: com.fasterxml.jackson.databind.JsonNode represents decoded JSON value i.e. interface{} 12 13 // Note: Java's com.fasterxml.jackson.databind.node.JsonNodeType represents type of JSON value, which in Go 14 // is the same as value itself, and therefore is interface{} 15 16 // Note: Java's com.fasterxml.jackson.databind.node.ObjectNode is map[string]interface{} 17 // It represents parsed json document 18 19 // Note: Java's com.fasterxml.jackson.databind.TreeNode represents a decoded JSON value that combines 20 // value and type. In Go it's interface{} 21 22 // Note: Java's com.fasterxml.jackson.databind.node.ArrayNode represents array of JSON objects 23 // It's []map[string]interface{} in Go 24 25 // we should use jsonMarshal instead of jsonMarshal so that it's easy 26 // to change json marshalling in all code base (e.g. to use a faster 27 // json library or ensure that values are marshaled correctly) 28 func jsonMarshal(v interface{}) ([]byte, error) { 29 return json.Marshal(v) 30 } 31 32 func jsonUnmarshal(d []byte, v interface{}) error { 33 return json.Unmarshal(d, v) 34 } 35 36 func jsonGetAsTextPointer(doc map[string]interface{}, key string) *string { 37 v, ok := doc[key] 38 if !ok { 39 return nil 40 } 41 // TODO: only allow *string ? 42 if s, ok := v.(*string); ok { 43 return s 44 } 45 s := v.(string) 46 return &s 47 } 48 49 func jsonGetAsString(doc map[string]interface{}, key string) (string, bool) { 50 return jsonGetAsText(doc, key) 51 } 52 53 func jsonGetAsText(doc map[string]interface{}, key string) (string, bool) { 54 v, ok := doc[key] 55 if !ok { 56 return "", false 57 } 58 s, ok := v.(string) 59 if !ok { 60 return "", false 61 } 62 return s, true 63 } 64 65 func jsonGetAsInt(doc map[string]interface{}, key string) (int, bool) { 66 v, ok := doc[key] 67 if !ok { 68 return 0, false 69 } 70 f, ok := v.(float64) 71 if ok { 72 return int(f), true 73 } 74 s, ok := v.(string) 75 if !ok { 76 return 0, false 77 } 78 n, err := strconv.Atoi(s) 79 if err != nil { 80 return 0, false 81 } 82 return n, true 83 } 84 85 func jsonGetAsInt64(doc map[string]interface{}, key string) (int64, bool) { 86 v, ok := doc[key] 87 if !ok { 88 return 0, false 89 } 90 f, ok := v.(float64) 91 if ok { 92 return int64(f), true 93 } 94 s, ok := v.(string) 95 if !ok { 96 return 0, false 97 } 98 n, err := strconv.ParseInt(s, 10, 64) 99 if err != nil { 100 return 0, false 101 } 102 return n, true 103 } 104 105 func jsonGetAsBool(doc map[string]interface{}, key string) (bool, bool) { 106 v, ok := doc[key] 107 if !ok { 108 return false, false 109 } 110 b, ok := v.(bool) 111 if ok { 112 return b, true 113 } 114 s, ok := v.(string) 115 if !ok { 116 return false, false 117 } 118 if strings.EqualFold(s, "true") { 119 return true, true 120 } 121 if strings.EqualFold(s, "false") { 122 return false, true 123 } 124 return false, false 125 } 126 127 // converts a struct to JSON representations as map of string to value 128 // TODO: could be faster 129 func structToJSONMap(v interface{}) map[string]interface{} { 130 d, err := jsonMarshal(v) 131 must(err) 132 var res map[string]interface{} 133 err = jsonUnmarshal(d, &res) 134 must(err) 135 return res 136 } 137 138 // given a json in the form of map[string]interface{}, de-serialize it to a struct 139 // TODO: could be faster 140 func structFromJSONMap(js map[string]interface{}, v interface{}) error { 141 d, err := jsonMarshal(js) 142 if err != nil { 143 return err 144 } 145 return jsonUnmarshal(d, v) 146 } 147 148 // matches a Java naming from EnityMapper 149 func valueToTree(v interface{}) map[string]interface{} { 150 return structToJSONMap(v) 151 } 152 153 // TODO: remove 154 // copyJSONMap makes a deep copy of map[string]interface{} 155 // TODO: possibly not the fastest way to do it 156 /* 157 func copyJSONMap(v map[string]interface{}) map[string]interface{} { 158 d, err := jsonMarshal(v) 159 must(err) 160 var res map[string]interface{} 161 err = jsonUnmarshal(d, &res) 162 must(err) 163 return res 164 } 165 */ 166 167 // jsonDecodeFirst decode first JSON object from d 168 // This is like jsonUnmarshal() but allows for d 169 // to contain multiple JSON objects 170 // This is for compatibility with Java's ObjectMapper.readTree() 171 func jsonUnmarshalFirst(d []byte, v interface{}) error { 172 r := bytes.NewReader(d) 173 dec := json.NewDecoder(r) 174 err := dec.Decode(v) 175 if err != nil { 176 dbg("jsonDecodeFirst: dec.Decode() of type %T failed with %s. JSON:\n%s\n\n", v, err, string(d)) 177 } 178 return err 179 } 180 181 func isUnprintable(c byte) bool { 182 if c < 32 { 183 // 9 - tab, 10 - LF, 13 - CR 184 if c == 9 || c == 10 || c == 13 { 185 return false 186 } 187 return true 188 } 189 return c >= 127 190 } 191 192 func isBinaryData(d []byte) bool { 193 for _, b := range d { 194 if isUnprintable(b) { 195 return true 196 } 197 } 198 return false 199 } 200 201 func asHex(d []byte) ([]byte, bool) { 202 if !isBinaryData(d) { 203 return d, false 204 } 205 206 // convert unprintable characters to hex 207 var res []byte 208 for i, c := range d { 209 if i > 2048 { 210 break 211 } 212 if isUnprintable(c) { 213 s := fmt.Sprintf("x%02x ", c) 214 res = append(res, s...) 215 } else { 216 res = append(res, c) 217 } 218 } 219 return res, true 220 } 221 222 // if d is a valid json, pretty-print it 223 // only used for debugging 224 func maybePrettyPrintJSON(d []byte) []byte { 225 if d2, ok := asHex(d); ok { 226 return d2 227 } 228 var m map[string]interface{} 229 err := json.Unmarshal(d, &m) 230 if err != nil { 231 return d 232 } 233 d2, err := json.MarshalIndent(m, "", " ") 234 if err != nil { 235 return d 236 } 237 return d2 238 } 239 240 func stringPtrToString(s *string) string { 241 if s == nil { 242 return "" 243 } 244 return *s 245 }