mirror of https://github.com/goccy/go-json.git
add omitnil for map and slice
This commit is contained in:
parent
a2149a5b25
commit
cbb3c17c5e
|
@ -637,6 +637,8 @@ func optimizeStructHeader(code *Opcode, tag *runtime.StructTag) OpType {
|
|||
headType := code.ToHeaderType(tag.IsString)
|
||||
if tag.IsOmitEmpty {
|
||||
headType = headType.HeadToOmitEmptyHead()
|
||||
} else if tag.IsOmitNil {
|
||||
headType = headType.HeadToOmitNilHead()
|
||||
}
|
||||
return headType
|
||||
}
|
||||
|
@ -645,6 +647,8 @@ func optimizeStructField(code *Opcode, tag *runtime.StructTag) OpType {
|
|||
fieldType := code.ToFieldType(tag.IsString)
|
||||
if tag.IsOmitEmpty {
|
||||
fieldType = fieldType.FieldToOmitEmptyField()
|
||||
} else if tag.IsOmitNil {
|
||||
fieldType = fieldType.FieldToOmitNilField()
|
||||
}
|
||||
return fieldType
|
||||
}
|
||||
|
|
|
@ -37,6 +37,12 @@ func (t OpType) IsMultipleOpHead() bool {
|
|||
return true
|
||||
case OpStructHeadOmitEmptyMap:
|
||||
return true
|
||||
case OpStructHeadOmitNilSlice:
|
||||
return true
|
||||
case OpStructHeadOmitNilArray:
|
||||
return true
|
||||
case OpStructHeadOmitNilMap:
|
||||
return true
|
||||
case OpStructHeadOmitEmptyStruct:
|
||||
return true
|
||||
case OpStructHeadSlicePtr:
|
||||
|
@ -75,6 +81,12 @@ func (t OpType) IsMultipleOpField() bool {
|
|||
return true
|
||||
case OpStructFieldOmitEmptyMap:
|
||||
return true
|
||||
case OpStructFieldOmitNilSlice:
|
||||
return true
|
||||
case OpStructFieldOmitNilArray:
|
||||
return true
|
||||
case OpStructFieldOmitNilMap:
|
||||
return true
|
||||
case OpStructFieldOmitEmptyStruct:
|
||||
return true
|
||||
case OpStructFieldSlicePtr:
|
||||
|
|
|
@ -22,7 +22,7 @@ const (
|
|||
CodeStructEnd CodeType = 11
|
||||
)
|
||||
|
||||
var opTypeStrings = [400]string{
|
||||
var opTypeStrings = [411]string{
|
||||
"End",
|
||||
"Interface",
|
||||
"Ptr",
|
||||
|
@ -423,6 +423,17 @@ var opTypeStrings = [400]string{
|
|||
"StructFieldOmitEmpty",
|
||||
"StructEnd",
|
||||
"StructEndOmitEmpty",
|
||||
"400",
|
||||
"StructHeadOmitNilArray",
|
||||
"StructFieldOmitNilArray",
|
||||
"403",
|
||||
"404",
|
||||
"StructHeadOmitNilMap",
|
||||
"StructFieldOmitNilMap",
|
||||
"407",
|
||||
"408",
|
||||
"StructHeadOmitNilSlice",
|
||||
"StructFieldOmitNilSlice",
|
||||
}
|
||||
|
||||
type OpType uint16
|
||||
|
@ -828,10 +839,17 @@ const (
|
|||
OpStructFieldOmitEmpty OpType = 397
|
||||
OpStructEnd OpType = 398
|
||||
OpStructEndOmitEmpty OpType = 399
|
||||
|
||||
OpStructHeadOmitNilArray OpType = 401
|
||||
OpStructHeadOmitNilMap OpType = 405
|
||||
OpStructHeadOmitNilSlice OpType = 409
|
||||
OpStructFieldOmitNilArray OpType = 402
|
||||
OpStructFieldOmitNilMap OpType = 406
|
||||
OpStructFieldOmitNilSlice OpType = 410
|
||||
)
|
||||
|
||||
func (t OpType) String() string {
|
||||
if int(t) >= 400 {
|
||||
if int(t) >= 411 {
|
||||
return ""
|
||||
}
|
||||
return opTypeStrings[int(t)]
|
||||
|
@ -893,6 +911,15 @@ func (t OpType) HeadToOmitEmptyHead() OpType {
|
|||
return t
|
||||
}
|
||||
|
||||
func (t OpType) HeadToOmitNilHead() OpType {
|
||||
const toOmitNilOffset = 313
|
||||
if strings.Contains(OpType(int(t)+toOmitNilOffset).String(), "OmitNil") {
|
||||
return OpType(int(t) + toOmitNilOffset)
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
func (t OpType) PtrHeadToHead() OpType {
|
||||
idx := strings.Index(t.String(), "PtrHead")
|
||||
if idx == -1 {
|
||||
|
@ -930,3 +957,11 @@ func (t OpType) FieldToOmitEmptyField() OpType {
|
|||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t OpType) FieldToOmitNilField() OpType {
|
||||
const toOmitNilOffset = 142
|
||||
if strings.Contains(OpType(int(t)+toOmitNilOffset).String(), "OmitNil") {
|
||||
return OpType(int(t) + toOmitNilOffset)
|
||||
}
|
||||
return t
|
||||
}
|
|
@ -2725,6 +2725,27 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
|
|||
code = code.Next
|
||||
store(ctxptr, code.Idx, p)
|
||||
}
|
||||
case encoder.OpStructHeadOmitNilSlice:
|
||||
p := load(ctxptr, code.Idx)
|
||||
if p == 0 {
|
||||
if code.Flags&encoder.AnonymousHeadFlags == 0 {
|
||||
b = appendNullComma(ctx, b)
|
||||
}
|
||||
code = code.End.Next
|
||||
break
|
||||
}
|
||||
if code.Flags&encoder.AnonymousHeadFlags == 0 {
|
||||
b = appendStructHead(ctx, b)
|
||||
}
|
||||
p += uintptr(code.Offset)
|
||||
slice := ptrToSlice(p)
|
||||
if slice.Data == nil {
|
||||
code = code.NextField
|
||||
} else {
|
||||
b = appendStructKey(ctx, code, b)
|
||||
code = code.Next
|
||||
store(ctxptr, code.Idx, p)
|
||||
}
|
||||
case encoder.OpStructPtrHeadArrayPtr, encoder.OpStructPtrHeadSlicePtr:
|
||||
p := load(ctxptr, code.Idx)
|
||||
if p == 0 {
|
||||
|
@ -2854,6 +2875,28 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
|
|||
code = code.Next
|
||||
store(ctxptr, code.Idx, p)
|
||||
}
|
||||
case encoder.OpStructHeadOmitNilMap:
|
||||
p := load(ctxptr, code.Idx)
|
||||
if p == 0 && (code.Flags&encoder.IndirectFlags) != 0 {
|
||||
if code.Flags&encoder.AnonymousHeadFlags == 0 {
|
||||
b = appendNullComma(ctx, b)
|
||||
}
|
||||
code = code.End.Next
|
||||
break
|
||||
}
|
||||
if code.Flags&encoder.AnonymousHeadFlags == 0 {
|
||||
b = appendStructHead(ctx, b)
|
||||
}
|
||||
if p != 0 && (code.Flags&encoder.IndirectFlags) != 0 {
|
||||
p = ptrToPtr(p + uintptr(code.Offset))
|
||||
}
|
||||
if ptrToUnsafePtr(p) == nil {
|
||||
code = code.NextField
|
||||
} else {
|
||||
b = appendStructKey(ctx, code, b)
|
||||
code = code.Next
|
||||
store(ctxptr, code.Idx, p)
|
||||
}
|
||||
case encoder.OpStructPtrHeadMapPtr:
|
||||
p := load(ctxptr, code.Idx)
|
||||
if p == 0 {
|
||||
|
@ -4082,6 +4125,17 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
|
|||
code = code.Next
|
||||
store(ctxptr, code.Idx, p)
|
||||
}
|
||||
case encoder.OpStructFieldOmitNilSlice:
|
||||
p := load(ctxptr, code.Idx)
|
||||
p += uintptr(code.Offset)
|
||||
slice := ptrToSlice(p)
|
||||
if slice.Data == nil {
|
||||
code = code.NextField
|
||||
} else {
|
||||
b = appendStructKey(ctx, code, b)
|
||||
code = code.Next
|
||||
store(ctxptr, code.Idx, p)
|
||||
}
|
||||
case encoder.OpStructFieldSlicePtr:
|
||||
b = appendStructKey(ctx, code, b)
|
||||
p := load(ctxptr, code.Idx)
|
||||
|
@ -4114,6 +4168,16 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
|
|||
code = code.Next
|
||||
store(ctxptr, code.Idx, p)
|
||||
}
|
||||
case encoder.OpStructFieldOmitNilMap:
|
||||
p := load(ctxptr, code.Idx)
|
||||
p = ptrToPtr(p + uintptr(code.Offset))
|
||||
if ptrToUnsafePtr(p) == nil {
|
||||
code = code.NextField
|
||||
} else {
|
||||
b = appendStructKey(ctx, code, b)
|
||||
code = code.Next
|
||||
store(ctxptr, code.Idx, p)
|
||||
}
|
||||
case encoder.OpStructFieldMapPtr:
|
||||
b = appendStructKey(ctx, code, b)
|
||||
p := load(ctxptr, code.Idx)
|
||||
|
|
|
@ -33,6 +33,7 @@ type StructTag struct {
|
|||
Key string
|
||||
IsTaggedKey bool
|
||||
IsOmitEmpty bool
|
||||
IsOmitNil bool
|
||||
IsString bool
|
||||
Field reflect.StructField
|
||||
}
|
||||
|
@ -82,6 +83,8 @@ func StructTagFromField(field reflect.StructField) *StructTag {
|
|||
switch opt {
|
||||
case "omitempty":
|
||||
st.IsOmitEmpty = true
|
||||
case "omitnil":
|
||||
st.IsOmitNil = true
|
||||
case "string":
|
||||
st.IsString = true
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/goccy/go-json"
|
||||
)
|
||||
|
@ -311,3 +312,18 @@ func ExampleHTMLEscape() {
|
|||
// Output:
|
||||
//{"Name":"\u003cb\u003eHTML content\u003c/b\u003e"}
|
||||
}
|
||||
|
||||
func TestOmitNil(t *testing.T) {
|
||||
type Foo struct {
|
||||
Bar []string `json:"bar,omitnil"`
|
||||
Baz map[string]string `json:"baz,omitnil"`
|
||||
}
|
||||
var a, b Foo
|
||||
b.Bar = []string{} // <- empty, not nil
|
||||
b.Baz = map[string]string{} // <- empty, not nil
|
||||
|
||||
e := json.NewEncoder(os.Stdout)
|
||||
_ = e.Encode(a)
|
||||
println("")
|
||||
_ = e.Encode(b)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue