...

Source file src/github.com/launchdarkly/go-sdk-common/v3/ldvalue/value_json_conversion.go

Documentation: github.com/launchdarkly/go-sdk-common/v3/ldvalue

     1  package ldvalue
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"strconv"
     7  
     8  	"github.com/launchdarkly/go-jsonstream/v3/jreader"
     9  	"github.com/launchdarkly/go-jsonstream/v3/jwriter"
    10  )
    11  
    12  // This file contains methods for converting Value to and from JSON.
    13  
    14  // Parse returns a Value parsed from a JSON string, or Null if it cannot be parsed.
    15  //
    16  // This is simply a shortcut for calling json.Unmarshal and disregarding errors. It is meant for
    17  // use in test scenarios where malformed data is not a concern.
    18  func Parse(jsonData []byte) Value {
    19  	var v Value
    20  	if err := v.UnmarshalJSON(jsonData); err != nil {
    21  		return Null()
    22  	}
    23  	return v
    24  }
    25  
    26  // JSONString returns the JSON representation of the value.
    27  //
    28  // This is equivalent to calling [Value.MarshalJSON] and converting the result to a string.
    29  // Since all Values by definition can be represented in JSON, this method does not need to
    30  // return an error value so it can be easily used within an expression.
    31  func (v Value) JSONString() string {
    32  	// The following is somewhat redundant with json.Marshal, but it avoids the overhead of
    33  	// converting between byte arrays and strings.
    34  	switch v.valueType {
    35  	case NullType:
    36  		return nullAsJSON
    37  	case BoolType:
    38  		if v.boolValue {
    39  			return trueString
    40  		}
    41  		return falseString
    42  	case NumberType:
    43  		if v.IsInt() {
    44  			return strconv.Itoa(int(v.numberValue))
    45  		}
    46  		return strconv.FormatFloat(v.numberValue, 'f', -1, 64)
    47  	}
    48  	// For all other types, we rely on our custom marshaller.
    49  	bytes, _ := json.Marshal(v)
    50  	// It shouldn't be possible for marshalling to fail, because Value can only contain
    51  	// JSON-compatible types. But if it somehow did fail, bytes will be nil and we'll return
    52  	// an empty string.
    53  	return string(bytes)
    54  }
    55  
    56  // MarshalJSON converts the Value to its JSON representation.
    57  //
    58  // Note that the "omitempty" tag for a struct field will not cause an empty Value field to be
    59  // omitted; it will be output as null. If you want to completely omit a JSON property when there
    60  // is no value, it must be a pointer; use AsPointer().
    61  func (v Value) MarshalJSON() ([]byte, error) {
    62  	switch v.valueType {
    63  	case NullType:
    64  		return nullAsJSONBytes, nil
    65  	case BoolType:
    66  		if v.boolValue {
    67  			return trueBytes, nil
    68  		}
    69  		return falseBytes, nil
    70  	case NumberType:
    71  		if v.IsInt() {
    72  			return []byte(strconv.Itoa(int(v.numberValue))), nil
    73  		}
    74  		return []byte(strconv.FormatFloat(v.numberValue, 'f', -1, 64)), nil
    75  	case StringType:
    76  		return json.Marshal(v.stringValue)
    77  	case ArrayType:
    78  		return v.arrayValue.MarshalJSON()
    79  	case ObjectType:
    80  		return v.objectValue.MarshalJSON()
    81  	case RawType:
    82  		return v.rawValue, nil
    83  	}
    84  	return nil, errors.New("unknown data type") // should not be possible
    85  }
    86  
    87  // UnmarshalJSON parses a Value from JSON.
    88  func (v *Value) UnmarshalJSON(data []byte) error {
    89  	return jreader.UnmarshalJSONWithReader(data, v)
    90  }
    91  
    92  // ReadFromJSONReader provides JSON deserialization for use with the jsonstream API.
    93  //
    94  // This implementation is used by the SDK in cases where it is more efficient than [json.Unmarshal].
    95  // See [github.com/launchdarkly/go-jsonstream/v3] for more details.
    96  func (v *Value) ReadFromJSONReader(r *jreader.Reader) {
    97  	a := r.Any()
    98  	if r.Error() != nil {
    99  		return
   100  	}
   101  	switch a.Kind {
   102  	case jreader.BoolValue:
   103  		*v = Bool(a.Bool)
   104  	case jreader.NumberValue:
   105  		*v = Float64(a.Number)
   106  	case jreader.StringValue:
   107  		*v = String(a.String)
   108  	case jreader.ArrayValue:
   109  		var va ValueArray
   110  		if va.readFromJSONArray(r, &a.Array); r.Error() == nil {
   111  			*v = Value{valueType: ArrayType, arrayValue: va}
   112  		}
   113  	case jreader.ObjectValue:
   114  		var vm ValueMap
   115  		if vm.readFromJSONObject(r, &a.Object); r.Error() == nil {
   116  			*v = Value{valueType: ObjectType, objectValue: vm}
   117  		}
   118  	default:
   119  		*v = Null()
   120  	}
   121  }
   122  
   123  // WriteToJSONWriter provides JSON serialization for use with the jsonstream API.
   124  //
   125  // This implementation is used by the SDK in cases where it is more efficient than [json.Marshal].
   126  // See [github.com/launchdarkly/go-jsonstream/v3] for more details.
   127  func (v Value) WriteToJSONWriter(w *jwriter.Writer) {
   128  	switch v.valueType {
   129  	case NullType:
   130  		w.Null()
   131  	case BoolType:
   132  		w.Bool(v.boolValue)
   133  	case NumberType:
   134  		w.Float64(v.numberValue)
   135  	case StringType:
   136  		w.String(v.stringValue)
   137  	case ArrayType:
   138  		v.arrayValue.WriteToJSONWriter(w)
   139  	case ObjectType:
   140  		v.objectValue.WriteToJSONWriter(w)
   141  	case RawType:
   142  		w.Raw(v.rawValue)
   143  	}
   144  }
   145  
   146  // JSONString returns the JSON representation of the array.
   147  func (a ValueArray) JSONString() string {
   148  	bytes, _ := a.MarshalJSON()
   149  	// It shouldn't be possible for marshalling to fail, because Value can only contain
   150  	// JSON-compatible types. But if it somehow did fail, bytes will be nil and we'll return
   151  	// an empty tring.
   152  	return string(bytes)
   153  }
   154  
   155  // MarshalJSON converts the ValueArray to its JSON representation.
   156  //
   157  // Like a Go slice, a ValueArray in an uninitialized/nil state produces a JSON null rather than an empty [].
   158  func (a ValueArray) MarshalJSON() ([]byte, error) {
   159  	if a.data == nil {
   160  		return nullAsJSONBytes, nil
   161  	}
   162  	return json.Marshal(a.data)
   163  }
   164  
   165  // UnmarshalJSON parses a ValueArray from JSON.
   166  func (a *ValueArray) UnmarshalJSON(data []byte) error {
   167  	return jreader.UnmarshalJSONWithReader(data, a)
   168  }
   169  
   170  // ReadFromJSONReader provides JSON deserialization for use with the jsonstream API.
   171  //
   172  // This implementation is used by the SDK in cases where it is more efficient than [json.Unmarshal].
   173  // See [github.com/launchdarkly/go-jsonstream/v3] for more details.
   174  func (a *ValueArray) ReadFromJSONReader(r *jreader.Reader) {
   175  	arr := r.ArrayOrNull()
   176  	a.readFromJSONArray(r, &arr)
   177  }
   178  
   179  // WriteToJSONWriter provides JSON serialization for use with the jsonstream API.
   180  //
   181  // This implementation is used by the SDK in cases where it is more efficient than [json.Marshal].
   182  // See [github.com/launchdarkly/go-jsonstream/v3] for more details.
   183  //
   184  // Like a Go slice, a ValueArray in an uninitialized/nil state produces a JSON null rather than an empty [].
   185  func (a ValueArray) WriteToJSONWriter(w *jwriter.Writer) {
   186  	if a.data == nil {
   187  		w.Null()
   188  		return
   189  	}
   190  	arr := w.Array()
   191  	for _, v := range a.data {
   192  		v.WriteToJSONWriter(w)
   193  	}
   194  	arr.End()
   195  }
   196  
   197  func (a *ValueArray) readFromJSONArray(r *jreader.Reader, arr *jreader.ArrayState) {
   198  	if r.Error() != nil {
   199  		return
   200  	}
   201  	if !arr.IsDefined() {
   202  		*a = ValueArray{}
   203  		return
   204  	}
   205  	var ab ValueArrayBuilder
   206  	for arr.Next() {
   207  		var vv Value
   208  		vv.ReadFromJSONReader(r)
   209  		ab.Add(vv)
   210  	}
   211  	if r.Error() == nil {
   212  		*a = ab.Build()
   213  	}
   214  }
   215  
   216  // String converts the value to a map representation, equivalent to JSONString().
   217  //
   218  // This method is provided because it is common to use the Stringer interface as a quick way to
   219  // summarize the contents of a value. The simplest way to do so in this case is to use the JSON
   220  // representation.
   221  func (m ValueMap) String() string {
   222  	return m.JSONString()
   223  }
   224  
   225  // JSONString returns the JSON representation of the map.
   226  func (m ValueMap) JSONString() string {
   227  	bytes, _ := m.MarshalJSON()
   228  	// It shouldn't be possible for marshalling to fail, because Value can only contain
   229  	// JSON-compatible types. But if it somehow did fail, bytes will be nil and we'll return
   230  	// an empty tring.
   231  	return string(bytes)
   232  }
   233  
   234  // MarshalJSON converts the ValueMap to its JSON representation.
   235  //
   236  // Like a Go map, a ValueMap in an uninitialized/nil state produces a JSON null rather than an empty {}.
   237  func (m ValueMap) MarshalJSON() ([]byte, error) {
   238  	return jwriter.MarshalJSONWithWriter(m)
   239  }
   240  
   241  // UnmarshalJSON parses a ValueMap from JSON.
   242  func (m *ValueMap) UnmarshalJSON(data []byte) error {
   243  	return jreader.UnmarshalJSONWithReader(data, m)
   244  }
   245  
   246  // ReadFromJSONReader provides JSON deserialization for use with the jsonstream API.
   247  //
   248  // This implementation is used by the SDK in cases where it is more efficient than [json.Unmarshal].
   249  // See [github.com/launchdarkly/go-jsonstream/v3] for more details.
   250  func (m *ValueMap) ReadFromJSONReader(r *jreader.Reader) {
   251  	obj := r.ObjectOrNull()
   252  	m.readFromJSONObject(r, &obj)
   253  }
   254  
   255  // WriteToJSONWriter provides JSON serialization for use with the jsonstream API.
   256  //
   257  // This implementation is used by the SDK in cases where it is more efficient than [json.Marshal].
   258  // See [github.com/launchdarkly/go-jsonstream/v3] for more details.
   259  //
   260  // Like a Go map, a ValueMap in an uninitialized/nil state produces a JSON null rather than an empty {}.
   261  func (m ValueMap) WriteToJSONWriter(w *jwriter.Writer) {
   262  	if m.data == nil {
   263  		w.Null()
   264  		return
   265  	}
   266  	obj := w.Object()
   267  	for k, vv := range m.data {
   268  		vv.WriteToJSONWriter(obj.Name(k))
   269  	}
   270  	obj.End()
   271  }
   272  
   273  func (m *ValueMap) readFromJSONObject(r *jreader.Reader, obj *jreader.ObjectState) {
   274  	if r.Error() != nil {
   275  		return
   276  	}
   277  	if !obj.IsDefined() {
   278  		*m = ValueMap{}
   279  		return
   280  	}
   281  	var mb ValueMapBuilder
   282  	for obj.Next() {
   283  		name := obj.Name()
   284  		var vv Value
   285  		vv.ReadFromJSONReader(r)
   286  		mb.Set(string(name), vv)
   287  	}
   288  	if r.Error() == nil {
   289  		*m = mb.Build()
   290  	}
   291  }
   292  

View as plain text