326 lines
7.4 KiB
Go
326 lines
7.4 KiB
Go
package jsonvalue
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"math"
|
|
"strings"
|
|
|
|
"github.com/Andrew-M-C/go.jsonvalue/internal/buffer"
|
|
"github.com/Andrew-M-C/go.jsonvalue/internal/unsafe"
|
|
)
|
|
|
|
// MustMarshal is the same as Marshal. If error occurs, an empty byte slice will be returned.
|
|
//
|
|
// MustMarshal 与 Marshal 相同,但是当错误发生时,什么都不做,直接返回空数据
|
|
func (v *V) MustMarshal(opts ...Option) []byte {
|
|
ret, err := v.Marshal(opts...)
|
|
if err != nil {
|
|
return []byte{}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
// MustMarshalString is the same as MarshalString, If error occurs, an empty byte slice will be returned.
|
|
//
|
|
// MustMarshalString 与 MarshalString 相同,但是当错误发生时,什么都不做,直接返回空数据
|
|
func (v *V) MustMarshalString(opt ...Option) string {
|
|
ret, err := v.MarshalString(opt...)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return ret
|
|
}
|
|
|
|
// Marshal returns marshaled bytes.
|
|
//
|
|
// Marshal 返回序列化后的 JSON 字节序列。
|
|
func (v *V) Marshal(opts ...Option) (b []byte, err error) {
|
|
if NotExist == v.valueType {
|
|
return nil, ErrValueUninitialized
|
|
}
|
|
|
|
buf := buffer.NewBuffer()
|
|
opt := combineOptions(opts)
|
|
|
|
err = marshalToBuffer(v, nil, buf, opt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
b = buf.Bytes()
|
|
return b, nil
|
|
}
|
|
|
|
// MarshalString is same with Marshal, but returns string. It is much more efficient than string(b).
|
|
//
|
|
// MarshalString 与 Marshal 相同, 不同的是返回 string 类型。它比 string(b) 操作更高效。
|
|
func (v *V) MarshalString(opts ...Option) (s string, err error) {
|
|
b, err := v.Marshal(opts...)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return unsafe.BtoS(b), nil
|
|
}
|
|
|
|
func marshalToBuffer(v *V, parentInfo *ParentInfo, buf buffer.Buffer, opt *Opt) (err error) {
|
|
switch v.valueType {
|
|
default:
|
|
// do nothing
|
|
case String:
|
|
marshalString(v, buf, opt)
|
|
case Boolean:
|
|
marshalBoolean(v, buf)
|
|
case Number:
|
|
err = marshalNumber(v, buf, opt)
|
|
case Null:
|
|
marshalNull(buf)
|
|
case Object:
|
|
marshalObject(v, parentInfo, buf, opt)
|
|
case Array:
|
|
marshalArray(v, parentInfo, buf, opt)
|
|
}
|
|
return err
|
|
}
|
|
|
|
func marshalString(v *V, buf buffer.Buffer, opt *Opt) {
|
|
_ = buf.WriteByte('"')
|
|
escapeStringToBuff(v.valueStr, buf, opt)
|
|
_ = buf.WriteByte('"')
|
|
}
|
|
|
|
func marshalBoolean(v *V, buf buffer.Buffer) {
|
|
_, _ = buf.WriteString(formatBool(v.valueBool))
|
|
}
|
|
|
|
func marshalNumber(v *V, buf buffer.Buffer, opt *Opt) error {
|
|
if b := v.srcByte; len(b) > 0 {
|
|
_, _ = buf.Write(b)
|
|
return nil
|
|
}
|
|
// else, +Inf or -Inf or NaN
|
|
if math.IsInf(v.num.f64, 1) { // +Inf
|
|
return marshalInfP(buf, opt)
|
|
}
|
|
if math.IsInf(v.num.f64, -1) { // -Inf
|
|
return marshalInfN(buf, opt)
|
|
}
|
|
|
|
return marshalNaN(buf, opt)
|
|
}
|
|
|
|
func marshalNaN(buf buffer.Buffer, opt *Opt) error {
|
|
switch opt.FloatNaNHandleType {
|
|
default:
|
|
fallthrough
|
|
case FloatNaNTreatAsError:
|
|
return fmt.Errorf("%w: %v", ErrUnsupportedFloat, math.NaN())
|
|
|
|
case FloatNaNConvertToFloat:
|
|
if !isValidFloat(opt.FloatNaNToFloat) {
|
|
return fmt.Errorf("%w: %v", ErrUnsupportedFloatInOpt, opt.FloatNaNToFloat)
|
|
}
|
|
b, _ := json.Marshal(opt.FloatNaNToFloat)
|
|
_, _ = buf.Write(b)
|
|
|
|
case FloatNaNNull:
|
|
_, _ = buf.WriteString("null")
|
|
|
|
case FloatNaNConvertToString:
|
|
if s := opt.FloatNaNToString; s == "" {
|
|
_, _ = buf.WriteString(`"NaN"`)
|
|
} else {
|
|
_ = buf.WriteByte('"')
|
|
escapeStringToBuff(s, buf, opt)
|
|
_ = buf.WriteByte('"')
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func marshalInfP(buf buffer.Buffer, opt *Opt) error {
|
|
switch opt.FloatInfHandleType {
|
|
default:
|
|
fallthrough
|
|
case FloatInfTreatAsError:
|
|
return fmt.Errorf("%w: %v", ErrUnsupportedFloat, math.Inf(1))
|
|
|
|
case FloatInfConvertToFloat:
|
|
if !isValidFloat(opt.FloatInfToFloat) {
|
|
return fmt.Errorf("%w: %v", ErrUnsupportedFloatInOpt, opt.FloatInfToFloat)
|
|
}
|
|
b, _ := json.Marshal(opt.FloatInfToFloat)
|
|
_, _ = buf.Write(b)
|
|
|
|
case FloatInfNull:
|
|
_, _ = buf.WriteString("null")
|
|
|
|
case FloatInfConvertToString:
|
|
if s := opt.FloatInfPositiveToString; s == "" {
|
|
_, _ = buf.WriteString(`"+Inf"`)
|
|
} else {
|
|
_ = buf.WriteByte('"')
|
|
escapeStringToBuff(s, buf, opt)
|
|
_ = buf.WriteByte('"')
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func marshalInfN(buf buffer.Buffer, opt *Opt) error {
|
|
switch opt.FloatInfHandleType {
|
|
default:
|
|
fallthrough
|
|
case FloatInfTreatAsError:
|
|
return fmt.Errorf("%w: %v", ErrUnsupportedFloat, math.Inf(-1))
|
|
|
|
case FloatInfConvertToFloat:
|
|
if !isValidFloat(opt.FloatInfToFloat) {
|
|
return fmt.Errorf("%w: %v", ErrUnsupportedFloatInOpt, -opt.FloatInfToFloat)
|
|
}
|
|
b, _ := json.Marshal(-opt.FloatInfToFloat)
|
|
_, _ = buf.Write(b)
|
|
|
|
case FloatInfNull:
|
|
_, _ = buf.WriteString("null")
|
|
|
|
case FloatInfConvertToString:
|
|
_ = buf.WriteByte('"')
|
|
if s := opt.FloatInfNegativeToString; s != "" {
|
|
escapeStringToBuff(s, buf, opt)
|
|
} else if opt.FloatInfPositiveToString != "" {
|
|
s = "-" + strings.TrimLeft(opt.FloatInfPositiveToString, "+")
|
|
escapeStringToBuff(s, buf, opt)
|
|
} else {
|
|
_, _ = buf.WriteString(`-Inf`)
|
|
}
|
|
_ = buf.WriteByte('"')
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func isValidFloat(f float64) bool {
|
|
if math.IsNaN(f) {
|
|
return false
|
|
}
|
|
if math.IsInf(f, 0) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func marshalNull(buf buffer.Buffer) {
|
|
_, _ = buf.WriteString("null")
|
|
}
|
|
|
|
func marshalObject(v *V, parentInfo *ParentInfo, buf buffer.Buffer, opt *Opt) {
|
|
if len(v.children.object) == 0 {
|
|
_, _ = buf.WriteString("{}")
|
|
return
|
|
}
|
|
|
|
opt.indent.cnt++
|
|
_ = buf.WriteByte('{')
|
|
|
|
if opt.MarshalLessFunc != nil {
|
|
sov := newSortObjectV(v, parentInfo, opt)
|
|
sov.marshalObjectWithLessFunc(buf, opt)
|
|
|
|
} else if len(opt.MarshalKeySequence) > 0 {
|
|
sssv := newSortStringSliceV(v, opt)
|
|
sssv.marshalObjectWithStringSlice(buf, opt)
|
|
|
|
} else if opt.marshalBySetSequence {
|
|
sssv := newSortStringSliceVBySetSeq(v)
|
|
sssv.marshalObjectWithStringSlice(buf, opt)
|
|
|
|
} else {
|
|
writeObjectKVInRandomizedSequence(v, buf, opt)
|
|
}
|
|
|
|
opt.indent.cnt--
|
|
if opt.indent.enabled {
|
|
_ = buf.WriteByte('\n')
|
|
writeIndent(buf, opt)
|
|
}
|
|
_ = buf.WriteByte('}')
|
|
}
|
|
|
|
func writeObjectKVInRandomizedSequence(v *V, buf buffer.Buffer, opt *Opt) {
|
|
firstWritten := false
|
|
for k, child := range v.children.object {
|
|
firstWritten = writeObjectChildren(nil, buf, !firstWritten, k, child.v, opt)
|
|
}
|
|
}
|
|
|
|
func writeObjectChildren(
|
|
parentInfo *ParentInfo, buf buffer.Buffer, isFirstOne bool, key string, child *V, opt *Opt,
|
|
) (written bool) {
|
|
if child.IsNull() && opt.OmitNull {
|
|
return false
|
|
}
|
|
if !isFirstOne {
|
|
_ = buf.WriteByte(',')
|
|
}
|
|
|
|
if opt.indent.enabled {
|
|
_ = buf.WriteByte('\n')
|
|
writeIndent(buf, opt)
|
|
}
|
|
|
|
_ = buf.WriteByte('"')
|
|
escapeStringToBuff(key, buf, opt)
|
|
|
|
if opt.indent.enabled {
|
|
_, _ = buf.WriteString("\": ")
|
|
} else {
|
|
_, _ = buf.WriteString("\":")
|
|
}
|
|
|
|
_ = marshalToBuffer(child, parentInfo, buf, opt)
|
|
return true
|
|
}
|
|
|
|
func writeIndent(buf buffer.Buffer, opt *Opt) {
|
|
_, _ = buf.WriteString(opt.indent.prefix)
|
|
for i := 0; i < opt.indent.cnt; i++ {
|
|
_, _ = buf.WriteString(opt.indent.indent)
|
|
}
|
|
}
|
|
|
|
func marshalArray(v *V, parentInfo *ParentInfo, buf buffer.Buffer, opt *Opt) {
|
|
if len(v.children.arr) == 0 {
|
|
_, _ = buf.WriteString("[]")
|
|
return
|
|
}
|
|
|
|
opt.indent.cnt++
|
|
_ = buf.WriteByte('[')
|
|
|
|
v.RangeArray(func(i int, child *V) bool {
|
|
if i > 0 {
|
|
_ = buf.WriteByte(',')
|
|
}
|
|
if opt.indent.enabled {
|
|
_ = buf.WriteByte('\n')
|
|
writeIndent(buf, opt)
|
|
}
|
|
if opt.MarshalLessFunc == nil {
|
|
_ = marshalToBuffer(child, nil, buf, opt)
|
|
} else {
|
|
_ = marshalToBuffer(child, newParentInfo(v, parentInfo, intKey(i)), buf, opt)
|
|
}
|
|
return true
|
|
})
|
|
|
|
opt.indent.cnt--
|
|
if opt.indent.enabled {
|
|
_ = buf.WriteByte('\n')
|
|
writeIndent(buf, opt)
|
|
}
|
|
_ = buf.WriteByte(']')
|
|
}
|