...

Source file src/github.com/clbanning/mxj/v2/json.go

Documentation: github.com/clbanning/mxj/v2

     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  

View as plain text