Merge pull request #12 from goccy/feature/fix-field-after-omitempty

Fix encoding of struct field after omitempty field
This commit is contained in:
Masaaki Goshima 2020-08-08 19:00:47 +09:00 committed by GitHub
commit c359cc258f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 249 additions and 37 deletions

View File

@ -16,6 +16,9 @@ func (e *Encoder) compileHead(typ *rtype, withIndent bool) (*opcode, error) {
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
}
if typ.Kind() == reflect.Map {
return e.compileMap(typ, false, withIndent)
}
return e.compile(typ, withIndent)
}
@ -33,7 +36,7 @@ func (e *Encoder) compile(typ *rtype, withIndent bool) (*opcode, error) {
case reflect.Array:
return e.compileArray(typ, withIndent)
case reflect.Map:
return e.compileMap(typ, withIndent)
return e.compileMap(typ, true, withIndent)
case reflect.Struct:
return e.compileStruct(typ, withIndent)
case reflect.Int:
@ -366,7 +369,7 @@ func mapiternext(it unsafe.Pointer)
//go:noescape
func maplen(m unsafe.Pointer) int
func (e *Encoder) compileMap(typ *rtype, withIndent bool) (*opcode, error) {
func (e *Encoder) compileMap(typ *rtype, withLoad, withIndent bool) (*opcode, error) {
// header => code => value => code => key => code => value => code => end
// ^ |
// |_______________________|
@ -387,13 +390,17 @@ func (e *Encoder) compileMap(typ *rtype, withIndent bool) (*opcode, error) {
e.indent--
header := newMapHeaderCode(typ, e.indent)
header := newMapHeaderCode(typ, withLoad, e.indent)
header.key = key
header.value = value
end := newOpCode(opMapEnd, nil, e.indent, newEndOp(e.indent))
if withIndent {
if header.op == opMapHead {
header.op = opMapHeadIndent
} else {
header.op = opMapHeadLoadIndent
}
key.op = opMapKeyIndent
value.op = opMapValueIndent
end.op = opMapEndIndent

View File

@ -47,11 +47,13 @@ const (
opArrayEndIndent
opMapHead
opMapHeadLoad
opMapKey
opMapValue
opMapEnd
opMapHeadIndent
opMapHeadLoadIndent
opMapKeyIndent
opMapValueIndent
opMapEndIndent
@ -327,8 +329,9 @@ func (t opType) String() string {
case opArrayEndIndent:
return "ARRAY_END_INDENT"
case opMapHead:
return "MAP_HEAD"
case opMapHeadLoad:
return "MAP_HEAD_LOAD"
case opMapKey:
return "MAP_KEY"
case opMapValue:
@ -338,6 +341,8 @@ func (t opType) String() string {
case opMapHeadIndent:
return "MAP_HEAD_INDENT"
case opMapHeadLoadIndent:
return "MAP_HEAD_LOAD_INDENT"
case opMapKeyIndent:
return "MAP_KEY_INDENT"
case opMapValueIndent:
@ -919,10 +924,16 @@ func (c *mapValueCode) set(iter unsafe.Pointer) {
c.iter = iter
}
func newMapHeaderCode(typ *rtype, indent int) *mapHeaderCode {
func newMapHeaderCode(typ *rtype, withLoad bool, indent int) *mapHeaderCode {
var op opType
if withLoad {
op = opMapHeadLoad
} else {
op = opMapHead
}
return &mapHeaderCode{
opcodeHeader: &opcodeHeader{
op: opMapHead,
op: op,
typ: typ,
indent: indent,
},

View File

@ -129,6 +129,116 @@ func Test_Marshal(t *testing.T) {
bytes, err := json.Marshal(&v)
assertErr(t, err)
assertEq(t, "struct", `{"t":1}`, string(bytes))
t.Run("int", func(t *testing.T) {
var v struct {
A int `json:"a,omitempty"`
B int `json:"b"`
}
v.B = 1
bytes, err := json.Marshal(&v)
assertErr(t, err)
assertEq(t, "int", `{"b":1}`, string(bytes))
})
t.Run("int8", func(t *testing.T) {
var v struct {
A int `json:"a,omitempty"`
B int8 `json:"b"`
}
v.B = 1
bytes, err := json.Marshal(&v)
assertErr(t, err)
assertEq(t, "int8", `{"b":1}`, string(bytes))
})
t.Run("int16", func(t *testing.T) {
var v struct {
A int `json:"a,omitempty"`
B int16 `json:"b"`
}
v.B = 1
bytes, err := json.Marshal(&v)
assertErr(t, err)
assertEq(t, "int16", `{"b":1}`, string(bytes))
})
t.Run("int32", func(t *testing.T) {
var v struct {
A int `json:"a,omitempty"`
B int32 `json:"b"`
}
v.B = 1
bytes, err := json.Marshal(&v)
assertErr(t, err)
assertEq(t, "int32", `{"b":1}`, string(bytes))
})
t.Run("int64", func(t *testing.T) {
var v struct {
A int `json:"a,omitempty"`
B int64 `json:"b"`
}
v.B = 1
bytes, err := json.Marshal(&v)
assertErr(t, err)
assertEq(t, "int64", `{"b":1}`, string(bytes))
})
t.Run("string", func(t *testing.T) {
var v struct {
A int `json:"a,omitempty"`
B string `json:"b"`
}
v.B = "b"
bytes, err := json.Marshal(&v)
assertErr(t, err)
assertEq(t, "string", `{"b":"b"}`, string(bytes))
})
t.Run("float32", func(t *testing.T) {
var v struct {
A int `json:"a,omitempty"`
B float32 `json:"b"`
}
v.B = 1.1
bytes, err := json.Marshal(&v)
assertErr(t, err)
assertEq(t, "float32", `{"b":1.1}`, string(bytes))
})
t.Run("float64", func(t *testing.T) {
var v struct {
A int `json:"a,omitempty"`
B float64 `json:"b"`
}
v.B = 3.14
bytes, err := json.Marshal(&v)
assertErr(t, err)
assertEq(t, "float64", `{"b":3.14}`, string(bytes))
})
t.Run("slice", func(t *testing.T) {
var v struct {
A int `json:"a,omitempty"`
B []int `json:"b"`
}
v.B = []int{1, 2, 3}
bytes, err := json.Marshal(&v)
assertErr(t, err)
assertEq(t, "slice", `{"b":[1,2,3]}`, string(bytes))
})
t.Run("array", func(t *testing.T) {
var v struct {
A int `json:"a,omitempty"`
B [2]int `json:"b"`
}
v.B = [2]int{1, 2}
bytes, err := json.Marshal(&v)
assertErr(t, err)
assertEq(t, "array", `{"b":[1,2]}`, string(bytes))
})
t.Run("map", func(t *testing.T) {
v := new(struct {
A int `json:"a,omitempty"`
B map[string]interface{} `json:"b"`
})
v.B = map[string]interface{}{"c": 1}
bytes, err := json.Marshal(v)
assertErr(t, err)
assertEq(t, "array", `{"b":{"c":1}}`, string(bytes))
})
})
t.Run("head_omitempty", func(t *testing.T) {
type T struct {

View File

@ -264,6 +264,30 @@ func (e *Encoder) run(code *opcode) error {
code = mapHeadCode.end.next
}
}
case opMapHeadLoad:
ptr := code.ptr
mapHeadCode := code.toMapHeadCode()
if ptr == 0 {
e.encodeNull()
code = mapHeadCode.end.next
} else {
// load pointer
ptr = uintptr(*(*unsafe.Pointer)(unsafe.Pointer(ptr)))
e.encodeByte('{')
mlen := maplen(unsafe.Pointer(ptr))
if mlen > 0 {
iter := mapiterinit(code.typ, unsafe.Pointer(ptr))
mapHeadCode.key.set(mlen, iter)
mapHeadCode.value.set(iter)
key := mapiterkey(iter)
code.next.ptr = uintptr(key)
code = code.next
} else {
e.encodeByte('}')
code = mapHeadCode.end.next
}
}
case opMapKey:
c := code.toMapKeyCode()
c.idx++
@ -1681,91 +1705,117 @@ func (e *Encoder) run(code *opcode) error {
field.nextField.ptr = field.ptr
}
case opStructField:
if e.buf[len(e.buf)-1] != '{' {
e.encodeByte(',')
}
c := code.toStructFieldCode()
e.encodeBytes(c.key)
code = code.next
code.ptr = c.ptr + c.offset
c.nextField.ptr = c.ptr
case opStructFieldInt:
if e.buf[len(e.buf)-1] != '{' {
e.encodeByte(',')
}
c := code.toStructFieldCode()
c.nextField.ptr = c.ptr
e.encodeBytes(c.key)
e.encodeInt(e.ptrToInt(c.ptr + c.offset))
code = code.next
case opStructFieldInt8:
if e.buf[len(e.buf)-1] != '{' {
e.encodeByte(',')
}
c := code.toStructFieldCode()
c.nextField.ptr = c.ptr
e.encodeBytes(c.key)
e.encodeInt8(e.ptrToInt8(c.ptr + c.offset))
code = code.next
case opStructFieldInt16:
if e.buf[len(e.buf)-1] != '{' {
e.encodeByte(',')
}
c := code.toStructFieldCode()
c.nextField.ptr = c.ptr
e.encodeBytes(c.key)
e.encodeInt16(e.ptrToInt16(c.ptr + c.offset))
code = code.next
case opStructFieldInt32:
if e.buf[len(e.buf)-1] != '{' {
e.encodeByte(',')
}
c := code.toStructFieldCode()
c.nextField.ptr = c.ptr
e.encodeBytes(c.key)
e.encodeInt32(e.ptrToInt32(c.ptr + c.offset))
code = code.next
case opStructFieldInt64:
if e.buf[len(e.buf)-1] != '{' {
e.encodeByte(',')
}
c := code.toStructFieldCode()
c.nextField.ptr = c.ptr
e.encodeBytes(c.key)
e.encodeInt64(e.ptrToInt64(c.ptr + c.offset))
code = code.next
case opStructFieldUint:
if e.buf[len(e.buf)-1] != '{' {
e.encodeByte(',')
}
c := code.toStructFieldCode()
c.nextField.ptr = c.ptr
e.encodeBytes(c.key)
e.encodeUint(e.ptrToUint(c.ptr + c.offset))
code = code.next
case opStructFieldUint8:
if e.buf[len(e.buf)-1] != '{' {
e.encodeByte(',')
}
c := code.toStructFieldCode()
c.nextField.ptr = c.ptr
e.encodeBytes(c.key)
e.encodeUint8(e.ptrToUint8(c.ptr + c.offset))
code = code.next
case opStructFieldUint16:
if e.buf[len(e.buf)-1] != '{' {
e.encodeByte(',')
}
c := code.toStructFieldCode()
c.nextField.ptr = c.ptr
e.encodeBytes(c.key)
e.encodeUint16(e.ptrToUint16(c.ptr + c.offset))
code = code.next
case opStructFieldUint32:
if e.buf[len(e.buf)-1] != '{' {
e.encodeByte(',')
}
c := code.toStructFieldCode()
c.nextField.ptr = c.ptr
e.encodeBytes(c.key)
e.encodeUint32(e.ptrToUint32(c.ptr + c.offset))
code = code.next
case opStructFieldUint64:
if e.buf[len(e.buf)-1] != '{' {
e.encodeByte(',')
}
c := code.toStructFieldCode()
c.nextField.ptr = c.ptr
e.encodeBytes(c.key)
e.encodeUint64(e.ptrToUint64(c.ptr + c.offset))
code = code.next
case opStructFieldFloat32:
if e.buf[len(e.buf)-1] != '{' {
e.encodeByte(',')
}
c := code.toStructFieldCode()
c.nextField.ptr = c.ptr
e.encodeBytes(c.key)
e.encodeFloat32(e.ptrToFloat32(c.ptr + c.offset))
code = code.next
case opStructFieldFloat64:
if e.buf[len(e.buf)-1] != '{' {
e.encodeByte(',')
}
c := code.toStructFieldCode()
c.nextField.ptr = c.ptr
e.encodeBytes(c.key)
@ -1779,14 +1829,18 @@ func (e *Encoder) run(code *opcode) error {
e.encodeFloat64(v)
code = code.next
case opStructFieldString:
if e.buf[len(e.buf)-1] != '{' {
e.encodeByte(',')
}
c := code.toStructFieldCode()
c.nextField.ptr = c.ptr
e.encodeBytes(c.key)
e.encodeString(e.ptrToString(c.ptr + c.offset))
code = code.next
case opStructFieldBool:
if e.buf[len(e.buf)-1] != '{' {
e.encodeByte(',')
}
c := code.toStructFieldCode()
c.nextField.ptr = c.ptr
e.encodeBytes(c.key)
@ -1795,7 +1849,9 @@ func (e *Encoder) run(code *opcode) error {
case opStructFieldIndent:
c := code.toStructFieldCode()
if e.buf[len(e.buf)-2] != '{' {
e.encodeBytes([]byte{',', '\n'})
}
e.encodeIndent(c.indent)
e.encodeBytes(c.key)
e.encodeByte(' ')
@ -1804,7 +1860,9 @@ func (e *Encoder) run(code *opcode) error {
c.nextField.ptr = c.ptr
case opStructFieldIntIndent:
c := code.toStructFieldCode()
if e.buf[len(e.buf)-2] != '{' {
e.encodeBytes([]byte{',', '\n'})
}
e.encodeIndent(c.indent)
e.encodeBytes(c.key)
e.encodeByte(' ')
@ -1813,7 +1871,9 @@ func (e *Encoder) run(code *opcode) error {
c.nextField.ptr = c.ptr
case opStructFieldInt8Indent:
c := code.toStructFieldCode()
if e.buf[len(e.buf)-2] != '{' {
e.encodeBytes([]byte{',', '\n'})
}
e.encodeIndent(c.indent)
e.encodeBytes(c.key)
e.encodeByte(' ')
@ -1822,7 +1882,9 @@ func (e *Encoder) run(code *opcode) error {
c.nextField.ptr = c.ptr
case opStructFieldInt16Indent:
c := code.toStructFieldCode()
if e.buf[len(e.buf)-2] != '{' {
e.encodeBytes([]byte{',', '\n'})
}
e.encodeIndent(c.indent)
e.encodeBytes(c.key)
e.encodeByte(' ')
@ -1831,7 +1893,9 @@ func (e *Encoder) run(code *opcode) error {
c.nextField.ptr = c.ptr
case opStructFieldInt32Indent:
c := code.toStructFieldCode()
if e.buf[len(e.buf)-2] != '{' {
e.encodeBytes([]byte{',', '\n'})
}
e.encodeIndent(c.indent)
e.encodeBytes(c.key)
e.encodeByte(' ')
@ -1840,7 +1904,9 @@ func (e *Encoder) run(code *opcode) error {
c.nextField.ptr = c.ptr
case opStructFieldInt64Indent:
c := code.toStructFieldCode()
if e.buf[len(e.buf)-2] != '{' {
e.encodeBytes([]byte{',', '\n'})
}
e.encodeIndent(c.indent)
e.encodeBytes(c.key)
e.encodeByte(' ')
@ -1849,7 +1915,9 @@ func (e *Encoder) run(code *opcode) error {
c.nextField.ptr = c.ptr
case opStructFieldUintIndent:
c := code.toStructFieldCode()
if e.buf[len(e.buf)-2] != '{' {
e.encodeBytes([]byte{',', '\n'})
}
e.encodeIndent(c.indent)
e.encodeBytes(c.key)
e.encodeByte(' ')
@ -1858,7 +1926,9 @@ func (e *Encoder) run(code *opcode) error {
c.nextField.ptr = c.ptr
case opStructFieldUint8Indent:
c := code.toStructFieldCode()
if e.buf[len(e.buf)-2] != '{' {
e.encodeBytes([]byte{',', '\n'})
}
e.encodeIndent(c.indent)
e.encodeBytes(c.key)
e.encodeByte(' ')
@ -1867,7 +1937,9 @@ func (e *Encoder) run(code *opcode) error {
c.nextField.ptr = c.ptr
case opStructFieldUint16Indent:
c := code.toStructFieldCode()
if e.buf[len(e.buf)-2] != '{' {
e.encodeBytes([]byte{',', '\n'})
}
e.encodeIndent(c.indent)
e.encodeBytes(c.key)
e.encodeByte(' ')
@ -1876,7 +1948,9 @@ func (e *Encoder) run(code *opcode) error {
c.nextField.ptr = c.ptr
case opStructFieldUint32Indent:
c := code.toStructFieldCode()
if e.buf[len(e.buf)-2] != '{' {
e.encodeBytes([]byte{',', '\n'})
}
e.encodeIndent(c.indent)
e.encodeBytes(c.key)
e.encodeByte(' ')
@ -1885,7 +1959,9 @@ func (e *Encoder) run(code *opcode) error {
c.nextField.ptr = c.ptr
case opStructFieldUint64Indent:
c := code.toStructFieldCode()
if e.buf[len(e.buf)-2] != '{' {
e.encodeBytes([]byte{',', '\n'})
}
e.encodeIndent(c.indent)
e.encodeBytes(c.key)
e.encodeByte(' ')
@ -1894,7 +1970,9 @@ func (e *Encoder) run(code *opcode) error {
c.nextField.ptr = c.ptr
case opStructFieldFloat32Indent:
c := code.toStructFieldCode()
if e.buf[len(e.buf)-2] != '{' {
e.encodeBytes([]byte{',', '\n'})
}
e.encodeIndent(c.indent)
e.encodeBytes(c.key)
e.encodeByte(' ')
@ -1903,7 +1981,9 @@ func (e *Encoder) run(code *opcode) error {
c.nextField.ptr = c.ptr
case opStructFieldFloat64Indent:
c := code.toStructFieldCode()
if e.buf[len(e.buf)-2] != '{' {
e.encodeBytes([]byte{',', '\n'})
}
e.encodeIndent(c.indent)
e.encodeBytes(c.key)
e.encodeByte(' ')
@ -1919,7 +1999,9 @@ func (e *Encoder) run(code *opcode) error {
c.nextField.ptr = c.ptr
case opStructFieldStringIndent:
c := code.toStructFieldCode()
if e.buf[len(e.buf)-2] != '{' {
e.encodeBytes([]byte{',', '\n'})
}
e.encodeIndent(c.indent)
e.encodeBytes(c.key)
e.encodeByte(' ')
@ -1928,7 +2010,9 @@ func (e *Encoder) run(code *opcode) error {
c.nextField.ptr = c.ptr
case opStructFieldBoolIndent:
c := code.toStructFieldCode()
if e.buf[len(e.buf)-2] != '{' {
e.encodeBytes([]byte{',', '\n'})
}
e.encodeIndent(c.indent)
e.encodeBytes(c.key)
e.encodeByte(' ')