json.go 9.6 KB

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