1 // Copyright 2012-2014 Charles Banning. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file
4 5 package mxj
6 7 import (
8 "bytes"
9 "encoding/json"
10 "fmt"
11 "io"
12 "time"
13 )
14 15 // ------------------------------ write JSON -----------------------
16 17 // Just a wrapper on json.Marshal.
18 // If option safeEncoding is'true' then safe encoding of '<', '>' and '&'
19 // is preserved. (see encoding/json#Marshal, encoding/json#Encode)
20 func (mv Map) Json(safeEncoding ...bool) ([]byte, error) {
21 var s bool
22 if len(safeEncoding) == 1 {
23 s = safeEncoding[0]
24 }
25 26 b, err := json.Marshal(mv)
27 28 if !s {
29 b = bytes.Replace(b, []byte("\\u003c"), []byte("<"), -1)
30 b = bytes.Replace(b, []byte("\\u003e"), []byte(">"), -1)
31 b = bytes.Replace(b, []byte("\\u0026"), []byte("&"), -1)
32 }
33 return b, err
34 }
35 36 // Just a wrapper on json.MarshalIndent.
37 // If option safeEncoding is'true' then safe encoding of '<' , '>' and '&'
38 // is preserved. (see encoding/json#Marshal, encoding/json#Encode)
39 func (mv Map) JsonIndent(prefix, indent string, safeEncoding ...bool) ([]byte, error) {
40 var s bool
41 if len(safeEncoding) == 1 {
42 s = safeEncoding[0]
43 }
44 45 b, err := json.MarshalIndent(mv, prefix, indent)
46 if !s {
47 b = bytes.Replace(b, []byte("\\u003c"), []byte("<"), -1)
48 b = bytes.Replace(b, []byte("\\u003e"), []byte(">"), -1)
49 b = bytes.Replace(b, []byte("\\u0026"), []byte("&"), -1)
50 }
51 return b, err
52 }
53 54 // The following implementation is provided for symmetry with NewMapJsonReader[Raw]
55 // The names will also provide a key for the number of return arguments.
56 57 // Writes the Map as JSON on the Writer.
58 // If 'safeEncoding' is 'true', then "safe" encoding of '<', '>' and '&' is preserved.
59 func (mv Map) JsonWriter(jsonWriter io.Writer, safeEncoding ...bool) error {
60 b, err := mv.Json(safeEncoding...)
61 if err != nil {
62 return err
63 }
64 65 _, err = jsonWriter.Write(b)
66 return err
67 }
68 69 // Writes the Map as JSON on the Writer. []byte is the raw JSON that was written.
70 // If 'safeEncoding' is 'true', then "safe" encoding of '<', '>' and '&' is preserved.
71 func (mv Map) JsonWriterRaw(jsonWriter io.Writer, safeEncoding ...bool) ([]byte, error) {
72 b, err := mv.Json(safeEncoding...)
73 if err != nil {
74 return b, err
75 }
76 77 _, err = jsonWriter.Write(b)
78 return b, err
79 }
80 81 // Writes the Map as pretty JSON on the Writer.
82 // If 'safeEncoding' is 'true', then "safe" encoding of '<', '>' and '&' is preserved.
83 func (mv Map) JsonIndentWriter(jsonWriter io.Writer, prefix, indent string, safeEncoding ...bool) error {
84 b, err := mv.JsonIndent(prefix, indent, safeEncoding...)
85 if err != nil {
86 return err
87 }
88 89 _, err = jsonWriter.Write(b)
90 return err
91 }
92 93 // Writes the Map as pretty JSON on the Writer. []byte is the raw JSON that was written.
94 // If 'safeEncoding' is 'true', then "safe" encoding of '<', '>' and '&' is preserved.
95 func (mv Map) JsonIndentWriterRaw(jsonWriter io.Writer, prefix, indent string, safeEncoding ...bool) ([]byte, error) {
96 b, err := mv.JsonIndent(prefix, indent, safeEncoding...)
97 if err != nil {
98 return b, err
99 }
100 101 _, err = jsonWriter.Write(b)
102 return b, err
103 }
104 105 // --------------------------- read JSON -----------------------------
106 107 // Decode numericvalues as json.Number type Map values - see encoding/json#Number.
108 // NOTE: this is for decoding JSON into a Map with NewMapJson(), NewMapJsonReader(),
109 // etc.; it does not affect NewMapXml(), etc. The XML encoders mv.Xml() and mv.XmlIndent()
110 // do recognize json.Number types; a JSON object can be decoded to a Map with json.Number
111 // value types and the resulting Map can be correctly encoded into a XML object.
112 var JsonUseNumber bool
113 114 // Just a wrapper on json.Unmarshal
115 // Converting JSON to XML is a simple as:
116 // ...
117 // mapVal, merr := mxj.NewMapJson(jsonVal)
118 // if merr != nil {
119 // // handle error
120 // }
121 // xmlVal, xerr := mapVal.Xml()
122 // if xerr != nil {
123 // // handle error
124 // }
125 // NOTE: as a special case, passing a list, e.g., [{"some-null-value":"", "a-non-null-value":"bar"}],
126 // will be interpreted as having the root key 'object' prepended - {"object":[ ... ]} - to unmarshal to a Map.
127 // See mxj/j2x/j2x_test.go.
128 func NewMapJson(jsonVal []byte) (Map, error) {
129 // empty or nil begets empty
130 if len(jsonVal) == 0 {
131 m := make(map[string]interface{}, 0)
132 return m, nil
133 }
134 // handle a goofy case ...
135 if jsonVal[0] == '[' {
136 jsonVal = []byte(`{"object":` + string(jsonVal) + `}`)
137 }
138 m := make(map[string]interface{})
139 // err := json.Unmarshal(jsonVal, &m)
140 buf := bytes.NewReader(jsonVal)
141 dec := json.NewDecoder(buf)
142 if JsonUseNumber {
143 dec.UseNumber()
144 }
145 err := dec.Decode(&m)
146 return m, err
147 }
148 149 // Retrieve a Map value from an io.Reader.
150 // NOTE: The raw JSON off the reader is buffered to []byte using a ByteReader. If the io.Reader is an
151 // os.File, there may be significant performance impact. If the io.Reader is wrapping a []byte
152 // value in-memory, however, such as http.Request.Body you CAN use it to efficiently unmarshal
153 // a JSON object.
154 func NewMapJsonReader(jsonReader io.Reader) (Map, error) {
155 jb, err := getJson(jsonReader)
156 if err != nil || len(*jb) == 0 {
157 return nil, err
158 }
159 160 // Unmarshal the 'presumed' JSON string
161 return NewMapJson(*jb)
162 }
163 164 // Retrieve a Map value and raw JSON - []byte - from an io.Reader.
165 // NOTE: The raw JSON off the reader is buffered to []byte using a ByteReader. If the io.Reader is an
166 // os.File, there may be significant performance impact. If the io.Reader is wrapping a []byte
167 // value in-memory, however, such as http.Request.Body you CAN use it to efficiently unmarshal
168 // a JSON object and retrieve the raw JSON in a single call.
169 func NewMapJsonReaderRaw(jsonReader io.Reader) (Map, []byte, error) {
170 jb, err := getJson(jsonReader)
171 if err != nil || len(*jb) == 0 {
172 return nil, *jb, err
173 }
174 175 // Unmarshal the 'presumed' JSON string
176 m, merr := NewMapJson(*jb)
177 return m, *jb, merr
178 }
179 180 // Pull the next JSON string off the stream: just read from first '{' to its closing '}'.
181 // Returning a pointer to the slice saves 16 bytes - maybe unnecessary, but internal to package.
182 func getJson(rdr io.Reader) (*[]byte, error) {
183 bval := make([]byte, 1)
184 jb := make([]byte, 0)
185 var inQuote, inJson bool
186 var parenCnt int
187 var previous byte
188 189 // scan the input for a matched set of {...}
190 // json.Unmarshal will handle syntax checking.
191 for {
192 _, err := rdr.Read(bval)
193 if err != nil {
194 if err == io.EOF && inJson && parenCnt > 0 {
195 return &jb, fmt.Errorf("no closing } for JSON string: %s", string(jb))
196 }
197 return &jb, err
198 }
199 switch bval[0] {
200 case '{':
201 if !inQuote {
202 parenCnt++
203 inJson = true
204 }
205 case '}':
206 if !inQuote {
207 parenCnt--
208 }
209 if parenCnt < 0 {
210 return nil, fmt.Errorf("closing } without opening {: %s", string(jb))
211 }
212 case '"':
213 if inQuote {
214 if previous == '\\' {
215 break
216 }
217 inQuote = false
218 } else {
219 inQuote = true
220 }
221 case '\n', '\r', '\t', ' ':
222 if !inQuote {
223 continue
224 }
225 }
226 if inJson {
227 jb = append(jb, bval[0])
228 if parenCnt == 0 {
229 break
230 }
231 }
232 previous = bval[0]
233 }
234 235 return &jb, nil
236 }
237 238 // ------------------------------- JSON Reader handler via Map values -----------------------
239 240 // Default poll delay to keep Handler from spinning on an open stream
241 // like sitting on os.Stdin waiting for imput.
242 var jhandlerPollInterval = time.Duration(1e6)
243 244 // While unnecessary, we make HandleJsonReader() have the same signature as HandleXmlReader().
245 // This avoids treating one or other as a special case and discussing the underlying stdlib logic.
246 247 // Bulk process JSON using handlers that process a Map value.
248 // 'rdr' is an io.Reader for the JSON (stream).
249 // 'mapHandler' is the Map processing handler. Return of 'false' stops io.Reader processing.
250 // 'errHandler' is the error processor. Return of 'false' stops io.Reader processing and returns the error.
251 // Note: mapHandler() and errHandler() calls are blocking, so reading and processing of messages is serialized.
252 // This means that you can stop reading the file on error or after processing a particular message.
253 // To have reading and handling run concurrently, pass argument to a go routine in handler and return 'true'.
254 func HandleJsonReader(jsonReader io.Reader, mapHandler func(Map) bool, errHandler func(error) bool) error {
255 var n int
256 for {
257 m, merr := NewMapJsonReader(jsonReader)
258 n++
259 260 // handle error condition with errhandler
261 if merr != nil && merr != io.EOF {
262 merr = fmt.Errorf("[jsonReader: %d] %s", n, merr.Error())
263 if ok := errHandler(merr); !ok {
264 // caused reader termination
265 return merr
266 }
267 continue
268 }
269 270 // pass to maphandler
271 if len(m) != 0 {
272 if ok := mapHandler(m); !ok {
273 break
274 }
275 } else if merr != io.EOF {
276 <-time.After(jhandlerPollInterval)
277 }
278 279 if merr == io.EOF {
280 break
281 }
282 }
283 return nil
284 }
285 286 // Bulk process JSON using handlers that process a Map value and the raw JSON.
287 // 'rdr' is an io.Reader for the JSON (stream).
288 // 'mapHandler' is the Map and raw JSON - []byte - processor. Return of 'false' stops io.Reader processing.
289 // 'errHandler' is the error and raw JSON processor. Return of 'false' stops io.Reader processing and returns the error.
290 // Note: mapHandler() and errHandler() calls are blocking, so reading and processing of messages is serialized.
291 // This means that you can stop reading the file on error or after processing a particular message.
292 // To have reading and handling run concurrently, pass argument(s) to a go routine in handler and return 'true'.
293 func HandleJsonReaderRaw(jsonReader io.Reader, mapHandler func(Map, []byte) bool, errHandler func(error, []byte) bool) error {
294 var n int
295 for {
296 m, raw, merr := NewMapJsonReaderRaw(jsonReader)
297 n++
298 299 // handle error condition with errhandler
300 if merr != nil && merr != io.EOF {
301 merr = fmt.Errorf("[jsonReader: %d] %s", n, merr.Error())
302 if ok := errHandler(merr, raw); !ok {
303 // caused reader termination
304 return merr
305 }
306 continue
307 }
308 309 // pass to maphandler
310 if len(m) != 0 {
311 if ok := mapHandler(m, raw); !ok {
312 break
313 }
314 } else if merr != io.EOF {
315 <-time.After(jhandlerPollInterval)
316 }
317 318 if merr == io.EOF {
319 break
320 }
321 }
322 return nil
323 }
324