forked from mirror/sjson
Merge branch 'master' of https://github.com/jkearse3/sjson into jkearse3-master
This commit is contained in:
commit
10d4d354a8
181
sjson.go
181
sjson.go
|
@ -3,9 +3,7 @@ package sjson
|
||||||
|
|
||||||
import (
|
import (
|
||||||
jsongo "encoding/json"
|
jsongo "encoding/json"
|
||||||
"reflect"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
)
|
)
|
||||||
|
@ -410,72 +408,6 @@ func isOptimisticPath(path string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func set(jstr, path, raw string,
|
|
||||||
stringify, del, optimistic, inplace bool) ([]byte, error) {
|
|
||||||
if path == "" {
|
|
||||||
return nil, &errorType{"path cannot be empty"}
|
|
||||||
}
|
|
||||||
if !del && optimistic && isOptimisticPath(path) {
|
|
||||||
res := gjson.Get(jstr, path)
|
|
||||||
if res.Exists() && res.Index > 0 {
|
|
||||||
sz := len(jstr) - len(res.Raw) + len(raw)
|
|
||||||
if stringify {
|
|
||||||
sz += 2
|
|
||||||
}
|
|
||||||
if inplace && sz <= len(jstr) {
|
|
||||||
if !stringify || !mustMarshalString(raw) {
|
|
||||||
jsonh := *(*reflect.StringHeader)(unsafe.Pointer(&jstr))
|
|
||||||
jsonbh := reflect.SliceHeader{
|
|
||||||
Data: jsonh.Data, Len: jsonh.Len, Cap: jsonh.Len}
|
|
||||||
jbytes := *(*[]byte)(unsafe.Pointer(&jsonbh))
|
|
||||||
if stringify {
|
|
||||||
jbytes[res.Index] = '"'
|
|
||||||
copy(jbytes[res.Index+1:], []byte(raw))
|
|
||||||
jbytes[res.Index+1+len(raw)] = '"'
|
|
||||||
copy(jbytes[res.Index+1+len(raw)+1:],
|
|
||||||
jbytes[res.Index+len(res.Raw):])
|
|
||||||
} else {
|
|
||||||
copy(jbytes[res.Index:], []byte(raw))
|
|
||||||
copy(jbytes[res.Index+len(raw):],
|
|
||||||
jbytes[res.Index+len(res.Raw):])
|
|
||||||
}
|
|
||||||
return jbytes[:sz], nil
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
buf := make([]byte, 0, sz)
|
|
||||||
buf = append(buf, jstr[:res.Index]...)
|
|
||||||
if stringify {
|
|
||||||
buf = appendStringify(buf, raw)
|
|
||||||
} else {
|
|
||||||
buf = append(buf, raw...)
|
|
||||||
}
|
|
||||||
buf = append(buf, jstr[res.Index+len(res.Raw):]...)
|
|
||||||
return buf, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// parse the path, make sure that it does not contain invalid characters
|
|
||||||
// such as '#', '?', '*'
|
|
||||||
paths := make([]pathResult, 0, 4)
|
|
||||||
r, err := parsePath(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
paths = append(paths, r)
|
|
||||||
for r.more {
|
|
||||||
if r, err = parsePath(r.path); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
paths = append(paths, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
njson, err := appendRawPaths(nil, jstr, paths, raw, stringify, del)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return njson, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set sets a json value for the specified path.
|
// Set sets a json value for the specified path.
|
||||||
// A path is in dot syntax, such as "name.last" or "age".
|
// A path is in dot syntax, such as "name.last" or "age".
|
||||||
// This function expects that the json is well-formed, and does not validate.
|
// This function expects that the json is well-formed, and does not validate.
|
||||||
|
@ -501,29 +433,6 @@ func Set(json, path string, value interface{}) (string, error) {
|
||||||
return SetOptions(json, path, value, nil)
|
return SetOptions(json, path, value, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetOptions sets a json value for the specified path with options.
|
|
||||||
// A path is in dot syntax, such as "name.last" or "age".
|
|
||||||
// This function expects that the json is well-formed, and does not validate.
|
|
||||||
// Invalid json will not panic, but it may return back unexpected results.
|
|
||||||
// An error is returned if the path is not valid.
|
|
||||||
func SetOptions(json, path string, value interface{},
|
|
||||||
opts *Options) (string, error) {
|
|
||||||
if opts != nil {
|
|
||||||
if opts.ReplaceInPlace {
|
|
||||||
// it's not safe to replace bytes in-place for strings
|
|
||||||
// copy the Options and set options.ReplaceInPlace to false.
|
|
||||||
nopts := *opts
|
|
||||||
opts = &nopts
|
|
||||||
opts.ReplaceInPlace = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
jsonh := *(*reflect.StringHeader)(unsafe.Pointer(&json))
|
|
||||||
jsonbh := reflect.SliceHeader{Data: jsonh.Data, Len: jsonh.Len}
|
|
||||||
jsonb := *(*[]byte)(unsafe.Pointer(&jsonbh))
|
|
||||||
res, err := SetBytesOptions(jsonb, path, value, opts)
|
|
||||||
return string(res), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetBytes sets a json value for the specified path.
|
// SetBytes sets a json value for the specified path.
|
||||||
// If working with bytes, this method preferred over
|
// If working with bytes, this method preferred over
|
||||||
// Set(string(data), path, value)
|
// Set(string(data), path, value)
|
||||||
|
@ -531,77 +440,6 @@ func SetBytes(json []byte, path string, value interface{}) ([]byte, error) {
|
||||||
return SetBytesOptions(json, path, value, nil)
|
return SetBytesOptions(json, path, value, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetBytesOptions sets a json value for the specified path with options.
|
|
||||||
// If working with bytes, this method preferred over
|
|
||||||
// SetOptions(string(data), path, value)
|
|
||||||
func SetBytesOptions(json []byte, path string, value interface{},
|
|
||||||
opts *Options) ([]byte, error) {
|
|
||||||
var optimistic, inplace bool
|
|
||||||
if opts != nil {
|
|
||||||
optimistic = opts.Optimistic
|
|
||||||
inplace = opts.ReplaceInPlace
|
|
||||||
}
|
|
||||||
jstr := *(*string)(unsafe.Pointer(&json))
|
|
||||||
var res []byte
|
|
||||||
var err error
|
|
||||||
switch v := value.(type) {
|
|
||||||
default:
|
|
||||||
b, err := jsongo.Marshal(value)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
raw := *(*string)(unsafe.Pointer(&b))
|
|
||||||
res, err = set(jstr, path, raw, false, false, optimistic, inplace)
|
|
||||||
case dtype:
|
|
||||||
res, err = set(jstr, path, "", false, true, optimistic, inplace)
|
|
||||||
case string:
|
|
||||||
res, err = set(jstr, path, v, true, false, optimistic, inplace)
|
|
||||||
case []byte:
|
|
||||||
raw := *(*string)(unsafe.Pointer(&v))
|
|
||||||
res, err = set(jstr, path, raw, true, false, optimistic, inplace)
|
|
||||||
case bool:
|
|
||||||
if v {
|
|
||||||
res, err = set(jstr, path, "true", false, false, optimistic, inplace)
|
|
||||||
} else {
|
|
||||||
res, err = set(jstr, path, "false", false, false, optimistic, inplace)
|
|
||||||
}
|
|
||||||
case int8:
|
|
||||||
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
|
||||||
false, false, optimistic, inplace)
|
|
||||||
case int16:
|
|
||||||
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
|
||||||
false, false, optimistic, inplace)
|
|
||||||
case int32:
|
|
||||||
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
|
||||||
false, false, optimistic, inplace)
|
|
||||||
case int64:
|
|
||||||
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
|
||||||
false, false, optimistic, inplace)
|
|
||||||
case uint8:
|
|
||||||
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
|
||||||
false, false, optimistic, inplace)
|
|
||||||
case uint16:
|
|
||||||
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
|
||||||
false, false, optimistic, inplace)
|
|
||||||
case uint32:
|
|
||||||
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
|
||||||
false, false, optimistic, inplace)
|
|
||||||
case uint64:
|
|
||||||
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
|
||||||
false, false, optimistic, inplace)
|
|
||||||
case float32:
|
|
||||||
res, err = set(jstr, path, strconv.FormatFloat(float64(v), 'f', -1, 64),
|
|
||||||
false, false, optimistic, inplace)
|
|
||||||
case float64:
|
|
||||||
res, err = set(jstr, path, strconv.FormatFloat(float64(v), 'f', -1, 64),
|
|
||||||
false, false, optimistic, inplace)
|
|
||||||
}
|
|
||||||
if err == errNoChange {
|
|
||||||
return json, nil
|
|
||||||
}
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetRaw sets a raw json value for the specified path.
|
// SetRaw sets a raw json value for the specified path.
|
||||||
// This function works the same as Set except that the value is set as a
|
// This function works the same as Set except that the value is set as a
|
||||||
// raw block of json. This allows for setting premarshalled json objects.
|
// raw block of json. This allows for setting premarshalled json objects.
|
||||||
|
@ -631,25 +469,6 @@ func SetRawBytes(json []byte, path string, value []byte) ([]byte, error) {
|
||||||
return SetRawBytesOptions(json, path, value, nil)
|
return SetRawBytesOptions(json, path, value, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetRawBytesOptions sets a raw json value for the specified path with options.
|
|
||||||
// If working with bytes, this method preferred over
|
|
||||||
// SetRawOptions(string(data), path, value, opts)
|
|
||||||
func SetRawBytesOptions(json []byte, path string, value []byte,
|
|
||||||
opts *Options) ([]byte, error) {
|
|
||||||
jstr := *(*string)(unsafe.Pointer(&json))
|
|
||||||
vstr := *(*string)(unsafe.Pointer(&value))
|
|
||||||
var optimistic, inplace bool
|
|
||||||
if opts != nil {
|
|
||||||
optimistic = opts.Optimistic
|
|
||||||
inplace = opts.ReplaceInPlace
|
|
||||||
}
|
|
||||||
res, err := set(jstr, path, vstr, false, false, optimistic, inplace)
|
|
||||||
if err == errNoChange {
|
|
||||||
return json, nil
|
|
||||||
}
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
type dtype struct{}
|
type dtype struct{}
|
||||||
|
|
||||||
// Delete deletes a value from json for the specified path.
|
// Delete deletes a value from json for the specified path.
|
||||||
|
|
|
@ -0,0 +1,196 @@
|
||||||
|
//+build appengine
|
||||||
|
|
||||||
|
package sjson
|
||||||
|
|
||||||
|
import (
|
||||||
|
jsongo "encoding/json"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/tidwall/gjson"
|
||||||
|
)
|
||||||
|
|
||||||
|
func set(jstr, path, raw string,
|
||||||
|
stringify, del, optimistic, inplace bool) ([]byte, error) {
|
||||||
|
if path == "" {
|
||||||
|
return nil, &errorType{"path cannot be empty"}
|
||||||
|
}
|
||||||
|
if !del && optimistic && isOptimisticPath(path) {
|
||||||
|
res := gjson.Get(jstr, path)
|
||||||
|
if res.Exists() && res.Index > 0 {
|
||||||
|
sz := len(jstr) - len(res.Raw) + len(raw)
|
||||||
|
if stringify {
|
||||||
|
sz += 2
|
||||||
|
}
|
||||||
|
if inplace && sz <= len(jstr) {
|
||||||
|
if !stringify || !mustMarshalString(raw) {
|
||||||
|
// jsonh := *(*reflect.StringHeader)(unsafe.Pointer(&jstr))
|
||||||
|
// jsonbh := reflect.SliceHeader{
|
||||||
|
// Data: jsonh.Data, Len: jsonh.Len, Cap: jsonh.Len}
|
||||||
|
// jbytes := *(*[]byte)(unsafe.Pointer(&jsonbh))
|
||||||
|
jbytes := []byte(jstr)
|
||||||
|
if stringify {
|
||||||
|
jbytes[res.Index] = '"'
|
||||||
|
copy(jbytes[res.Index+1:], []byte(raw))
|
||||||
|
jbytes[res.Index+1+len(raw)] = '"'
|
||||||
|
copy(jbytes[res.Index+1+len(raw)+1:],
|
||||||
|
jbytes[res.Index+len(res.Raw):])
|
||||||
|
} else {
|
||||||
|
copy(jbytes[res.Index:], []byte(raw))
|
||||||
|
copy(jbytes[res.Index+len(raw):],
|
||||||
|
jbytes[res.Index+len(res.Raw):])
|
||||||
|
}
|
||||||
|
return jbytes[:sz], nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
buf := make([]byte, 0, sz)
|
||||||
|
buf = append(buf, jstr[:res.Index]...)
|
||||||
|
if stringify {
|
||||||
|
buf = appendStringify(buf, raw)
|
||||||
|
} else {
|
||||||
|
buf = append(buf, raw...)
|
||||||
|
}
|
||||||
|
buf = append(buf, jstr[res.Index+len(res.Raw):]...)
|
||||||
|
return buf, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// parse the path, make sure that it does not contain invalid characters
|
||||||
|
// such as '#', '?', '*'
|
||||||
|
paths := make([]pathResult, 0, 4)
|
||||||
|
r, err := parsePath(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
paths = append(paths, r)
|
||||||
|
for r.more {
|
||||||
|
if r, err = parsePath(r.path); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
paths = append(paths, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
njson, err := appendRawPaths(nil, jstr, paths, raw, stringify, del)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return njson, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOptions sets a json value for the specified path with options.
|
||||||
|
// A path is in dot syntax, such as "name.last" or "age".
|
||||||
|
// This function expects that the json is well-formed, and does not validate.
|
||||||
|
// Invalid json will not panic, but it may return back unexpected results.
|
||||||
|
// An error is returned if the path is not valid.
|
||||||
|
func SetOptions(json, path string, value interface{},
|
||||||
|
opts *Options) (string, error) {
|
||||||
|
if opts != nil {
|
||||||
|
if opts.ReplaceInPlace {
|
||||||
|
// it's not safe to replace bytes in-place for strings
|
||||||
|
// copy the Options and set options.ReplaceInPlace to false.
|
||||||
|
nopts := *opts
|
||||||
|
opts = &nopts
|
||||||
|
opts.ReplaceInPlace = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// jsonh := *(*reflect.StringHeader)(unsafe.Pointer(&json))
|
||||||
|
// jsonbh := reflect.SliceHeader{Data: jsonh.Data, Len: jsonh.Len}
|
||||||
|
// jsonb := *(*[]byte)(unsafe.Pointer(&jsonbh))
|
||||||
|
jsonb := []byte(json)
|
||||||
|
res, err := SetBytesOptions(jsonb, path, value, opts)
|
||||||
|
return string(res), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBytesOptions sets a json value for the specified path with options.
|
||||||
|
// If working with bytes, this method preferred over
|
||||||
|
// SetOptions(string(data), path, value)
|
||||||
|
func SetBytesOptions(json []byte, path string, value interface{},
|
||||||
|
opts *Options) ([]byte, error) {
|
||||||
|
var optimistic, inplace bool
|
||||||
|
if opts != nil {
|
||||||
|
optimistic = opts.Optimistic
|
||||||
|
inplace = opts.ReplaceInPlace
|
||||||
|
}
|
||||||
|
// jstr := *(*string)(unsafe.Pointer(&json))
|
||||||
|
jstr := string(json)
|
||||||
|
var res []byte
|
||||||
|
var err error
|
||||||
|
switch v := value.(type) {
|
||||||
|
default:
|
||||||
|
b, err := jsongo.Marshal(value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// raw := *(*string)(unsafe.Pointer(&b))
|
||||||
|
raw := string(b)
|
||||||
|
res, err = set(jstr, path, raw, false, false, optimistic, inplace)
|
||||||
|
case dtype:
|
||||||
|
res, err = set(jstr, path, "", false, true, optimistic, inplace)
|
||||||
|
case string:
|
||||||
|
res, err = set(jstr, path, v, true, false, optimistic, inplace)
|
||||||
|
case []byte:
|
||||||
|
// raw := *(*string)(unsafe.Pointer(&v))
|
||||||
|
raw := string(v)
|
||||||
|
res, err = set(jstr, path, raw, true, false, optimistic, inplace)
|
||||||
|
case bool:
|
||||||
|
if v {
|
||||||
|
res, err = set(jstr, path, "true", false, false, optimistic, inplace)
|
||||||
|
} else {
|
||||||
|
res, err = set(jstr, path, "false", false, false, optimistic, inplace)
|
||||||
|
}
|
||||||
|
case int8:
|
||||||
|
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
||||||
|
false, false, optimistic, inplace)
|
||||||
|
case int16:
|
||||||
|
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
||||||
|
false, false, optimistic, inplace)
|
||||||
|
case int32:
|
||||||
|
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
||||||
|
false, false, optimistic, inplace)
|
||||||
|
case int64:
|
||||||
|
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
||||||
|
false, false, optimistic, inplace)
|
||||||
|
case uint8:
|
||||||
|
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
||||||
|
false, false, optimistic, inplace)
|
||||||
|
case uint16:
|
||||||
|
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
||||||
|
false, false, optimistic, inplace)
|
||||||
|
case uint32:
|
||||||
|
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
||||||
|
false, false, optimistic, inplace)
|
||||||
|
case uint64:
|
||||||
|
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
||||||
|
false, false, optimistic, inplace)
|
||||||
|
case float32:
|
||||||
|
res, err = set(jstr, path, strconv.FormatFloat(float64(v), 'f', -1, 64),
|
||||||
|
false, false, optimistic, inplace)
|
||||||
|
case float64:
|
||||||
|
res, err = set(jstr, path, strconv.FormatFloat(float64(v), 'f', -1, 64),
|
||||||
|
false, false, optimistic, inplace)
|
||||||
|
}
|
||||||
|
if err == errNoChange {
|
||||||
|
return json, nil
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRawBytesOptions sets a raw json value for the specified path with options.
|
||||||
|
// If working with bytes, this method preferred over
|
||||||
|
// SetRawOptions(string(data), path, value, opts)
|
||||||
|
func SetRawBytesOptions(json []byte, path string, value []byte,
|
||||||
|
opts *Options) ([]byte, error) {
|
||||||
|
// jstr := *(*string)(unsafe.Pointer(&json))
|
||||||
|
// vstr := *(*string)(unsafe.Pointer(&value))
|
||||||
|
jstr := string(json)
|
||||||
|
vstr := string(value)
|
||||||
|
var optimistic, inplace bool
|
||||||
|
if opts != nil {
|
||||||
|
optimistic = opts.Optimistic
|
||||||
|
inplace = opts.ReplaceInPlace
|
||||||
|
}
|
||||||
|
res, err := set(jstr, path, vstr, false, false, optimistic, inplace)
|
||||||
|
if err == errNoChange {
|
||||||
|
return json, nil
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
|
@ -0,0 +1,191 @@
|
||||||
|
//+build !appengine
|
||||||
|
|
||||||
|
package sjson
|
||||||
|
|
||||||
|
import (
|
||||||
|
jsongo "encoding/json"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/tidwall/gjson"
|
||||||
|
)
|
||||||
|
|
||||||
|
func set(jstr, path, raw string,
|
||||||
|
stringify, del, optimistic, inplace bool) ([]byte, error) {
|
||||||
|
if path == "" {
|
||||||
|
return nil, &errorType{"path cannot be empty"}
|
||||||
|
}
|
||||||
|
if !del && optimistic && isOptimisticPath(path) {
|
||||||
|
res := gjson.Get(jstr, path)
|
||||||
|
if res.Exists() && res.Index > 0 {
|
||||||
|
sz := len(jstr) - len(res.Raw) + len(raw)
|
||||||
|
if stringify {
|
||||||
|
sz += 2
|
||||||
|
}
|
||||||
|
if inplace && sz <= len(jstr) {
|
||||||
|
if !stringify || !mustMarshalString(raw) {
|
||||||
|
jsonh := *(*reflect.StringHeader)(unsafe.Pointer(&jstr))
|
||||||
|
jsonbh := reflect.SliceHeader{
|
||||||
|
Data: jsonh.Data, Len: jsonh.Len, Cap: jsonh.Len}
|
||||||
|
jbytes := *(*[]byte)(unsafe.Pointer(&jsonbh))
|
||||||
|
if stringify {
|
||||||
|
jbytes[res.Index] = '"'
|
||||||
|
copy(jbytes[res.Index+1:], []byte(raw))
|
||||||
|
jbytes[res.Index+1+len(raw)] = '"'
|
||||||
|
copy(jbytes[res.Index+1+len(raw)+1:],
|
||||||
|
jbytes[res.Index+len(res.Raw):])
|
||||||
|
} else {
|
||||||
|
copy(jbytes[res.Index:], []byte(raw))
|
||||||
|
copy(jbytes[res.Index+len(raw):],
|
||||||
|
jbytes[res.Index+len(res.Raw):])
|
||||||
|
}
|
||||||
|
return jbytes[:sz], nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
buf := make([]byte, 0, sz)
|
||||||
|
buf = append(buf, jstr[:res.Index]...)
|
||||||
|
if stringify {
|
||||||
|
buf = appendStringify(buf, raw)
|
||||||
|
} else {
|
||||||
|
buf = append(buf, raw...)
|
||||||
|
}
|
||||||
|
buf = append(buf, jstr[res.Index+len(res.Raw):]...)
|
||||||
|
return buf, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// parse the path, make sure that it does not contain invalid characters
|
||||||
|
// such as '#', '?', '*'
|
||||||
|
paths := make([]pathResult, 0, 4)
|
||||||
|
r, err := parsePath(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
paths = append(paths, r)
|
||||||
|
for r.more {
|
||||||
|
if r, err = parsePath(r.path); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
paths = append(paths, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
njson, err := appendRawPaths(nil, jstr, paths, raw, stringify, del)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return njson, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOptions sets a json value for the specified path with options.
|
||||||
|
// A path is in dot syntax, such as "name.last" or "age".
|
||||||
|
// This function expects that the json is well-formed, and does not validate.
|
||||||
|
// Invalid json will not panic, but it may return back unexpected results.
|
||||||
|
// An error is returned if the path is not valid.
|
||||||
|
func SetOptions(json, path string, value interface{},
|
||||||
|
opts *Options) (string, error) {
|
||||||
|
if opts != nil {
|
||||||
|
if opts.ReplaceInPlace {
|
||||||
|
// it's not safe to replace bytes in-place for strings
|
||||||
|
// copy the Options and set options.ReplaceInPlace to false.
|
||||||
|
nopts := *opts
|
||||||
|
opts = &nopts
|
||||||
|
opts.ReplaceInPlace = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jsonh := *(*reflect.StringHeader)(unsafe.Pointer(&json))
|
||||||
|
jsonbh := reflect.SliceHeader{Data: jsonh.Data, Len: jsonh.Len}
|
||||||
|
jsonb := *(*[]byte)(unsafe.Pointer(&jsonbh))
|
||||||
|
res, err := SetBytesOptions(jsonb, path, value, opts)
|
||||||
|
return string(res), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBytesOptions sets a json value for the specified path with options.
|
||||||
|
// If working with bytes, this method preferred over
|
||||||
|
// SetOptions(string(data), path, value)
|
||||||
|
func SetBytesOptions(json []byte, path string, value interface{},
|
||||||
|
opts *Options) ([]byte, error) {
|
||||||
|
var optimistic, inplace bool
|
||||||
|
if opts != nil {
|
||||||
|
optimistic = opts.Optimistic
|
||||||
|
inplace = opts.ReplaceInPlace
|
||||||
|
}
|
||||||
|
jstr := *(*string)(unsafe.Pointer(&json))
|
||||||
|
var res []byte
|
||||||
|
var err error
|
||||||
|
switch v := value.(type) {
|
||||||
|
default:
|
||||||
|
b, err := jsongo.Marshal(value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
raw := *(*string)(unsafe.Pointer(&b))
|
||||||
|
res, err = set(jstr, path, raw, false, false, optimistic, inplace)
|
||||||
|
case dtype:
|
||||||
|
res, err = set(jstr, path, "", false, true, optimistic, inplace)
|
||||||
|
case string:
|
||||||
|
res, err = set(jstr, path, v, true, false, optimistic, inplace)
|
||||||
|
case []byte:
|
||||||
|
raw := *(*string)(unsafe.Pointer(&v))
|
||||||
|
res, err = set(jstr, path, raw, true, false, optimistic, inplace)
|
||||||
|
case bool:
|
||||||
|
if v {
|
||||||
|
res, err = set(jstr, path, "true", false, false, optimistic, inplace)
|
||||||
|
} else {
|
||||||
|
res, err = set(jstr, path, "false", false, false, optimistic, inplace)
|
||||||
|
}
|
||||||
|
case int8:
|
||||||
|
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
||||||
|
false, false, optimistic, inplace)
|
||||||
|
case int16:
|
||||||
|
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
||||||
|
false, false, optimistic, inplace)
|
||||||
|
case int32:
|
||||||
|
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
||||||
|
false, false, optimistic, inplace)
|
||||||
|
case int64:
|
||||||
|
res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
|
||||||
|
false, false, optimistic, inplace)
|
||||||
|
case uint8:
|
||||||
|
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
||||||
|
false, false, optimistic, inplace)
|
||||||
|
case uint16:
|
||||||
|
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
||||||
|
false, false, optimistic, inplace)
|
||||||
|
case uint32:
|
||||||
|
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
||||||
|
false, false, optimistic, inplace)
|
||||||
|
case uint64:
|
||||||
|
res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
|
||||||
|
false, false, optimistic, inplace)
|
||||||
|
case float32:
|
||||||
|
res, err = set(jstr, path, strconv.FormatFloat(float64(v), 'f', -1, 64),
|
||||||
|
false, false, optimistic, inplace)
|
||||||
|
case float64:
|
||||||
|
res, err = set(jstr, path, strconv.FormatFloat(float64(v), 'f', -1, 64),
|
||||||
|
false, false, optimistic, inplace)
|
||||||
|
}
|
||||||
|
if err == errNoChange {
|
||||||
|
return json, nil
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRawBytesOptions sets a raw json value for the specified path with options.
|
||||||
|
// If working with bytes, this method preferred over
|
||||||
|
// SetRawOptions(string(data), path, value, opts)
|
||||||
|
func SetRawBytesOptions(json []byte, path string, value []byte,
|
||||||
|
opts *Options) ([]byte, error) {
|
||||||
|
jstr := *(*string)(unsafe.Pointer(&json))
|
||||||
|
vstr := *(*string)(unsafe.Pointer(&value))
|
||||||
|
var optimistic, inplace bool
|
||||||
|
if opts != nil {
|
||||||
|
optimistic = opts.Optimistic
|
||||||
|
inplace = opts.ReplaceInPlace
|
||||||
|
}
|
||||||
|
res, err := set(jstr, path, vstr, false, false, optimistic, inplace)
|
||||||
|
if err == errNoChange {
|
||||||
|
return json, nil
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
Loading…
Reference in New Issue