mirror of https://github.com/goccy/go-json.git
Merge pull request #32 from goccy/feature/support-string-tag
Support string tag
This commit is contained in:
commit
f30c85fcfd
|
@ -25,11 +25,16 @@ type headType struct {
|
||||||
OmitEmptyPtrHead string
|
OmitEmptyPtrHead string
|
||||||
AnonymousOmitEmptyHead string
|
AnonymousOmitEmptyHead string
|
||||||
AnonymousOmitEmptyPtrHead string
|
AnonymousOmitEmptyPtrHead string
|
||||||
|
StringTagHead string
|
||||||
|
StringTagPtrHead string
|
||||||
|
AnonymousStringTagHead string
|
||||||
|
AnonymousStringTagPtrHead string
|
||||||
}
|
}
|
||||||
|
|
||||||
type fieldType struct {
|
type fieldType struct {
|
||||||
Field string
|
Field string
|
||||||
OmitEmptyField string
|
OmitEmptyField string
|
||||||
|
StringTagField string
|
||||||
}
|
}
|
||||||
|
|
||||||
func _main() error {
|
func _main() error {
|
||||||
|
@ -93,6 +98,10 @@ func (t opType) headToPtrHead() opType {
|
||||||
return op{{ $type.OmitEmptyPtrHead }}
|
return op{{ $type.OmitEmptyPtrHead }}
|
||||||
case op{{ $type.AnonymousOmitEmptyHead }}:
|
case op{{ $type.AnonymousOmitEmptyHead }}:
|
||||||
return op{{ $type.AnonymousOmitEmptyPtrHead }}
|
return op{{ $type.AnonymousOmitEmptyPtrHead }}
|
||||||
|
case op{{ $type.StringTagHead }}:
|
||||||
|
return op{{ $type.StringTagPtrHead }}
|
||||||
|
case op{{ $type.AnonymousStringTagHead }}:
|
||||||
|
return op{{ $type.AnonymousStringTagPtrHead }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
}
|
}
|
||||||
return t
|
return t
|
||||||
|
@ -109,6 +118,10 @@ func (t opType) headToAnonymousHead() opType {
|
||||||
return op{{ $type.AnonymousOmitEmptyHead }}
|
return op{{ $type.AnonymousOmitEmptyHead }}
|
||||||
case op{{ $type.OmitEmptyPtrHead }}:
|
case op{{ $type.OmitEmptyPtrHead }}:
|
||||||
return op{{ $type.AnonymousOmitEmptyPtrHead }}
|
return op{{ $type.AnonymousOmitEmptyPtrHead }}
|
||||||
|
case op{{ $type.StringTagHead }}:
|
||||||
|
return op{{ $type.AnonymousStringTagHead }}
|
||||||
|
case op{{ $type.StringTagPtrHead }}:
|
||||||
|
return op{{ $type.AnonymousStringTagPtrHead }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
}
|
}
|
||||||
return t
|
return t
|
||||||
|
@ -126,6 +139,18 @@ func (t opType) headToOmitEmptyHead() opType {
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t opType) headToStringTagHead() opType {
|
||||||
|
switch t {
|
||||||
|
{{- range $type := .HeadTypes }}
|
||||||
|
case op{{ $type.Head }}:
|
||||||
|
return op{{ $type.StringTagHead }}
|
||||||
|
case op{{ $type.PtrHead }}:
|
||||||
|
return op{{ $type.StringTagPtrHead }}
|
||||||
|
{{- end }}
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
func (t opType) ptrHeadToHead() opType {
|
func (t opType) ptrHeadToHead() opType {
|
||||||
switch t {
|
switch t {
|
||||||
{{- range $type := .HeadTypes }}
|
{{- range $type := .HeadTypes }}
|
||||||
|
@ -137,6 +162,10 @@ func (t opType) ptrHeadToHead() opType {
|
||||||
return op{{ $type.OmitEmptyHead }}
|
return op{{ $type.OmitEmptyHead }}
|
||||||
case op{{ $type.AnonymousOmitEmptyPtrHead }}:
|
case op{{ $type.AnonymousOmitEmptyPtrHead }}:
|
||||||
return op{{ $type.AnonymousOmitEmptyHead }}
|
return op{{ $type.AnonymousOmitEmptyHead }}
|
||||||
|
case op{{ $type.StringTagPtrHead }}:
|
||||||
|
return op{{ $type.StringTagHead }}
|
||||||
|
case op{{ $type.AnonymousStringTagPtrHead }}:
|
||||||
|
return op{{ $type.AnonymousStringTagHead }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
}
|
}
|
||||||
return t
|
return t
|
||||||
|
@ -152,6 +181,16 @@ func (t opType) fieldToOmitEmptyField() opType {
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t opType) fieldToStringTagField() opType {
|
||||||
|
switch t {
|
||||||
|
{{- range $type := .FieldTypes }}
|
||||||
|
case op{{ $type.Field }}:
|
||||||
|
return op{{ $type.StringTagField }}
|
||||||
|
{{- end }}
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
`)
|
`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -199,14 +238,19 @@ func (t opType) fieldToOmitEmptyField() opType {
|
||||||
{"MapEnd", "MapEndIndent", "Op"},
|
{"MapEnd", "MapEndIndent", "Op"},
|
||||||
{"StructFieldHead", "StructFieldHeadIndent", "StructField"},
|
{"StructFieldHead", "StructFieldHeadIndent", "StructField"},
|
||||||
{"StructFieldHeadOmitEmpty", "StructFieldHeadOmitEmptyIndent", "StructField"},
|
{"StructFieldHeadOmitEmpty", "StructFieldHeadOmitEmptyIndent", "StructField"},
|
||||||
|
{"StructFieldHeadStringTag", "StructFieldHeadStringTagIndent", "StructField"},
|
||||||
{"StructFieldAnonymousHead", "StructFieldAnonymousHeadIndent", "StructField"},
|
{"StructFieldAnonymousHead", "StructFieldAnonymousHeadIndent", "StructField"},
|
||||||
{"StructFieldAnonymousHeadOmitEmpty", "StructFieldAnonymousHeadOmitEmptyIndent", "StructField"},
|
{"StructFieldAnonymousHeadOmitEmpty", "StructFieldAnonymousHeadOmitEmptyIndent", "StructField"},
|
||||||
{"StructFieldPtrAnonymousHeadOmitEmpty", "StructFieldPtrAnonymousHeadOmitEmptyIndent", "StructField"},
|
{"StructFieldPtrAnonymousHeadOmitEmpty", "StructFieldPtrAnonymousHeadOmitEmptyIndent", "StructField"},
|
||||||
|
{"StructFieldAnonymousHeadStringTag", "StructFieldAnonymousHeadStringTagIndent", "StructField"},
|
||||||
|
{"StructFieldPtrAnonymousHeadStringTag", "StructFieldPtrAnonymousHeadStringTagIndent", "StructField"},
|
||||||
{"StructFieldPtrHead", "StructFieldPtrHeadIndent", "StructField"},
|
{"StructFieldPtrHead", "StructFieldPtrHeadIndent", "StructField"},
|
||||||
{"StructFieldPtrHeadOmitEmpty", "StructFieldPtrHeadOmitEmptyIndent", "StructField"},
|
{"StructFieldPtrHeadOmitEmpty", "StructFieldPtrHeadOmitEmptyIndent", "StructField"},
|
||||||
|
{"StructFieldPtrHeadStringTag", "StructFieldPtrHeadStringTagIndent", "StructField"},
|
||||||
{"StructFieldPtrAnonymousHead", "StructFieldPtrAnonymousHeadIndent", "StructField"},
|
{"StructFieldPtrAnonymousHead", "StructFieldPtrAnonymousHeadIndent", "StructField"},
|
||||||
{"StructField", "StructFieldIndent", "StructField"},
|
{"StructField", "StructFieldIndent", "StructField"},
|
||||||
{"StructFieldOmitEmpty", "StructFieldOmitEmptyIndent", "StructField"},
|
{"StructFieldOmitEmpty", "StructFieldOmitEmptyIndent", "StructField"},
|
||||||
|
{"StructFieldStringTag", "StructFieldStringTagIndent", "StructField"},
|
||||||
{"StructFieldRecursive", "StructFieldRecursiveIndent", "StructFieldRecursive"},
|
{"StructFieldRecursive", "StructFieldRecursiveIndent", "StructFieldRecursive"},
|
||||||
{"StructEnd", "StructEndIndent", "StructField"},
|
{"StructEnd", "StructEndIndent", "StructField"},
|
||||||
{"StructAnonymousEnd", "StructAnonymousEndIndent", "StructField"},
|
{"StructAnonymousEnd", "StructAnonymousEndIndent", "StructField"},
|
||||||
|
@ -221,14 +265,19 @@ func (t opType) fieldToOmitEmptyField() opType {
|
||||||
for _, prefix := range []string{
|
for _, prefix := range []string{
|
||||||
"StructFieldHead",
|
"StructFieldHead",
|
||||||
"StructFieldHeadOmitEmpty",
|
"StructFieldHeadOmitEmpty",
|
||||||
|
"StructFieldHeadStringTag",
|
||||||
"StructFieldAnonymousHead",
|
"StructFieldAnonymousHead",
|
||||||
"StructFieldAnonymousHeadOmitEmpty",
|
"StructFieldAnonymousHeadOmitEmpty",
|
||||||
|
"StructFieldAnonymousHeadStringTag",
|
||||||
"StructFieldPtrHead",
|
"StructFieldPtrHead",
|
||||||
"StructFieldPtrHeadOmitEmpty",
|
"StructFieldPtrHeadOmitEmpty",
|
||||||
|
"StructFieldPtrHeadStringTag",
|
||||||
"StructFieldPtrAnonymousHead",
|
"StructFieldPtrAnonymousHead",
|
||||||
"StructFieldPtrAnonymousHeadOmitEmpty",
|
"StructFieldPtrAnonymousHeadOmitEmpty",
|
||||||
|
"StructFieldPtrAnonymousHeadStringTag",
|
||||||
"StructField",
|
"StructField",
|
||||||
"StructFieldOmitEmpty",
|
"StructFieldOmitEmpty",
|
||||||
|
"StructFieldStringTag",
|
||||||
} {
|
} {
|
||||||
for _, typ := range primitiveTypesUpper {
|
for _, typ := range primitiveTypesUpper {
|
||||||
opTypes = append(opTypes, opType{
|
opTypes = append(opTypes, opType{
|
||||||
|
@ -252,8 +301,12 @@ func (t opType) fieldToOmitEmptyField() opType {
|
||||||
AnonymousPtrHead: "StructFieldPtrAnonymousHead",
|
AnonymousPtrHead: "StructFieldPtrAnonymousHead",
|
||||||
OmitEmptyHead: "StructFieldHeadOmitEmpty",
|
OmitEmptyHead: "StructFieldHeadOmitEmpty",
|
||||||
OmitEmptyPtrHead: "StructFieldPtrHeadOmitEmpty",
|
OmitEmptyPtrHead: "StructFieldPtrHeadOmitEmpty",
|
||||||
|
StringTagHead: "StructFieldHeadStringTag",
|
||||||
|
StringTagPtrHead: "StructFieldPtrHeadStringTag",
|
||||||
AnonymousOmitEmptyHead: "StructFieldAnonymousHeadOmitEmpty",
|
AnonymousOmitEmptyHead: "StructFieldAnonymousHeadOmitEmpty",
|
||||||
AnonymousOmitEmptyPtrHead: "StructFieldPtrAnonymousHeadOmitEmpty",
|
AnonymousOmitEmptyPtrHead: "StructFieldPtrAnonymousHeadOmitEmpty",
|
||||||
|
AnonymousStringTagHead: "StructFieldAnonymousHeadStringTag",
|
||||||
|
AnonymousStringTagPtrHead: "StructFieldPtrAnonymousHeadStringTag",
|
||||||
}
|
}
|
||||||
headTypes := []headType{base}
|
headTypes := []headType{base}
|
||||||
for _, prim := range primitiveTypesUpper {
|
for _, prim := range primitiveTypesUpper {
|
||||||
|
@ -266,6 +319,10 @@ func (t opType) fieldToOmitEmptyField() opType {
|
||||||
OmitEmptyPtrHead: fmt.Sprintf("%s%s", base.OmitEmptyPtrHead, prim),
|
OmitEmptyPtrHead: fmt.Sprintf("%s%s", base.OmitEmptyPtrHead, prim),
|
||||||
AnonymousOmitEmptyHead: fmt.Sprintf("%s%s", base.AnonymousOmitEmptyHead, prim),
|
AnonymousOmitEmptyHead: fmt.Sprintf("%s%s", base.AnonymousOmitEmptyHead, prim),
|
||||||
AnonymousOmitEmptyPtrHead: fmt.Sprintf("%s%s", base.AnonymousOmitEmptyPtrHead, prim),
|
AnonymousOmitEmptyPtrHead: fmt.Sprintf("%s%s", base.AnonymousOmitEmptyPtrHead, prim),
|
||||||
|
StringTagHead: fmt.Sprintf("%s%s", base.StringTagHead, prim),
|
||||||
|
StringTagPtrHead: fmt.Sprintf("%s%s", base.StringTagPtrHead, prim),
|
||||||
|
AnonymousStringTagHead: fmt.Sprintf("%s%s", base.AnonymousStringTagHead, prim),
|
||||||
|
AnonymousStringTagPtrHead: fmt.Sprintf("%s%s", base.AnonymousStringTagPtrHead, prim),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
for _, typ := range headTypes {
|
for _, typ := range headTypes {
|
||||||
|
@ -278,24 +335,31 @@ func (t opType) fieldToOmitEmptyField() opType {
|
||||||
OmitEmptyPtrHead: fmt.Sprintf("%sIndent", typ.OmitEmptyPtrHead),
|
OmitEmptyPtrHead: fmt.Sprintf("%sIndent", typ.OmitEmptyPtrHead),
|
||||||
AnonymousOmitEmptyHead: fmt.Sprintf("%sIndent", typ.AnonymousOmitEmptyHead),
|
AnonymousOmitEmptyHead: fmt.Sprintf("%sIndent", typ.AnonymousOmitEmptyHead),
|
||||||
AnonymousOmitEmptyPtrHead: fmt.Sprintf("%sIndent", typ.AnonymousOmitEmptyPtrHead),
|
AnonymousOmitEmptyPtrHead: fmt.Sprintf("%sIndent", typ.AnonymousOmitEmptyPtrHead),
|
||||||
|
StringTagHead: fmt.Sprintf("%sIndent", typ.StringTagHead),
|
||||||
|
StringTagPtrHead: fmt.Sprintf("%sIndent", typ.StringTagPtrHead),
|
||||||
|
AnonymousStringTagHead: fmt.Sprintf("%sIndent", typ.AnonymousStringTagHead),
|
||||||
|
AnonymousStringTagPtrHead: fmt.Sprintf("%sIndent", typ.AnonymousStringTagPtrHead),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
baseField := fieldType{
|
baseField := fieldType{
|
||||||
Field: "StructField",
|
Field: "StructField",
|
||||||
OmitEmptyField: "StructFieldOmitEmpty",
|
OmitEmptyField: "StructFieldOmitEmpty",
|
||||||
|
StringTagField: "StructFieldStringTag",
|
||||||
}
|
}
|
||||||
fieldTypes := []fieldType{baseField}
|
fieldTypes := []fieldType{baseField}
|
||||||
for _, prim := range primitiveTypesUpper {
|
for _, prim := range primitiveTypesUpper {
|
||||||
fieldTypes = append(fieldTypes, fieldType{
|
fieldTypes = append(fieldTypes, fieldType{
|
||||||
Field: fmt.Sprintf("%s%s", baseField.Field, prim),
|
Field: fmt.Sprintf("%s%s", baseField.Field, prim),
|
||||||
OmitEmptyField: fmt.Sprintf("%s%s", baseField.OmitEmptyField, prim),
|
OmitEmptyField: fmt.Sprintf("%s%s", baseField.OmitEmptyField, prim),
|
||||||
|
StringTagField: fmt.Sprintf("%s%s", baseField.StringTagField, prim),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
for _, typ := range fieldTypes {
|
for _, typ := range fieldTypes {
|
||||||
fieldTypes = append(fieldTypes, fieldType{
|
fieldTypes = append(fieldTypes, fieldType{
|
||||||
Field: fmt.Sprintf("%sIndent", typ.Field),
|
Field: fmt.Sprintf("%sIndent", typ.Field),
|
||||||
OmitEmptyField: fmt.Sprintf("%sIndent", typ.OmitEmptyField),
|
OmitEmptyField: fmt.Sprintf("%sIndent", typ.OmitEmptyField),
|
||||||
|
StringTagField: fmt.Sprintf("%sIndent", typ.StringTagField),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,20 +7,31 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (d *Decoder) compileHead(typ *rtype) (decoder, error) {
|
func (d *Decoder) compileHead(typ *rtype) (decoder, error) {
|
||||||
if typ.Implements(unmarshalJSONType) {
|
switch {
|
||||||
|
case typ.Implements(unmarshalJSONType):
|
||||||
return newUnmarshalJSONDecoder(typ), nil
|
return newUnmarshalJSONDecoder(typ), nil
|
||||||
} else if typ.Implements(unmarshalTextType) {
|
case rtype_ptrTo(typ).Implements(marshalJSONType):
|
||||||
|
return newUnmarshalJSONDecoder(rtype_ptrTo(typ)), nil
|
||||||
|
case typ.Implements(unmarshalTextType):
|
||||||
return newUnmarshalTextDecoder(typ), nil
|
return newUnmarshalTextDecoder(typ), nil
|
||||||
|
case rtype_ptrTo(typ).Implements(unmarshalTextType):
|
||||||
|
return newUnmarshalTextDecoder(rtype_ptrTo(typ)), nil
|
||||||
}
|
}
|
||||||
return d.compile(typ.Elem())
|
return d.compile(typ.Elem())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Decoder) compile(typ *rtype) (decoder, error) {
|
func (d *Decoder) compile(typ *rtype) (decoder, error) {
|
||||||
if typ.Implements(unmarshalJSONType) {
|
switch {
|
||||||
|
case typ.Implements(unmarshalJSONType):
|
||||||
return newUnmarshalJSONDecoder(typ), nil
|
return newUnmarshalJSONDecoder(typ), nil
|
||||||
} else if typ.Implements(unmarshalTextType) {
|
case rtype_ptrTo(typ).Implements(marshalJSONType):
|
||||||
|
return newUnmarshalJSONDecoder(rtype_ptrTo(typ)), nil
|
||||||
|
case typ.Implements(unmarshalTextType):
|
||||||
return newUnmarshalTextDecoder(typ), nil
|
return newUnmarshalTextDecoder(typ), nil
|
||||||
|
case rtype_ptrTo(typ).Implements(unmarshalTextType):
|
||||||
|
return newUnmarshalTextDecoder(rtype_ptrTo(typ)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch typ.Kind() {
|
switch typ.Kind() {
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
return d.compilePtr(typ)
|
return d.compilePtr(typ)
|
||||||
|
@ -34,6 +45,8 @@ func (d *Decoder) compile(typ *rtype) (decoder, error) {
|
||||||
return d.compileMap(typ)
|
return d.compileMap(typ)
|
||||||
case reflect.Interface:
|
case reflect.Interface:
|
||||||
return d.compileInterface(typ)
|
return d.compileInterface(typ)
|
||||||
|
case reflect.Uintptr:
|
||||||
|
return d.compileUint()
|
||||||
case reflect.Int:
|
case reflect.Int:
|
||||||
return d.compileInt()
|
return d.compileInt()
|
||||||
case reflect.Int8:
|
case reflect.Int8:
|
||||||
|
@ -188,46 +201,26 @@ func (d *Decoder) compileInterface(typ *rtype) (decoder, error) {
|
||||||
return newInterfaceDecoder(typ), nil
|
return newInterfaceDecoder(typ), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Decoder) getTag(field reflect.StructField) string {
|
|
||||||
return field.Tag.Get("json")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Decoder) isIgnoredStructField(field reflect.StructField) bool {
|
|
||||||
if field.PkgPath != "" && !field.Anonymous {
|
|
||||||
// private field
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
tag := d.getTag(field)
|
|
||||||
if tag == "-" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Decoder) compileStruct(typ *rtype) (decoder, error) {
|
func (d *Decoder) compileStruct(typ *rtype) (decoder, error) {
|
||||||
fieldNum := typ.NumField()
|
fieldNum := typ.NumField()
|
||||||
fieldMap := map[string]*structFieldSet{}
|
fieldMap := map[string]*structFieldSet{}
|
||||||
for i := 0; i < fieldNum; i++ {
|
for i := 0; i < fieldNum; i++ {
|
||||||
field := typ.Field(i)
|
field := typ.Field(i)
|
||||||
if d.isIgnoredStructField(field) {
|
if isIgnoredStructField(field) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
keyName := field.Name
|
tag := structTagFromField(field)
|
||||||
tag := d.getTag(field)
|
|
||||||
opts := strings.Split(tag, ",")
|
|
||||||
if len(opts) > 0 {
|
|
||||||
if opts[0] != "" {
|
|
||||||
keyName = opts[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dec, err := d.compile(type2rtype(field.Type))
|
dec, err := d.compile(type2rtype(field.Type))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if tag.isString {
|
||||||
|
dec = newWrappedStringDecoder(dec)
|
||||||
|
}
|
||||||
fieldSet := &structFieldSet{dec: dec, offset: field.Offset}
|
fieldSet := &structFieldSet{dec: dec, offset: field.Offset}
|
||||||
fieldMap[field.Name] = fieldSet
|
fieldMap[field.Name] = fieldSet
|
||||||
fieldMap[keyName] = fieldSet
|
fieldMap[tag.key] = fieldSet
|
||||||
fieldMap[strings.ToLower(keyName)] = fieldSet
|
fieldMap[strings.ToLower(tag.key)] = fieldSet
|
||||||
}
|
}
|
||||||
return newStructDecoder(fieldMap), nil
|
return newStructDecoder(fieldMap), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,85 +33,6 @@ var (
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
hexToInt = [256]int{
|
|
||||||
'0': 0,
|
|
||||||
'1': 1,
|
|
||||||
'2': 2,
|
|
||||||
'3': 3,
|
|
||||||
'4': 4,
|
|
||||||
'5': 5,
|
|
||||||
'6': 6,
|
|
||||||
'7': 7,
|
|
||||||
'8': 8,
|
|
||||||
'9': 9,
|
|
||||||
'A': 10,
|
|
||||||
'B': 11,
|
|
||||||
'C': 12,
|
|
||||||
'D': 13,
|
|
||||||
'E': 14,
|
|
||||||
'F': 15,
|
|
||||||
'a': 10,
|
|
||||||
'b': 11,
|
|
||||||
'c': 12,
|
|
||||||
'd': 13,
|
|
||||||
'e': 14,
|
|
||||||
'f': 15,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func unicodeToRune(code []byte) rune {
|
|
||||||
sum := 0
|
|
||||||
for i := 0; i < len(code); i++ {
|
|
||||||
sum += hexToInt[code[i]] << (uint(len(code)-i-1) * 4)
|
|
||||||
}
|
|
||||||
return rune(sum)
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeEscapeString(s *stream) error {
|
|
||||||
s.cursor++
|
|
||||||
RETRY:
|
|
||||||
switch s.buf[s.cursor] {
|
|
||||||
case '"':
|
|
||||||
s.buf[s.cursor] = '"'
|
|
||||||
case '\\':
|
|
||||||
s.buf[s.cursor] = '\\'
|
|
||||||
case '/':
|
|
||||||
s.buf[s.cursor] = '/'
|
|
||||||
case 'b':
|
|
||||||
s.buf[s.cursor] = '\b'
|
|
||||||
case 'f':
|
|
||||||
s.buf[s.cursor] = '\f'
|
|
||||||
case 'n':
|
|
||||||
s.buf[s.cursor] = '\n'
|
|
||||||
case 'r':
|
|
||||||
s.buf[s.cursor] = '\r'
|
|
||||||
case 't':
|
|
||||||
s.buf[s.cursor] = '\t'
|
|
||||||
case 'u':
|
|
||||||
if s.cursor+5 >= s.length {
|
|
||||||
if !s.read() {
|
|
||||||
return errInvalidCharacter(s.char(), "escaped string", s.totalOffset())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
code := unicodeToRune(s.buf[s.cursor+1 : s.cursor+5])
|
|
||||||
unicode := []byte(string(code))
|
|
||||||
s.buf = append(append(s.buf[:s.cursor-1], unicode...), s.buf[s.cursor+5:]...)
|
|
||||||
s.cursor--
|
|
||||||
return nil
|
|
||||||
case nul:
|
|
||||||
if !s.read() {
|
|
||||||
return errInvalidCharacter(s.char(), "escaped string", s.totalOffset())
|
|
||||||
}
|
|
||||||
goto RETRY
|
|
||||||
default:
|
|
||||||
return errUnexpectedEndOfJSON("string", s.totalOffset())
|
|
||||||
}
|
|
||||||
s.buf = append(s.buf[:s.cursor-1], s.buf[s.cursor:]...)
|
|
||||||
s.cursor--
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *interfaceDecoder) decodeStream(s *stream, p uintptr) error {
|
func (d *interfaceDecoder) decodeStream(s *stream, p uintptr) error {
|
||||||
s.skipWhiteSpace()
|
s.skipWhiteSpace()
|
||||||
for {
|
for {
|
||||||
|
|
116
decode_string.go
116
decode_string.go
|
@ -30,6 +30,85 @@ func (d *stringDecoder) decode(buf []byte, cursor int64, p uintptr) (int64, erro
|
||||||
return cursor, nil
|
return cursor, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
hexToInt = [256]int{
|
||||||
|
'0': 0,
|
||||||
|
'1': 1,
|
||||||
|
'2': 2,
|
||||||
|
'3': 3,
|
||||||
|
'4': 4,
|
||||||
|
'5': 5,
|
||||||
|
'6': 6,
|
||||||
|
'7': 7,
|
||||||
|
'8': 8,
|
||||||
|
'9': 9,
|
||||||
|
'A': 10,
|
||||||
|
'B': 11,
|
||||||
|
'C': 12,
|
||||||
|
'D': 13,
|
||||||
|
'E': 14,
|
||||||
|
'F': 15,
|
||||||
|
'a': 10,
|
||||||
|
'b': 11,
|
||||||
|
'c': 12,
|
||||||
|
'd': 13,
|
||||||
|
'e': 14,
|
||||||
|
'f': 15,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func unicodeToRune(code []byte) rune {
|
||||||
|
sum := 0
|
||||||
|
for i := 0; i < len(code); i++ {
|
||||||
|
sum += hexToInt[code[i]] << (uint(len(code)-i-1) * 4)
|
||||||
|
}
|
||||||
|
return rune(sum)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeEscapeString(s *stream) error {
|
||||||
|
s.cursor++
|
||||||
|
RETRY:
|
||||||
|
switch s.buf[s.cursor] {
|
||||||
|
case '"':
|
||||||
|
s.buf[s.cursor] = '"'
|
||||||
|
case '\\':
|
||||||
|
s.buf[s.cursor] = '\\'
|
||||||
|
case '/':
|
||||||
|
s.buf[s.cursor] = '/'
|
||||||
|
case 'b':
|
||||||
|
s.buf[s.cursor] = '\b'
|
||||||
|
case 'f':
|
||||||
|
s.buf[s.cursor] = '\f'
|
||||||
|
case 'n':
|
||||||
|
s.buf[s.cursor] = '\n'
|
||||||
|
case 'r':
|
||||||
|
s.buf[s.cursor] = '\r'
|
||||||
|
case 't':
|
||||||
|
s.buf[s.cursor] = '\t'
|
||||||
|
case 'u':
|
||||||
|
if s.cursor+5 >= s.length {
|
||||||
|
if !s.read() {
|
||||||
|
return errInvalidCharacter(s.char(), "escaped string", s.totalOffset())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
code := unicodeToRune(s.buf[s.cursor+1 : s.cursor+5])
|
||||||
|
unicode := []byte(string(code))
|
||||||
|
s.buf = append(append(s.buf[:s.cursor-1], unicode...), s.buf[s.cursor+5:]...)
|
||||||
|
s.cursor--
|
||||||
|
return nil
|
||||||
|
case nul:
|
||||||
|
if !s.read() {
|
||||||
|
return errInvalidCharacter(s.char(), "escaped string", s.totalOffset())
|
||||||
|
}
|
||||||
|
goto RETRY
|
||||||
|
default:
|
||||||
|
return errUnexpectedEndOfJSON("string", s.totalOffset())
|
||||||
|
}
|
||||||
|
s.buf = append(s.buf[:s.cursor-1], s.buf[s.cursor:]...)
|
||||||
|
s.cursor--
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func stringBytes(s *stream) ([]byte, error) {
|
func stringBytes(s *stream) ([]byte, error) {
|
||||||
s.cursor++
|
s.cursor++
|
||||||
start := s.cursor
|
start := s.cursor
|
||||||
|
@ -111,6 +190,43 @@ func (d *stringDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, err
|
||||||
switch buf[cursor] {
|
switch buf[cursor] {
|
||||||
case '\\':
|
case '\\':
|
||||||
cursor++
|
cursor++
|
||||||
|
switch buf[cursor] {
|
||||||
|
case '"':
|
||||||
|
buf[cursor] = '"'
|
||||||
|
buf = append(buf[:cursor-1], buf[cursor:]...)
|
||||||
|
case '\\':
|
||||||
|
buf[cursor] = '\\'
|
||||||
|
buf = append(buf[:cursor-1], buf[cursor:]...)
|
||||||
|
case '/':
|
||||||
|
buf[cursor] = '/'
|
||||||
|
buf = append(buf[:cursor-1], buf[cursor:]...)
|
||||||
|
case 'b':
|
||||||
|
buf[cursor] = '\b'
|
||||||
|
buf = append(buf[:cursor-1], buf[cursor:]...)
|
||||||
|
case 'f':
|
||||||
|
buf[cursor] = '\f'
|
||||||
|
buf = append(buf[:cursor-1], buf[cursor:]...)
|
||||||
|
case 'n':
|
||||||
|
buf[cursor] = '\n'
|
||||||
|
buf = append(buf[:cursor-1], buf[cursor:]...)
|
||||||
|
case 'r':
|
||||||
|
buf[cursor] = '\r'
|
||||||
|
buf = append(buf[:cursor-1], buf[cursor:]...)
|
||||||
|
case 't':
|
||||||
|
buf[cursor] = '\t'
|
||||||
|
buf = append(buf[:cursor-1], buf[cursor:]...)
|
||||||
|
case 'u':
|
||||||
|
buflen := int64(len(buf))
|
||||||
|
if cursor+5 >= buflen {
|
||||||
|
return nil, 0, errUnexpectedEndOfJSON("escaped string", cursor)
|
||||||
|
}
|
||||||
|
code := unicodeToRune(buf[cursor+1 : cursor+5])
|
||||||
|
unicode := []byte(string(code))
|
||||||
|
buf = append(append(buf[:cursor-1], unicode...), buf[cursor+5:]...)
|
||||||
|
default:
|
||||||
|
return nil, 0, errUnexpectedEndOfJSON("escaped string", cursor)
|
||||||
|
}
|
||||||
|
continue
|
||||||
case '"':
|
case '"':
|
||||||
literal := buf[start:cursor]
|
literal := buf[start:cursor]
|
||||||
cursor++
|
cursor++
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
package json
|
||||||
|
|
||||||
|
type wrappedStringDecoder struct {
|
||||||
|
dec decoder
|
||||||
|
stringDecoder *stringDecoder
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWrappedStringDecoder(dec decoder) *wrappedStringDecoder {
|
||||||
|
return &wrappedStringDecoder{
|
||||||
|
dec: dec,
|
||||||
|
stringDecoder: newStringDecoder(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *wrappedStringDecoder) decodeStream(s *stream, p uintptr) error {
|
||||||
|
bytes, err := d.stringDecoder.decodeStreamByte(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// save current state
|
||||||
|
buf := s.buf
|
||||||
|
length := s.length
|
||||||
|
cursor := s.cursor
|
||||||
|
|
||||||
|
// set content in string to stream
|
||||||
|
bytes = append(bytes, nul)
|
||||||
|
s.buf = bytes
|
||||||
|
s.cursor = 0
|
||||||
|
s.length = int64(len(bytes))
|
||||||
|
if err := d.dec.decodeStream(s, p); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore state
|
||||||
|
s.buf = buf
|
||||||
|
s.length = length
|
||||||
|
s.cursor = cursor
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *wrappedStringDecoder) decode(buf []byte, cursor int64, p uintptr) (int64, error) {
|
||||||
|
bytes, c, err := d.stringDecoder.decodeByte(buf, cursor)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
bytes = append(bytes, nul)
|
||||||
|
if _, err := d.dec.decode(bytes, 0, p); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
|
@ -3,7 +3,6 @@ package json
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -341,22 +340,6 @@ func (e *Encoder) compileMap(typ *rtype, withLoad, root, withIndent bool) (*opco
|
||||||
return (*opcode)(unsafe.Pointer(header)), nil
|
return (*opcode)(unsafe.Pointer(header)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) getTag(field reflect.StructField) string {
|
|
||||||
return field.Tag.Get("json")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Encoder) isIgnoredStructField(field reflect.StructField) bool {
|
|
||||||
if field.PkgPath != "" && !field.Anonymous {
|
|
||||||
// private field
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
tag := e.getTag(field)
|
|
||||||
if tag == "-" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Encoder) typeToHeaderType(op opType) opType {
|
func (e *Encoder) typeToHeaderType(op opType) opType {
|
||||||
switch op {
|
switch op {
|
||||||
case opInt:
|
case opInt:
|
||||||
|
@ -433,10 +416,13 @@ func (e *Encoder) typeToFieldType(op opType) opType {
|
||||||
return opStructField
|
return opStructField
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) optimizeStructHeader(op opType, isOmitEmpty, withIndent bool) opType {
|
func (e *Encoder) optimizeStructHeader(op opType, tag *structTag, withIndent bool) opType {
|
||||||
headType := e.typeToHeaderType(op)
|
headType := e.typeToHeaderType(op)
|
||||||
if isOmitEmpty {
|
switch {
|
||||||
|
case tag.isOmitEmpty:
|
||||||
headType = headType.headToOmitEmptyHead()
|
headType = headType.headToOmitEmptyHead()
|
||||||
|
case tag.isString:
|
||||||
|
headType = headType.headToStringTagHead()
|
||||||
}
|
}
|
||||||
if withIndent {
|
if withIndent {
|
||||||
return headType.toIndent()
|
return headType.toIndent()
|
||||||
|
@ -444,10 +430,13 @@ func (e *Encoder) optimizeStructHeader(op opType, isOmitEmpty, withIndent bool)
|
||||||
return headType
|
return headType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) optimizeStructField(op opType, isOmitEmpty, withIndent bool) opType {
|
func (e *Encoder) optimizeStructField(op opType, tag *structTag, withIndent bool) opType {
|
||||||
fieldType := e.typeToFieldType(op)
|
fieldType := e.typeToFieldType(op)
|
||||||
if isOmitEmpty {
|
switch {
|
||||||
|
case tag.isOmitEmpty:
|
||||||
fieldType = fieldType.fieldToOmitEmptyField()
|
fieldType = fieldType.fieldToOmitEmptyField()
|
||||||
|
case tag.isString:
|
||||||
|
fieldType = fieldType.fieldToStringTagField()
|
||||||
}
|
}
|
||||||
if withIndent {
|
if withIndent {
|
||||||
return fieldType.toIndent()
|
return fieldType.toIndent()
|
||||||
|
@ -481,25 +470,9 @@ func (e *Encoder) compiledCode(typ *rtype, withIndent bool) *opcode {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) keyNameAndOmitEmptyFromField(field reflect.StructField) (string, bool) {
|
func (e *Encoder) structHeader(fieldCode *structFieldCode, valueCode *opcode, tag *structTag, withIndent bool) *opcode {
|
||||||
keyName := field.Name
|
|
||||||
tag := e.getTag(field)
|
|
||||||
opts := strings.Split(tag, ",")
|
|
||||||
if len(opts) > 0 {
|
|
||||||
if opts[0] != "" {
|
|
||||||
keyName = opts[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
isOmitEmpty := false
|
|
||||||
if len(opts) > 1 {
|
|
||||||
isOmitEmpty = opts[1] == "omitempty"
|
|
||||||
}
|
|
||||||
return keyName, isOmitEmpty
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Encoder) structHeader(fieldCode *structFieldCode, valueCode *opcode, isOmitEmpty, withIndent bool) *opcode {
|
|
||||||
fieldCode.indent--
|
fieldCode.indent--
|
||||||
op := e.optimizeStructHeader(valueCode.op, isOmitEmpty, withIndent)
|
op := e.optimizeStructHeader(valueCode.op, tag, withIndent)
|
||||||
fieldCode.op = op
|
fieldCode.op = op
|
||||||
switch op {
|
switch op {
|
||||||
case opStructFieldHead,
|
case opStructFieldHead,
|
||||||
|
@ -511,9 +484,9 @@ func (e *Encoder) structHeader(fieldCode *structFieldCode, valueCode *opcode, is
|
||||||
return (*opcode)(unsafe.Pointer(fieldCode))
|
return (*opcode)(unsafe.Pointer(fieldCode))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) structField(fieldCode *structFieldCode, valueCode *opcode, isOmitEmpty, withIndent bool) *opcode {
|
func (e *Encoder) structField(fieldCode *structFieldCode, valueCode *opcode, tag *structTag, withIndent bool) *opcode {
|
||||||
code := (*opcode)(unsafe.Pointer(fieldCode))
|
code := (*opcode)(unsafe.Pointer(fieldCode))
|
||||||
op := e.optimizeStructField(valueCode.op, isOmitEmpty, withIndent)
|
op := e.optimizeStructField(valueCode.op, tag, withIndent)
|
||||||
fieldCode.op = op
|
fieldCode.op = op
|
||||||
switch op {
|
switch op {
|
||||||
case opStructField,
|
case opStructField,
|
||||||
|
@ -548,10 +521,10 @@ func (e *Encoder) compileStruct(typ *rtype, isPtr, root, withIndent bool) (*opco
|
||||||
e.indent++
|
e.indent++
|
||||||
for i := 0; i < fieldNum; i++ {
|
for i := 0; i < fieldNum; i++ {
|
||||||
field := typ.Field(i)
|
field := typ.Field(i)
|
||||||
if e.isIgnoredStructField(field) {
|
if isIgnoredStructField(field) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
keyName, isOmitEmpty := e.keyNameAndOmitEmptyFromField(field)
|
tag := structTagFromField(field)
|
||||||
fieldType := type2rtype(field.Type)
|
fieldType := type2rtype(field.Type)
|
||||||
if isPtr && i == 0 {
|
if isPtr && i == 0 {
|
||||||
// head field of pointer structure at top level
|
// head field of pointer structure at top level
|
||||||
|
@ -579,7 +552,7 @@ func (e *Encoder) compileStruct(typ *rtype, isPtr, root, withIndent bool) (*opco
|
||||||
f = f.nextField.toStructFieldCode()
|
f = f.nextField.toStructFieldCode()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
key := fmt.Sprintf(`"%s":`, keyName)
|
key := fmt.Sprintf(`"%s":`, tag.key)
|
||||||
fieldCode := &structFieldCode{
|
fieldCode := &structFieldCode{
|
||||||
opcodeHeader: &opcodeHeader{
|
opcodeHeader: &opcodeHeader{
|
||||||
typ: valueCode.typ,
|
typ: valueCode.typ,
|
||||||
|
@ -591,13 +564,13 @@ func (e *Encoder) compileStruct(typ *rtype, isPtr, root, withIndent bool) (*opco
|
||||||
offset: field.Offset,
|
offset: field.Offset,
|
||||||
}
|
}
|
||||||
if fieldIdx == 0 {
|
if fieldIdx == 0 {
|
||||||
code = e.structHeader(fieldCode, valueCode, isOmitEmpty, withIndent)
|
code = e.structHeader(fieldCode, valueCode, tag, withIndent)
|
||||||
head = fieldCode
|
head = fieldCode
|
||||||
prevField = fieldCode
|
prevField = fieldCode
|
||||||
} else {
|
} else {
|
||||||
fcode := (*opcode)(unsafe.Pointer(fieldCode))
|
fcode := (*opcode)(unsafe.Pointer(fieldCode))
|
||||||
code.next = fcode
|
code.next = fcode
|
||||||
code = e.structField(fieldCode, valueCode, isOmitEmpty, withIndent)
|
code = e.structField(fieldCode, valueCode, tag, withIndent)
|
||||||
prevField.nextField = fcode
|
prevField.nextField = fcode
|
||||||
prevField = fieldCode
|
prevField = fieldCode
|
||||||
}
|
}
|
||||||
|
|
2752
encode_optype.go
2752
encode_optype.go
File diff suppressed because it is too large
Load Diff
|
@ -492,6 +492,77 @@ func Test_MarshalIndent(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type StringTag struct {
|
||||||
|
BoolStr bool `json:",string"`
|
||||||
|
IntStr int64 `json:",string"`
|
||||||
|
UintptrStr uintptr `json:",string"`
|
||||||
|
StrStr string `json:",string"`
|
||||||
|
NumberStr json.Number `json:",string"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRoundtripStringTag(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
in StringTag
|
||||||
|
want string // empty to just test that we roundtrip
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "AllTypes",
|
||||||
|
in: StringTag{
|
||||||
|
BoolStr: true,
|
||||||
|
IntStr: 42,
|
||||||
|
UintptrStr: 44,
|
||||||
|
StrStr: "xzbit",
|
||||||
|
NumberStr: "46",
|
||||||
|
},
|
||||||
|
want: `{
|
||||||
|
"BoolStr": "true",
|
||||||
|
"IntStr": "42",
|
||||||
|
"UintptrStr": "44",
|
||||||
|
"StrStr": "\"xzbit\"",
|
||||||
|
"NumberStr": "46"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// See golang.org/issues/38173.
|
||||||
|
name: "StringDoubleEscapes",
|
||||||
|
in: StringTag{
|
||||||
|
StrStr: "\b\f\n\r\t\"\\",
|
||||||
|
NumberStr: "0", // just to satisfy the roundtrip
|
||||||
|
},
|
||||||
|
want: `{
|
||||||
|
"BoolStr": "false",
|
||||||
|
"IntStr": "0",
|
||||||
|
"UintptrStr": "0",
|
||||||
|
"StrStr": "\"\\u0008\\u000c\\n\\r\\t\\\"\\\\\"",
|
||||||
|
"NumberStr": "0"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
// Indent with a tab prefix to make the multi-line string
|
||||||
|
// literals in the table nicer to read.
|
||||||
|
got, err := json.MarshalIndent(&test.in, "\t\t\t", "\t")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if got := string(got); got != test.want {
|
||||||
|
t.Fatalf(" got: %s\nwant: %s\n", got, test.want)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that it round-trips.
|
||||||
|
var s2 StringTag
|
||||||
|
if err := json.Unmarshal(got, &s2); err != nil {
|
||||||
|
t.Fatalf("Decode: %v", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(test.in, s2) {
|
||||||
|
t.Fatalf("decode didn't match.\nsource: %#v\nEncoded as:\n%s\ndecode: %#v", test.in, string(got), s2)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// byte slices are special even if they're renamed types.
|
// byte slices are special even if they're renamed types.
|
||||||
type renamedByte byte
|
type renamedByte byte
|
||||||
type renamedByteSlice []byte
|
type renamedByteSlice []byte
|
||||||
|
|
1520
encode_vm.go
1520
encode_vm.go
File diff suppressed because it is too large
Load Diff
9
json.go
9
json.go
|
@ -300,6 +300,15 @@ func (n Number) MarshalJSON() ([]byte, error) {
|
||||||
return []byte(n), nil
|
return []byte(n), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *Number) UnmarshalJSON(b []byte) error {
|
||||||
|
s := string(b)
|
||||||
|
if _, err := strconv.ParseFloat(s, 64); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*n = Number(s)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// RawMessage is a raw encoded JSON value.
|
// RawMessage is a raw encoded JSON value.
|
||||||
// It implements Marshaler and Unmarshaler and can
|
// It implements Marshaler and Unmarshaler and can
|
||||||
// be used to delay JSON decoding or precompute a JSON encoding.
|
// be used to delay JSON decoding or precompute a JSON encoding.
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
package json
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getTag(field reflect.StructField) string {
|
||||||
|
return field.Tag.Get("json")
|
||||||
|
}
|
||||||
|
|
||||||
|
func isIgnoredStructField(field reflect.StructField) bool {
|
||||||
|
if field.PkgPath != "" && !field.Anonymous {
|
||||||
|
// private field
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
tag := getTag(field)
|
||||||
|
if tag == "-" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type structTag struct {
|
||||||
|
key string
|
||||||
|
isOmitEmpty bool
|
||||||
|
isString bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func structTagFromField(field reflect.StructField) *structTag {
|
||||||
|
keyName := field.Name
|
||||||
|
tag := getTag(field)
|
||||||
|
opts := strings.Split(tag, ",")
|
||||||
|
if len(opts) > 0 {
|
||||||
|
if opts[0] != "" {
|
||||||
|
keyName = opts[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
st := &structTag{key: keyName}
|
||||||
|
if len(opts) > 1 {
|
||||||
|
st.isOmitEmpty = opts[1] == "omitempty"
|
||||||
|
st.isString = opts[1] == "string"
|
||||||
|
}
|
||||||
|
return st
|
||||||
|
}
|
Loading…
Reference in New Issue