mirror of https://github.com/goccy/go-json.git
Add test cases for MarshalJSON and MarshalText
This commit is contained in:
parent
4232d1d16d
commit
1d2beb8473
|
@ -171,7 +171,7 @@ func (t opType) fieldToOmitEmptyField() opType {
|
|||
primitiveTypes := []string{
|
||||
"int", "int8", "int16", "int32", "int64",
|
||||
"uint", "uint8", "uint16", "uint32", "uint64",
|
||||
"float32", "float64", "bool", "string",
|
||||
"float32", "float64", "bool", "string", "MarshalJSON", "MarshalText",
|
||||
}
|
||||
primitiveTypesUpper := []string{}
|
||||
for _, typ := range primitiveTypes {
|
||||
|
@ -181,8 +181,6 @@ func (t opType) fieldToOmitEmptyField() opType {
|
|||
{"End", "EndIndent", "Op"},
|
||||
{"Interface", "InterfaceIndent", "Op"},
|
||||
{"Ptr", "PtrIndent", "Op"},
|
||||
{"MarshalJSON", "MarshalJSONIndent", "Op"},
|
||||
{"MarshalText", "MarshalTextIndent", "Op"},
|
||||
{"SliceHead", "SliceHeadIndent", "SliceHead"},
|
||||
{"RootSliceHead", "RootSliceHeadIndent", "SliceHead"},
|
||||
{"SliceElem", "SliceElemIndent", "SliceElem"},
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
)
|
||||
|
||||
func (e *Encoder) compileHead(typ *rtype, withIndent bool) (*opcode, error) {
|
||||
root := true
|
||||
switch {
|
||||
case typ.Implements(marshalJSONType):
|
||||
return newOpCode(opMarshalJSON, typ, e.indent, newEndOp(e.indent)), nil
|
||||
|
@ -18,12 +19,15 @@ func (e *Encoder) compileHead(typ *rtype, withIndent bool) (*opcode, error) {
|
|||
case rtype_ptrTo(typ).Implements(marshalTextType):
|
||||
return newOpCode(opMarshalText, rtype_ptrTo(typ), e.indent, newEndOp(e.indent)), nil
|
||||
}
|
||||
isPtr := false
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
typ = typ.Elem()
|
||||
isPtr = true
|
||||
}
|
||||
root := true
|
||||
if typ.Kind() == reflect.Map {
|
||||
return e.compileMap(typ, false, root, withIndent)
|
||||
return e.compileMap(typ, isPtr, root, withIndent)
|
||||
} else if typ.Kind() == reflect.Struct {
|
||||
return e.compileStruct(typ, isPtr, root, withIndent)
|
||||
}
|
||||
return e.compile(typ, root, withIndent)
|
||||
}
|
||||
|
@ -49,7 +53,7 @@ func (e *Encoder) compile(typ *rtype, root, withIndent bool) (*opcode, error) {
|
|||
case reflect.Map:
|
||||
return e.compileMap(typ, true, root, withIndent)
|
||||
case reflect.Struct:
|
||||
return e.compileStruct(typ, root, withIndent)
|
||||
return e.compileStruct(typ, false, root, withIndent)
|
||||
case reflect.Interface:
|
||||
return e.compileInterface(typ, root)
|
||||
case reflect.Int:
|
||||
|
@ -376,6 +380,10 @@ func (e *Encoder) typeToHeaderType(op opType) opType {
|
|||
return opStructFieldHeadString
|
||||
case opBool:
|
||||
return opStructFieldHeadBool
|
||||
case opMarshalJSON:
|
||||
return opStructFieldHeadMarshalJSON
|
||||
case opMarshalText:
|
||||
return opStructFieldHeadMarshalText
|
||||
}
|
||||
return opStructFieldHead
|
||||
}
|
||||
|
@ -410,6 +418,10 @@ func (e *Encoder) typeToFieldType(op opType) opType {
|
|||
return opStructFieldString
|
||||
case opBool:
|
||||
return opStructFieldBool
|
||||
case opMarshalJSON:
|
||||
return opStructFieldMarshalJSON
|
||||
case opMarshalText:
|
||||
return opStructFieldMarshalText
|
||||
}
|
||||
return opStructField
|
||||
}
|
||||
|
@ -505,7 +517,7 @@ func (e *Encoder) structField(fieldCode *structFieldCode, valueCode *opcode, isO
|
|||
}
|
||||
return code
|
||||
}
|
||||
func (e *Encoder) compileStruct(typ *rtype, root, withIndent bool) (*opcode, error) {
|
||||
func (e *Encoder) compileStruct(typ *rtype, isPtr, root, withIndent bool) (*opcode, error) {
|
||||
if code := e.compiledCode(typ, withIndent); code != nil {
|
||||
return code, nil
|
||||
}
|
||||
|
@ -534,6 +546,15 @@ func (e *Encoder) compileStruct(typ *rtype, root, withIndent bool) (*opcode, err
|
|||
}
|
||||
keyName, isOmitEmpty := e.keyNameAndOmitEmptyFromField(field)
|
||||
fieldType := type2rtype(field.Type)
|
||||
if isPtr && i == 0 {
|
||||
// head field of pointer structure at top level
|
||||
// if field type is pointer and implements MarshalJSON or MarshalText,
|
||||
// it need to operation of dereference of pointer.
|
||||
if field.Type.Kind() == reflect.Ptr &&
|
||||
(field.Type.Implements(marshalJSONType) || field.Type.Implements(marshalTextType)) {
|
||||
fieldType = rtype_ptrTo(fieldType)
|
||||
}
|
||||
}
|
||||
valueCode, err := e.compile(fieldType, false, withIndent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -554,7 +575,7 @@ func (e *Encoder) compileStruct(typ *rtype, root, withIndent bool) (*opcode, err
|
|||
key := fmt.Sprintf(`"%s":`, keyName)
|
||||
fieldCode := &structFieldCode{
|
||||
opcodeHeader: &opcodeHeader{
|
||||
typ: fieldType,
|
||||
typ: valueCode.typ,
|
||||
next: valueCode,
|
||||
indent: e.indent,
|
||||
},
|
||||
|
|
1162
encode_optype.go
1162
encode_optype.go
File diff suppressed because it is too large
Load Diff
159
encode_test.go
159
encode_test.go
|
@ -3,6 +3,7 @@ package json_test
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -491,6 +492,108 @@ func Test_MarshalIndent(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestMarshalRawMessageValue(t *testing.T) {
|
||||
type (
|
||||
T1 struct {
|
||||
M json.RawMessage `json:",omitempty"`
|
||||
}
|
||||
T2 struct {
|
||||
M *json.RawMessage `json:",omitempty"`
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
rawNil = json.RawMessage(nil)
|
||||
rawEmpty = json.RawMessage([]byte{})
|
||||
rawText = json.RawMessage([]byte(`"foo"`))
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
in interface{}
|
||||
want string
|
||||
ok bool
|
||||
}{
|
||||
// Test with nil RawMessage.
|
||||
{rawNil, "null", true},
|
||||
{&rawNil, "null", true},
|
||||
{[]interface{}{rawNil}, "[null]", true},
|
||||
{&[]interface{}{rawNil}, "[null]", true},
|
||||
{[]interface{}{&rawNil}, "[null]", true},
|
||||
{&[]interface{}{&rawNil}, "[null]", true},
|
||||
{struct{ M json.RawMessage }{rawNil}, `{"M":null}`, true},
|
||||
{&struct{ M json.RawMessage }{rawNil}, `{"M":null}`, true},
|
||||
{struct{ M *json.RawMessage }{&rawNil}, `{"M":null}`, true},
|
||||
{&struct{ M *json.RawMessage }{&rawNil}, `{"M":null}`, true},
|
||||
{map[string]interface{}{"M": rawNil}, `{"M":null}`, true},
|
||||
{&map[string]interface{}{"M": rawNil}, `{"M":null}`, true},
|
||||
{map[string]interface{}{"M": &rawNil}, `{"M":null}`, true},
|
||||
{&map[string]interface{}{"M": &rawNil}, `{"M":null}`, true},
|
||||
{T1{rawNil}, "{}", true},
|
||||
{T2{&rawNil}, `{"M":null}`, true},
|
||||
{&T1{rawNil}, "{}", true},
|
||||
{&T2{&rawNil}, `{"M":null}`, true},
|
||||
|
||||
// Test with empty, but non-nil, RawMessage.
|
||||
{rawEmpty, "", false},
|
||||
{&rawEmpty, "", false},
|
||||
{[]interface{}{rawEmpty}, "", false},
|
||||
{&[]interface{}{rawEmpty}, "", false},
|
||||
{[]interface{}{&rawEmpty}, "", false},
|
||||
{&[]interface{}{&rawEmpty}, "", false},
|
||||
{struct{ X json.RawMessage }{rawEmpty}, "", false},
|
||||
{&struct{ X json.RawMessage }{rawEmpty}, "", false},
|
||||
{struct{ X *json.RawMessage }{&rawEmpty}, "", false},
|
||||
{&struct{ X *json.RawMessage }{&rawEmpty}, "", false},
|
||||
{map[string]interface{}{"nil": rawEmpty}, "", false},
|
||||
{&map[string]interface{}{"nil": rawEmpty}, "", false},
|
||||
{map[string]interface{}{"nil": &rawEmpty}, "", false},
|
||||
{&map[string]interface{}{"nil": &rawEmpty}, "", false},
|
||||
|
||||
{T1{rawEmpty}, "{}", true},
|
||||
{T2{&rawEmpty}, "", false},
|
||||
{&T1{rawEmpty}, "{}", true},
|
||||
{&T2{&rawEmpty}, "", false},
|
||||
|
||||
// Test with RawMessage with some text.
|
||||
//
|
||||
// The tests below marked with Issue6458 used to generate "ImZvbyI=" instead "foo".
|
||||
// This behavior was intentionally changed in Go 1.8.
|
||||
// See https://golang.org/issues/14493#issuecomment-255857318
|
||||
{rawText, `"foo"`, true}, // Issue6458
|
||||
{&rawText, `"foo"`, true},
|
||||
{[]interface{}{rawText}, `["foo"]`, true}, // Issue6458
|
||||
{&[]interface{}{rawText}, `["foo"]`, true}, // Issue6458
|
||||
{[]interface{}{&rawText}, `["foo"]`, true},
|
||||
{&[]interface{}{&rawText}, `["foo"]`, true},
|
||||
{struct{ M json.RawMessage }{rawText}, `{"M":"foo"}`, true}, // Issue6458
|
||||
{&struct{ M json.RawMessage }{rawText}, `{"M":"foo"}`, true},
|
||||
{struct{ M *json.RawMessage }{&rawText}, `{"M":"foo"}`, true},
|
||||
{&struct{ M *json.RawMessage }{&rawText}, `{"M":"foo"}`, true},
|
||||
{map[string]interface{}{"M": rawText}, `{"M":"foo"}`, true}, // Issue6458
|
||||
{&map[string]interface{}{"M": rawText}, `{"M":"foo"}`, true}, // Issue6458
|
||||
{map[string]interface{}{"M": &rawText}, `{"M":"foo"}`, true},
|
||||
{&map[string]interface{}{"M": &rawText}, `{"M":"foo"}`, true},
|
||||
{T1{rawText}, `{"M":"foo"}`, true}, // Issue6458
|
||||
{T2{&rawText}, `{"M":"foo"}`, true},
|
||||
{&T1{rawText}, `{"M":"foo"}`, true},
|
||||
{&T2{&rawText}, `{"M":"foo"}`, true},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
b, err := json.Marshal(tt.in)
|
||||
if ok := (err == nil); ok != tt.ok {
|
||||
if err != nil {
|
||||
t.Errorf("test %d, unexpected failure: %v", i, err)
|
||||
} else {
|
||||
t.Errorf("test %d, unexpected success", i)
|
||||
}
|
||||
}
|
||||
if got := string(b); got != tt.want {
|
||||
t.Errorf("test %d, Marshal(%#v) = %q, want %q", i, tt.in, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type marshalerError struct{}
|
||||
|
||||
func (*marshalerError) MarshalJSON() ([]byte, error) {
|
||||
|
@ -607,3 +710,59 @@ func TestMarshalerEscaping(t *testing.T) {
|
|||
t.Errorf("Marshal(ct) = %#q, want %#q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
type marshalPanic struct{}
|
||||
|
||||
func (marshalPanic) MarshalJSON() ([]byte, error) { panic(0xdead) }
|
||||
|
||||
func TestMarshalPanic(t *testing.T) {
|
||||
defer func() {
|
||||
if got := recover(); !reflect.DeepEqual(got, 0xdead) {
|
||||
t.Errorf("panic() = (%T)(%v), want 0xdead", got, got)
|
||||
}
|
||||
}()
|
||||
json.Marshal(&marshalPanic{})
|
||||
t.Error("Marshal should have panicked")
|
||||
}
|
||||
|
||||
func TestMarshalUncommonFieldNames(t *testing.T) {
|
||||
v := struct {
|
||||
A0, À, Aβ int
|
||||
}{}
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
t.Fatal("Marshal:", err)
|
||||
}
|
||||
want := `{"A0":0,"À":0,"Aβ":0}`
|
||||
got := string(b)
|
||||
if got != want {
|
||||
t.Fatalf("Marshal: got %s want %s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalerError(t *testing.T) {
|
||||
s := "test variable"
|
||||
st := reflect.TypeOf(s)
|
||||
errText := "json: test error"
|
||||
|
||||
tests := []struct {
|
||||
err *json.MarshalerError
|
||||
want string
|
||||
}{
|
||||
{
|
||||
json.NewMarshalerError(st, fmt.Errorf(errText), ""),
|
||||
"json: error calling MarshalJSON for type " + st.String() + ": " + errText,
|
||||
},
|
||||
{
|
||||
json.NewMarshalerError(st, fmt.Errorf(errText), "TestMarshalerError"),
|
||||
"json: error calling TestMarshalerError for type " + st.String() + ": " + errText,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
got := tt.err.Error()
|
||||
if got != tt.want {
|
||||
t.Errorf("MarshalerError test %d, got: %s, want: %s", i, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
378
encode_vm.go
378
encode_vm.go
|
@ -3,6 +3,7 @@ package json
|
|||
import (
|
||||
"bytes"
|
||||
"encoding"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
@ -115,6 +116,12 @@ func (e *Encoder) run(code *opcode) error {
|
|||
Err: err,
|
||||
}
|
||||
}
|
||||
if len(b) == 0 {
|
||||
return errUnexpectedEndOfJSON(
|
||||
fmt.Sprintf("error calling MarshalJSON for type %s", code.typ),
|
||||
0,
|
||||
)
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
if err := compact(&buf, b, true); err != nil {
|
||||
return err
|
||||
|
@ -933,6 +940,131 @@ func (e *Encoder) run(code *opcode) error {
|
|||
field.nextField.ptr = ptr
|
||||
code = field.next
|
||||
}
|
||||
case opStructFieldPtrHeadMarshalJSON:
|
||||
code.ptr = e.ptrToPtr(code.ptr)
|
||||
fallthrough
|
||||
case opStructFieldHeadMarshalJSON:
|
||||
field := code.toStructFieldCode()
|
||||
ptr := field.ptr
|
||||
if ptr == 0 {
|
||||
e.encodeNull()
|
||||
code = field.end
|
||||
} else {
|
||||
e.encodeByte('{')
|
||||
e.encodeBytes(field.key)
|
||||
fmt.Println("code.typ = ", code.typ)
|
||||
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{
|
||||
typ: code.typ,
|
||||
ptr: unsafe.Pointer(ptr),
|
||||
}))
|
||||
b, err := v.(Marshaler).MarshalJSON()
|
||||
if err != nil {
|
||||
return &MarshalerError{
|
||||
Type: rtype2type(code.typ),
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
if len(b) == 0 {
|
||||
return errUnexpectedEndOfJSON(
|
||||
fmt.Sprintf("error calling MarshalJSON for type %s", code.typ),
|
||||
0,
|
||||
)
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
if err := compact(&buf, b, true); err != nil {
|
||||
return err
|
||||
}
|
||||
e.encodeBytes(buf.Bytes())
|
||||
field.nextField.ptr = ptr
|
||||
code = field.next
|
||||
}
|
||||
case opStructFieldPtrAnonymousHeadMarshalJSON:
|
||||
code.ptr = e.ptrToPtr(code.ptr)
|
||||
fallthrough
|
||||
case opStructFieldAnonymousHeadMarshalJSON:
|
||||
field := code.toStructFieldCode()
|
||||
ptr := field.ptr
|
||||
if ptr == 0 {
|
||||
code = field.end
|
||||
} else {
|
||||
e.encodeBytes(field.key)
|
||||
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{
|
||||
typ: code.typ,
|
||||
ptr: unsafe.Pointer(ptr),
|
||||
}))
|
||||
b, err := v.(Marshaler).MarshalJSON()
|
||||
if err != nil {
|
||||
return &MarshalerError{
|
||||
Type: rtype2type(code.typ),
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
if len(b) == 0 {
|
||||
return errUnexpectedEndOfJSON(
|
||||
fmt.Sprintf("error calling MarshalJSON for type %s", code.typ),
|
||||
0,
|
||||
)
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
if err := compact(&buf, b, true); err != nil {
|
||||
return err
|
||||
}
|
||||
e.encodeBytes(buf.Bytes())
|
||||
field.nextField.ptr = ptr
|
||||
code = field.next
|
||||
}
|
||||
case opStructFieldPtrHeadMarshalText:
|
||||
code.ptr = e.ptrToPtr(code.ptr)
|
||||
fallthrough
|
||||
case opStructFieldHeadMarshalText:
|
||||
field := code.toStructFieldCode()
|
||||
ptr := field.ptr
|
||||
if ptr == 0 {
|
||||
e.encodeNull()
|
||||
code = field.end
|
||||
} else {
|
||||
e.encodeByte('{')
|
||||
e.encodeBytes(field.key)
|
||||
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{
|
||||
typ: code.typ,
|
||||
ptr: unsafe.Pointer(ptr),
|
||||
}))
|
||||
bytes, err := v.(encoding.TextMarshaler).MarshalText()
|
||||
if err != nil {
|
||||
return &MarshalerError{
|
||||
Type: rtype2type(code.typ),
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
e.encodeString(*(*string)(unsafe.Pointer(&bytes)))
|
||||
field.nextField.ptr = ptr
|
||||
code = field.next
|
||||
}
|
||||
case opStructFieldPtrAnonymousHeadMarshalText:
|
||||
code.ptr = e.ptrToPtr(code.ptr)
|
||||
fallthrough
|
||||
case opStructFieldAnonymousHeadMarshalText:
|
||||
field := code.toStructFieldCode()
|
||||
ptr := field.ptr
|
||||
if ptr == 0 {
|
||||
code = field.end
|
||||
} else {
|
||||
e.encodeBytes(field.key)
|
||||
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{
|
||||
typ: code.typ,
|
||||
ptr: unsafe.Pointer(ptr),
|
||||
}))
|
||||
bytes, err := v.(encoding.TextMarshaler).MarshalText()
|
||||
if err != nil {
|
||||
return &MarshalerError{
|
||||
Type: rtype2type(code.typ),
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
e.encodeString(*(*string)(unsafe.Pointer(&bytes)))
|
||||
field.nextField.ptr = ptr
|
||||
code = field.next
|
||||
}
|
||||
case opStructFieldPtrHeadIndent:
|
||||
if code.ptr != 0 {
|
||||
code.ptr = e.ptrToPtr(code.ptr)
|
||||
|
@ -1912,6 +2044,159 @@ func (e *Encoder) run(code *opcode) error {
|
|||
}
|
||||
field.nextField.ptr = ptr
|
||||
}
|
||||
|
||||
case opStructFieldPtrHeadOmitEmptyMarshalJSON:
|
||||
if code.ptr != 0 {
|
||||
code.ptr = e.ptrToPtr(code.ptr)
|
||||
}
|
||||
fallthrough
|
||||
case opStructFieldHeadOmitEmptyMarshalJSON:
|
||||
field := code.toStructFieldCode()
|
||||
ptr := field.ptr
|
||||
if ptr == 0 {
|
||||
e.encodeNull()
|
||||
code = field.end.next
|
||||
} else {
|
||||
e.encodeByte('{')
|
||||
p := unsafe.Pointer(ptr + field.offset)
|
||||
isPtr := code.typ.Kind() == reflect.Ptr
|
||||
if p == nil || (!isPtr && *(*unsafe.Pointer)(p) == nil) {
|
||||
code = field.nextField
|
||||
} else {
|
||||
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{typ: code.typ, ptr: p}))
|
||||
b, err := v.(Marshaler).MarshalJSON()
|
||||
if err != nil {
|
||||
return &MarshalerError{
|
||||
Type: rtype2type(code.typ),
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
if len(b) == 0 {
|
||||
if isPtr {
|
||||
return errUnexpectedEndOfJSON(
|
||||
fmt.Sprintf("error calling MarshalJSON for type %s", code.typ),
|
||||
0,
|
||||
)
|
||||
}
|
||||
code = field.nextField
|
||||
} else {
|
||||
var buf bytes.Buffer
|
||||
if err := compact(&buf, b, true); err != nil {
|
||||
return err
|
||||
}
|
||||
e.encodeBytes(field.key)
|
||||
e.encodeBytes(buf.Bytes())
|
||||
code = field.next
|
||||
}
|
||||
}
|
||||
field.nextField.ptr = ptr
|
||||
}
|
||||
case opStructFieldPtrAnonymousHeadOmitEmptyMarshalJSON:
|
||||
if code.ptr != 0 {
|
||||
code.ptr = e.ptrToPtr(code.ptr)
|
||||
}
|
||||
fallthrough
|
||||
case opStructFieldAnonymousHeadOmitEmptyMarshalJSON:
|
||||
field := code.toStructFieldCode()
|
||||
ptr := field.ptr
|
||||
if ptr == 0 {
|
||||
code = field.end.next
|
||||
} else {
|
||||
p := unsafe.Pointer(ptr + field.offset)
|
||||
isPtr := code.typ.Kind() == reflect.Ptr
|
||||
if p == nil || (!isPtr && *(*unsafe.Pointer)(p) == nil) {
|
||||
code = field.nextField
|
||||
} else {
|
||||
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{typ: code.typ, ptr: p}))
|
||||
b, err := v.(Marshaler).MarshalJSON()
|
||||
if err != nil {
|
||||
return &MarshalerError{
|
||||
Type: rtype2type(code.typ),
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
if len(b) == 0 {
|
||||
if isPtr {
|
||||
return errUnexpectedEndOfJSON(
|
||||
fmt.Sprintf("error calling MarshalJSON for type %s", code.typ),
|
||||
0,
|
||||
)
|
||||
}
|
||||
code = field.nextField
|
||||
} else {
|
||||
var buf bytes.Buffer
|
||||
if err := compact(&buf, b, true); err != nil {
|
||||
return err
|
||||
}
|
||||
e.encodeBytes(field.key)
|
||||
e.encodeBytes(buf.Bytes())
|
||||
code = field.next
|
||||
}
|
||||
}
|
||||
field.nextField.ptr = ptr
|
||||
}
|
||||
case opStructFieldPtrHeadOmitEmptyMarshalText:
|
||||
if code.ptr != 0 {
|
||||
code.ptr = e.ptrToPtr(code.ptr)
|
||||
}
|
||||
fallthrough
|
||||
case opStructFieldHeadOmitEmptyMarshalText:
|
||||
field := code.toStructFieldCode()
|
||||
ptr := field.ptr
|
||||
if ptr == 0 {
|
||||
e.encodeNull()
|
||||
code = field.end.next
|
||||
} else {
|
||||
e.encodeByte('{')
|
||||
p := unsafe.Pointer(ptr + field.offset)
|
||||
isPtr := code.typ.Kind() == reflect.Ptr
|
||||
if p == nil || (!isPtr && *(*unsafe.Pointer)(p) == nil) {
|
||||
code = field.nextField
|
||||
} else {
|
||||
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{typ: code.typ, ptr: p}))
|
||||
bytes, err := v.(encoding.TextMarshaler).MarshalText()
|
||||
if err != nil {
|
||||
return &MarshalerError{
|
||||
Type: rtype2type(code.typ),
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
e.encodeBytes(field.key)
|
||||
e.encodeString(*(*string)(unsafe.Pointer(&bytes)))
|
||||
code = field.next
|
||||
}
|
||||
field.nextField.ptr = ptr
|
||||
}
|
||||
case opStructFieldPtrAnonymousHeadOmitEmptyMarshalText:
|
||||
if code.ptr != 0 {
|
||||
code.ptr = e.ptrToPtr(code.ptr)
|
||||
}
|
||||
fallthrough
|
||||
case opStructFieldAnonymousHeadOmitEmptyMarshalText:
|
||||
field := code.toStructFieldCode()
|
||||
ptr := field.ptr
|
||||
if ptr == 0 {
|
||||
code = field.end.next
|
||||
} else {
|
||||
p := unsafe.Pointer(ptr + field.offset)
|
||||
isPtr := code.typ.Kind() == reflect.Ptr
|
||||
if p == nil || (!isPtr && *(*unsafe.Pointer)(p) == nil) {
|
||||
code = field.nextField
|
||||
} else {
|
||||
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{typ: code.typ, ptr: p}))
|
||||
bytes, err := v.(encoding.TextMarshaler).MarshalText()
|
||||
if err != nil {
|
||||
return &MarshalerError{
|
||||
Type: rtype2type(code.typ),
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
e.encodeBytes(field.key)
|
||||
e.encodeString(*(*string)(unsafe.Pointer(&bytes)))
|
||||
code = field.next
|
||||
}
|
||||
field.nextField.ptr = ptr
|
||||
}
|
||||
case opStructFieldPtrHeadOmitEmptyIndent:
|
||||
if code.ptr != 0 {
|
||||
code.ptr = e.ptrToPtr(code.ptr)
|
||||
|
@ -2465,7 +2750,52 @@ func (e *Encoder) run(code *opcode) error {
|
|||
e.encodeBytes(c.key)
|
||||
e.encodeBool(e.ptrToBool(c.ptr + c.offset))
|
||||
code = code.next
|
||||
|
||||
case opStructFieldMarshalJSON:
|
||||
if e.buf[len(e.buf)-1] != '{' {
|
||||
e.encodeByte(',')
|
||||
}
|
||||
c := code.toStructFieldCode()
|
||||
c.nextField.ptr = c.ptr
|
||||
e.encodeBytes(c.key)
|
||||
ptr := c.ptr + c.offset
|
||||
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{
|
||||
typ: code.typ,
|
||||
ptr: unsafe.Pointer(ptr),
|
||||
}))
|
||||
b, err := v.(Marshaler).MarshalJSON()
|
||||
if err != nil {
|
||||
return &MarshalerError{
|
||||
Type: rtype2type(code.typ),
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
if err := compact(&buf, b, true); err != nil {
|
||||
return err
|
||||
}
|
||||
e.encodeBytes(buf.Bytes())
|
||||
code = code.next
|
||||
case opStructFieldMarshalText:
|
||||
if e.buf[len(e.buf)-1] != '{' {
|
||||
e.encodeByte(',')
|
||||
}
|
||||
c := code.toStructFieldCode()
|
||||
c.nextField.ptr = c.ptr
|
||||
e.encodeBytes(c.key)
|
||||
ptr := c.ptr + c.offset
|
||||
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{
|
||||
typ: code.typ,
|
||||
ptr: unsafe.Pointer(ptr),
|
||||
}))
|
||||
bytes, err := v.(encoding.TextMarshaler).MarshalText()
|
||||
if err != nil {
|
||||
return &MarshalerError{
|
||||
Type: rtype2type(code.typ),
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
e.encodeString(*(*string)(unsafe.Pointer(&bytes)))
|
||||
code = code.next
|
||||
case opStructFieldIndent:
|
||||
c := code.toStructFieldCode()
|
||||
if e.buf[len(e.buf)-2] != '{' {
|
||||
|
@ -2827,6 +3157,52 @@ func (e *Encoder) run(code *opcode) error {
|
|||
code = code.next
|
||||
code.ptr = c.ptr
|
||||
|
||||
case opStructFieldOmitEmptyMarshalJSON:
|
||||
c := code.toStructFieldCode()
|
||||
ptr := c.ptr + c.offset
|
||||
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{
|
||||
typ: code.typ,
|
||||
ptr: unsafe.Pointer(ptr),
|
||||
}))
|
||||
if v != nil {
|
||||
b, err := v.(Marshaler).MarshalJSON()
|
||||
if err != nil {
|
||||
return &MarshalerError{
|
||||
Type: rtype2type(code.typ),
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
if err := compact(&buf, b, true); err != nil {
|
||||
return err
|
||||
}
|
||||
e.encodeBytes(buf.Bytes())
|
||||
}
|
||||
code = code.next
|
||||
code.ptr = c.ptr
|
||||
case opStructFieldOmitEmptyMarshalText:
|
||||
c := code.toStructFieldCode()
|
||||
ptr := c.ptr + c.offset
|
||||
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{
|
||||
typ: code.typ,
|
||||
ptr: unsafe.Pointer(ptr),
|
||||
}))
|
||||
if v != nil {
|
||||
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{
|
||||
typ: code.typ,
|
||||
ptr: unsafe.Pointer(ptr),
|
||||
}))
|
||||
bytes, err := v.(encoding.TextMarshaler).MarshalText()
|
||||
if err != nil {
|
||||
return &MarshalerError{
|
||||
Type: rtype2type(code.typ),
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
e.encodeString(*(*string)(unsafe.Pointer(&bytes)))
|
||||
}
|
||||
code = code.next
|
||||
code.ptr = c.ptr
|
||||
case opStructFieldOmitEmptyIndent:
|
||||
c := code.toStructFieldCode()
|
||||
p := c.ptr + c.offset
|
||||
|
|
2
error.go
2
error.go
|
@ -123,7 +123,7 @@ func errNotAtBeginningOfValue(cursor int64) *SyntaxError {
|
|||
|
||||
func errUnexpectedEndOfJSON(msg string, cursor int64) *SyntaxError {
|
||||
return &SyntaxError{
|
||||
msg: fmt.Sprintf("unexpected end of JSON input for %s", msg),
|
||||
msg: fmt.Sprintf("json: %s unexpected end of JSON input", msg),
|
||||
Offset: cursor,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,18 @@
|
|||
package json
|
||||
|
||||
import "reflect"
|
||||
|
||||
func NewSyntaxError(msg string, offset int64) *SyntaxError {
|
||||
return &SyntaxError{
|
||||
msg: msg,
|
||||
Offset: offset,
|
||||
}
|
||||
}
|
||||
|
||||
func NewMarshalerError(typ reflect.Type, err error, msg string) *MarshalerError {
|
||||
return &MarshalerError{
|
||||
Type: typ,
|
||||
Err: err,
|
||||
sourceFunc: msg,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue