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  }