mirror of
https://github.com/netbirdio/easyjson.git
synced 2026-05-22 18:44:42 -07:00
Merge branch 'master' into win-path
This commit is contained in:
@@ -2,3 +2,4 @@
|
||||
*_easyjson.go
|
||||
*.iml
|
||||
.idea
|
||||
*.swp
|
||||
|
||||
@@ -6,7 +6,7 @@ all: test
|
||||
|
||||
.root/src/$(PKG):
|
||||
mkdir -p $@
|
||||
for i in $$PWD/* ; do ln -s $$i $@/`basename $$i` ; done
|
||||
for i in $$PWD/* ; do ln -s $$i $@/`basename $$i` ; done
|
||||
|
||||
root: .root/src/$(PKG)
|
||||
|
||||
@@ -23,9 +23,11 @@ generate: root build
|
||||
.root/src/$(PKG)/tests/data.go \
|
||||
.root/src/$(PKG)/tests/omitempty.go \
|
||||
.root/src/$(PKG)/tests/nothing.go \
|
||||
.root/src/$(PKG)/tests/named_type.go
|
||||
.root/src/$(PKG)/tests/named_type.go \
|
||||
.root/src/$(PKG)/tests/custom_map_key_type.go \
|
||||
.root/src/$(PKG)/tests/embedded_type.go
|
||||
|
||||
.root/bin/easyjson -all .root/src/$(PKG)/tests/data.go
|
||||
.root/bin/easyjson -all .root/src/$(PKG)/tests/data.go
|
||||
.root/bin/easyjson -all .root/src/$(PKG)/tests/nothing.go
|
||||
.root/bin/easyjson -all .root/src/$(PKG)/tests/errors.go
|
||||
.root/bin/easyjson -snake_case .root/src/$(PKG)/tests/snake.go
|
||||
@@ -33,6 +35,9 @@ generate: root build
|
||||
.root/bin/easyjson -build_tags=use_easyjson .root/src/$(PKG)/benchmark/data.go
|
||||
.root/bin/easyjson .root/src/$(PKG)/tests/nested_easy.go
|
||||
.root/bin/easyjson .root/src/$(PKG)/tests/named_type.go
|
||||
.root/bin/easyjson .root/src/$(PKG)/tests/custom_map_key_type.go
|
||||
.root/bin/easyjson .root/src/$(PKG)/tests/embedded_type.go
|
||||
.root/bin/easyjson -disallow_unknown_fields .root/src/$(PKG)/tests/disallow_unknown.go
|
||||
|
||||
test: generate root
|
||||
go test \
|
||||
|
||||
@@ -53,6 +53,8 @@ Usage of easyjson:
|
||||
use lowerCamelCase instead of CamelCase by default
|
||||
-stubs
|
||||
only generate stubs for marshaler/unmarshaler funcs
|
||||
-disallow_unknown_fields
|
||||
return error if some unknown field in json appeared
|
||||
```
|
||||
|
||||
Using `-all` will generate marshalers/unmarshalers for all Go structs in the
|
||||
|
||||
@@ -22,10 +22,11 @@ type Generator struct {
|
||||
PkgPath, PkgName string
|
||||
Types []string
|
||||
|
||||
NoStdMarshalers bool
|
||||
SnakeCase bool
|
||||
LowerCamelCase bool
|
||||
OmitEmpty bool
|
||||
NoStdMarshalers bool
|
||||
SnakeCase bool
|
||||
LowerCamelCase bool
|
||||
OmitEmpty bool
|
||||
DisallowUnknownFields bool
|
||||
|
||||
OutName string
|
||||
BuildTags string
|
||||
@@ -120,6 +121,9 @@ func (g *Generator) writeMain() (path string, err error) {
|
||||
if g.NoStdMarshalers {
|
||||
fmt.Fprintln(f, " g.NoStdMarshalers()")
|
||||
}
|
||||
if g.DisallowUnknownFields {
|
||||
fmt.Fprintln(f, " g.DisallowUnknownFields()")
|
||||
}
|
||||
|
||||
sort.Strings(g.Types)
|
||||
for _, v := range g.Types {
|
||||
|
||||
+14
-12
@@ -27,6 +27,7 @@ var stubs = flag.Bool("stubs", false, "only generate stubs for marshaler/unmarsh
|
||||
var noformat = flag.Bool("noformat", false, "do not run 'gofmt -w' on output file")
|
||||
var specifiedName = flag.String("output_filename", "", "specify the filename of the output")
|
||||
var processPkg = flag.Bool("pkg", false, "process the whole package instead of just the given file")
|
||||
var disallowUnknownFields = flag.Bool("disallow_unknown_fields", false, "return error if any unknown field in json appeared")
|
||||
|
||||
func generate(fname string) (err error) {
|
||||
fInfo, err := os.Stat(fname)
|
||||
@@ -60,18 +61,19 @@ func generate(fname string) (err error) {
|
||||
}
|
||||
|
||||
g := bootstrap.Generator{
|
||||
BuildTags: trimmedBuildTags,
|
||||
PkgPath: p.PkgPath,
|
||||
PkgName: p.PkgName,
|
||||
Types: p.StructNames,
|
||||
SnakeCase: *snakeCase,
|
||||
LowerCamelCase: *lowerCamelCase,
|
||||
NoStdMarshalers: *noStdMarshalers,
|
||||
OmitEmpty: *omitEmpty,
|
||||
LeaveTemps: *leaveTemps,
|
||||
OutName: outName,
|
||||
StubsOnly: *stubs,
|
||||
NoFormat: *noformat,
|
||||
BuildTags: trimmedBuildTags,
|
||||
PkgPath: p.PkgPath,
|
||||
PkgName: p.PkgName,
|
||||
Types: p.StructNames,
|
||||
SnakeCase: *snakeCase,
|
||||
LowerCamelCase: *lowerCamelCase,
|
||||
NoStdMarshalers: *noStdMarshalers,
|
||||
DisallowUnknownFields: *disallowUnknownFields,
|
||||
OmitEmpty: *omitEmpty,
|
||||
LeaveTemps: *leaveTemps,
|
||||
OutName: outName,
|
||||
StubsOnly: *stubs,
|
||||
NoFormat: *noformat,
|
||||
}
|
||||
|
||||
if err := g.Run(); err != nil {
|
||||
|
||||
+36
-10
@@ -48,10 +48,12 @@ var primitiveStringDecoders = map[reflect.Kind]string{
|
||||
reflect.Uint32: "in.Uint32Str()",
|
||||
reflect.Uint64: "in.Uint64Str()",
|
||||
reflect.Uintptr: "in.UintptrStr()",
|
||||
reflect.Float32: "in.Float32Str()",
|
||||
reflect.Float64: "in.Float64Str()",
|
||||
}
|
||||
|
||||
var customDecoders = map[string]string{
|
||||
"json.Number": "in.JsonNumber()",
|
||||
"json.Number": "in.JsonNumber()",
|
||||
}
|
||||
|
||||
// genTypeDecoder generates decoding code for the type t, but uses unmarshaler interface if implemented by t.
|
||||
@@ -84,11 +86,19 @@ func (g *Generator) genTypeDecoder(t reflect.Type, out string, tags fieldTags, i
|
||||
return err
|
||||
}
|
||||
|
||||
// returns true of the type t implements one of the custom unmarshaler interfaces
|
||||
func hasCustomUnmarshaler(t reflect.Type) bool {
|
||||
t = reflect.PtrTo(t)
|
||||
return t.Implements(reflect.TypeOf((*easyjson.Unmarshaler)(nil)).Elem()) ||
|
||||
t.Implements(reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()) ||
|
||||
t.Implements(reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem())
|
||||
}
|
||||
|
||||
// genTypeDecoderNoCheck generates decoding code for the type t.
|
||||
func (g *Generator) genTypeDecoderNoCheck(t reflect.Type, out string, tags fieldTags, indent int) error {
|
||||
ws := strings.Repeat(" ", indent)
|
||||
// Check whether type is primitive, needs to be done after interface check.
|
||||
if dec := customDecoders[t.String()]; dec != "" {
|
||||
if dec := customDecoders[t.String()]; dec != "" {
|
||||
fmt.Fprintln(g.out, ws+out+" = "+dec)
|
||||
return nil
|
||||
} else if dec := primitiveStringDecoders[t.Kind()]; dec != "" && tags.asString {
|
||||
@@ -104,7 +114,7 @@ func (g *Generator) genTypeDecoderNoCheck(t reflect.Type, out string, tags field
|
||||
tmpVar := g.uniqueVarName()
|
||||
elem := t.Elem()
|
||||
|
||||
if elem.Kind() == reflect.Uint8 {
|
||||
if elem.Kind() == reflect.Uint8 && elem.Name() == "uint8" {
|
||||
fmt.Fprintln(g.out, ws+"if in.IsNull() {")
|
||||
fmt.Fprintln(g.out, ws+" in.Skip()")
|
||||
fmt.Fprintln(g.out, ws+" "+out+" = nil")
|
||||
@@ -151,7 +161,7 @@ func (g *Generator) genTypeDecoderNoCheck(t reflect.Type, out string, tags field
|
||||
iterVar := g.uniqueVarName()
|
||||
elem := t.Elem()
|
||||
|
||||
if elem.Kind() == reflect.Uint8 {
|
||||
if elem.Kind() == reflect.Uint8 && elem.Name() == "uint8" {
|
||||
fmt.Fprintln(g.out, ws+"if in.IsNull() {")
|
||||
fmt.Fprintln(g.out, ws+" in.Skip()")
|
||||
fmt.Fprintln(g.out, ws+"} else {")
|
||||
@@ -170,7 +180,7 @@ func (g *Generator) genTypeDecoderNoCheck(t reflect.Type, out string, tags field
|
||||
fmt.Fprintln(g.out, ws+" for !in.IsDelim(']') {")
|
||||
fmt.Fprintln(g.out, ws+" if "+iterVar+" < "+fmt.Sprint(length)+" {")
|
||||
|
||||
if err := g.genTypeDecoder(elem, out+"["+iterVar+"]", tags, indent+3); err != nil {
|
||||
if err := g.genTypeDecoder(elem, "("+out+")["+iterVar+"]", tags, indent+3); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -208,9 +218,9 @@ func (g *Generator) genTypeDecoderNoCheck(t reflect.Type, out string, tags field
|
||||
case reflect.Map:
|
||||
key := t.Key()
|
||||
keyDec, ok := primitiveStringDecoders[key.Kind()]
|
||||
if !ok {
|
||||
return fmt.Errorf("map type %v not supported: only string and integer keys are allowed", key)
|
||||
}
|
||||
if !ok && !hasCustomUnmarshaler(key) {
|
||||
return fmt.Errorf("map type %v not supported: only string and integer keys and types implementing json.Unmarshaler are allowed", key)
|
||||
} // else assume the caller knows what they are doing and that the custom unmarshaler performs the translation from string or integer keys to the key type
|
||||
elem := t.Elem()
|
||||
tmpVar := g.uniqueVarName()
|
||||
|
||||
@@ -225,7 +235,15 @@ func (g *Generator) genTypeDecoderNoCheck(t reflect.Type, out string, tags field
|
||||
fmt.Fprintln(g.out, ws+" }")
|
||||
|
||||
fmt.Fprintln(g.out, ws+" for !in.IsDelim('}') {")
|
||||
fmt.Fprintln(g.out, ws+" key := "+g.getType(key)+"("+keyDec+")")
|
||||
if keyDec != "" {
|
||||
fmt.Fprintln(g.out, ws+" key := "+g.getType(key)+"("+keyDec+")")
|
||||
} else {
|
||||
fmt.Fprintln(g.out, ws+" var key "+g.getType(key))
|
||||
if err := g.genTypeDecoder(key, "key", tags, indent+2); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintln(g.out, ws+" in.WantColon()")
|
||||
fmt.Fprintln(g.out, ws+" var "+tmpVar+" "+g.getType(elem))
|
||||
|
||||
@@ -443,7 +461,15 @@ func (g *Generator) genStructDecoder(t reflect.Type) error {
|
||||
}
|
||||
|
||||
fmt.Fprintln(g.out, " default:")
|
||||
fmt.Fprintln(g.out, " in.SkipRecursive()")
|
||||
if g.disallowUnknownFields {
|
||||
fmt.Fprintln(g.out, ` in.AddError(&jlexer.LexerError{
|
||||
Offset: in.GetPos(),
|
||||
Reason: "unknown field",
|
||||
Data: key,
|
||||
})`)
|
||||
} else {
|
||||
fmt.Fprintln(g.out, " in.SkipRecursive()")
|
||||
}
|
||||
fmt.Fprintln(g.out, " }")
|
||||
fmt.Fprintln(g.out, " in.WantComma()")
|
||||
fmt.Fprintln(g.out, " }")
|
||||
|
||||
+67
-37
@@ -45,6 +45,8 @@ var primitiveStringEncoders = map[reflect.Kind]string{
|
||||
reflect.Uint32: "out.Uint32Str(uint32(%v))",
|
||||
reflect.Uint64: "out.Uint64Str(uint64(%v))",
|
||||
reflect.Uintptr: "out.UintptrStr(uintptr(%v))",
|
||||
reflect.Float32: "out.Float32Str(float32(%v))",
|
||||
reflect.Float64: "out.Float64Str(float64(%v))",
|
||||
}
|
||||
|
||||
// fieldTags contains parsed version of json struct field tags.
|
||||
@@ -83,7 +85,7 @@ func parseFieldTags(f reflect.StructField) fieldTags {
|
||||
}
|
||||
|
||||
// genTypeEncoder generates code that encodes in of type t into the writer, but uses marshaler interface if implemented by t.
|
||||
func (g *Generator) genTypeEncoder(t reflect.Type, in string, tags fieldTags, indent int) error {
|
||||
func (g *Generator) genTypeEncoder(t reflect.Type, in string, tags fieldTags, indent int, assumeNonEmpty bool) error {
|
||||
ws := strings.Repeat(" ", indent)
|
||||
|
||||
marshalerIface := reflect.TypeOf((*easyjson.Marshaler)(nil)).Elem()
|
||||
@@ -104,12 +106,20 @@ func (g *Generator) genTypeEncoder(t reflect.Type, in string, tags fieldTags, in
|
||||
return nil
|
||||
}
|
||||
|
||||
err := g.genTypeEncoderNoCheck(t, in, tags, indent)
|
||||
err := g.genTypeEncoderNoCheck(t, in, tags, indent, assumeNonEmpty)
|
||||
return err
|
||||
}
|
||||
|
||||
// returns true of the type t implements one of the custom marshaler interfaces
|
||||
func hasCustomMarshaler(t reflect.Type) bool {
|
||||
t = reflect.PtrTo(t)
|
||||
return t.Implements(reflect.TypeOf((*easyjson.Marshaler)(nil)).Elem()) ||
|
||||
t.Implements(reflect.TypeOf((*json.Marshaler)(nil)).Elem()) ||
|
||||
t.Implements(reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem())
|
||||
}
|
||||
|
||||
// genTypeEncoderNoCheck generates code that encodes in of type t into the writer.
|
||||
func (g *Generator) genTypeEncoderNoCheck(t reflect.Type, in string, tags fieldTags, indent int) error {
|
||||
func (g *Generator) genTypeEncoderNoCheck(t reflect.Type, in string, tags fieldTags, indent int, assumeNonEmpty bool) error {
|
||||
ws := strings.Repeat(" ", indent)
|
||||
|
||||
// Check whether type is primitive, needs to be done after interface check.
|
||||
@@ -127,19 +137,23 @@ func (g *Generator) genTypeEncoderNoCheck(t reflect.Type, in string, tags fieldT
|
||||
iVar := g.uniqueVarName()
|
||||
vVar := g.uniqueVarName()
|
||||
|
||||
if t.Elem().Kind() == reflect.Uint8 {
|
||||
if t.Elem().Kind() == reflect.Uint8 && elem.Name() == "uint8" {
|
||||
fmt.Fprintln(g.out, ws+"out.Base64Bytes("+in+")")
|
||||
} else {
|
||||
fmt.Fprintln(g.out, ws+"if "+in+" == nil && (out.Flags & jwriter.NilSliceAsEmpty) == 0 {")
|
||||
fmt.Fprintln(g.out, ws+` out.RawString("null")`)
|
||||
fmt.Fprintln(g.out, ws+"} else {")
|
||||
if !assumeNonEmpty {
|
||||
fmt.Fprintln(g.out, ws+"if "+in+" == nil && (out.Flags & jwriter.NilSliceAsEmpty) == 0 {")
|
||||
fmt.Fprintln(g.out, ws+` out.RawString("null")`)
|
||||
fmt.Fprintln(g.out, ws+"} else {")
|
||||
} else {
|
||||
fmt.Fprintln(g.out, ws+"{")
|
||||
}
|
||||
fmt.Fprintln(g.out, ws+" out.RawByte('[')")
|
||||
fmt.Fprintln(g.out, ws+" for "+iVar+", "+vVar+" := range "+in+" {")
|
||||
fmt.Fprintln(g.out, ws+" if "+iVar+" > 0 {")
|
||||
fmt.Fprintln(g.out, ws+" out.RawByte(',')")
|
||||
fmt.Fprintln(g.out, ws+" }")
|
||||
|
||||
if err := g.genTypeEncoder(elem, vVar, tags, indent+2); err != nil {
|
||||
if err := g.genTypeEncoder(elem, vVar, tags, indent+2, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -152,7 +166,7 @@ func (g *Generator) genTypeEncoderNoCheck(t reflect.Type, in string, tags fieldT
|
||||
elem := t.Elem()
|
||||
iVar := g.uniqueVarName()
|
||||
|
||||
if t.Elem().Kind() == reflect.Uint8 {
|
||||
if t.Elem().Kind() == reflect.Uint8 && elem.Name() == "uint8" {
|
||||
fmt.Fprintln(g.out, ws+"out.Base64Bytes("+in+"[:])")
|
||||
} else {
|
||||
fmt.Fprintln(g.out, ws+"out.RawByte('[')")
|
||||
@@ -161,7 +175,7 @@ func (g *Generator) genTypeEncoderNoCheck(t reflect.Type, in string, tags fieldT
|
||||
fmt.Fprintln(g.out, ws+" out.RawByte(',')")
|
||||
fmt.Fprintln(g.out, ws+" }")
|
||||
|
||||
if err := g.genTypeEncoder(elem, in+"["+iVar+"]", tags, indent+1); err != nil {
|
||||
if err := g.genTypeEncoder(elem, "("+in+")["+iVar+"]", tags, indent+1, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -176,36 +190,50 @@ func (g *Generator) genTypeEncoderNoCheck(t reflect.Type, in string, tags fieldT
|
||||
fmt.Fprintln(g.out, ws+enc+"(out, "+in+")")
|
||||
|
||||
case reflect.Ptr:
|
||||
fmt.Fprintln(g.out, ws+"if "+in+" == nil {")
|
||||
fmt.Fprintln(g.out, ws+` out.RawString("null")`)
|
||||
fmt.Fprintln(g.out, ws+"} else {")
|
||||
if !assumeNonEmpty {
|
||||
fmt.Fprintln(g.out, ws+"if "+in+" == nil {")
|
||||
fmt.Fprintln(g.out, ws+` out.RawString("null")`)
|
||||
fmt.Fprintln(g.out, ws+"} else {")
|
||||
}
|
||||
|
||||
if err := g.genTypeEncoder(t.Elem(), "*"+in, tags, indent+1); err != nil {
|
||||
if err := g.genTypeEncoder(t.Elem(), "*"+in, tags, indent+1, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintln(g.out, ws+"}")
|
||||
if !assumeNonEmpty {
|
||||
fmt.Fprintln(g.out, ws+"}")
|
||||
}
|
||||
|
||||
case reflect.Map:
|
||||
key := t.Key()
|
||||
keyEnc, ok := primitiveStringEncoders[key.Kind()]
|
||||
if !ok {
|
||||
return fmt.Errorf("map key type %v not supported: only string and integer keys are allowed", key)
|
||||
}
|
||||
if !ok && !hasCustomMarshaler(key) {
|
||||
return fmt.Errorf("map key type %v not supported: only string and integer keys and types implementing Marshaler interfaces are allowed", key)
|
||||
} // else assume the caller knows what they are doing and that the custom marshaler performs the translation from the key type to a string or integer
|
||||
tmpVar := g.uniqueVarName()
|
||||
|
||||
fmt.Fprintln(g.out, ws+"if "+in+" == nil && (out.Flags & jwriter.NilMapAsEmpty) == 0 {")
|
||||
fmt.Fprintln(g.out, ws+" out.RawString(`null`)")
|
||||
fmt.Fprintln(g.out, ws+"} else {")
|
||||
if !assumeNonEmpty {
|
||||
fmt.Fprintln(g.out, ws+"if "+in+" == nil && (out.Flags & jwriter.NilMapAsEmpty) == 0 {")
|
||||
fmt.Fprintln(g.out, ws+" out.RawString(`null`)")
|
||||
fmt.Fprintln(g.out, ws+"} else {")
|
||||
} else {
|
||||
fmt.Fprintln(g.out, ws+"{")
|
||||
}
|
||||
fmt.Fprintln(g.out, ws+" out.RawByte('{')")
|
||||
fmt.Fprintln(g.out, ws+" "+tmpVar+"First := true")
|
||||
fmt.Fprintln(g.out, ws+" for "+tmpVar+"Name, "+tmpVar+"Value := range "+in+" {")
|
||||
fmt.Fprintln(g.out, ws+" if !"+tmpVar+"First { out.RawByte(',') }")
|
||||
fmt.Fprintln(g.out, ws+" "+tmpVar+"First = false")
|
||||
fmt.Fprintln(g.out, ws+" "+fmt.Sprintf(keyEnc, tmpVar+"Name"))
|
||||
fmt.Fprintln(g.out, ws+" if "+tmpVar+"First { "+tmpVar+"First = false } else { out.RawByte(',') }")
|
||||
if keyEnc != "" {
|
||||
fmt.Fprintln(g.out, ws+" "+fmt.Sprintf(keyEnc, tmpVar+"Name"))
|
||||
} else {
|
||||
if err := g.genTypeEncoder(key, tmpVar+"Name", tags, indent+2, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintln(g.out, ws+" out.RawByte(':')")
|
||||
|
||||
if err := g.genTypeEncoder(t.Elem(), tmpVar+"Value", tags, indent+2); err != nil {
|
||||
if err := g.genTypeEncoder(t.Elem(), tmpVar+"Value", tags, indent+2, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -265,19 +293,21 @@ func (g *Generator) genStructFieldEncoder(t reflect.Type, f reflect.StructField)
|
||||
if tags.omit {
|
||||
return nil
|
||||
}
|
||||
if !tags.omitEmpty && !g.omitEmpty || tags.noOmitEmpty {
|
||||
fmt.Fprintln(g.out, " if !first { out.RawByte(',') }")
|
||||
fmt.Fprintln(g.out, " first = false")
|
||||
fmt.Fprintf(g.out, " out.RawString(%q)\n", strconv.Quote(jsonName)+":")
|
||||
return g.genTypeEncoder(f.Type, "in."+f.Name, tags, 1)
|
||||
noOmitEmpty := (!tags.omitEmpty && !g.omitEmpty) || tags.noOmitEmpty
|
||||
if noOmitEmpty {
|
||||
fmt.Fprintln(g.out, " {")
|
||||
} else {
|
||||
fmt.Fprintln(g.out, " if", g.notEmptyCheck(f.Type, "in."+f.Name), "{")
|
||||
}
|
||||
fmt.Fprintf(g.out, " const prefix string = %q\n", ","+strconv.Quote(jsonName)+":")
|
||||
fmt.Fprintln(g.out, " if first {")
|
||||
fmt.Fprintln(g.out, " first = false")
|
||||
fmt.Fprintln(g.out, " out.RawString(prefix[1:])")
|
||||
fmt.Fprintln(g.out, " } else {")
|
||||
fmt.Fprintln(g.out, " out.RawString(prefix)")
|
||||
fmt.Fprintln(g.out, " }")
|
||||
|
||||
fmt.Fprintln(g.out, " if", g.notEmptyCheck(f.Type, "in."+f.Name), "{")
|
||||
fmt.Fprintln(g.out, " if !first { out.RawByte(',') }")
|
||||
fmt.Fprintln(g.out, " first = false")
|
||||
|
||||
fmt.Fprintf(g.out, " out.RawString(%q)\n", strconv.Quote(jsonName)+":")
|
||||
if err := g.genTypeEncoder(f.Type, "in."+f.Name, tags, 2); err != nil {
|
||||
if err := g.genTypeEncoder(f.Type, "in."+f.Name, tags, 2, !noOmitEmpty); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(g.out, " }")
|
||||
@@ -304,7 +334,7 @@ func (g *Generator) genSliceArrayMapEncoder(t reflect.Type) error {
|
||||
typ := g.getType(t)
|
||||
|
||||
fmt.Fprintln(g.out, "func "+fname+"(out *jwriter.Writer, in "+typ+") {")
|
||||
err := g.genTypeEncoderNoCheck(t, "in", fieldTags{}, 1)
|
||||
err := g.genTypeEncoderNoCheck(t, "in", fieldTags{}, 1, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+14
-4
@@ -33,9 +33,10 @@ type Generator struct {
|
||||
|
||||
varCounter int
|
||||
|
||||
noStdMarshalers bool
|
||||
omitEmpty bool
|
||||
fieldNamer FieldNamer
|
||||
noStdMarshalers bool
|
||||
omitEmpty bool
|
||||
disallowUnknownFields bool
|
||||
fieldNamer FieldNamer
|
||||
|
||||
// package path to local alias map for tracking imports
|
||||
imports map[string]string
|
||||
@@ -110,6 +111,11 @@ func (g *Generator) NoStdMarshalers() {
|
||||
g.noStdMarshalers = true
|
||||
}
|
||||
|
||||
// DisallowUnknownFields instructs not to skip unknown fields in json and return error.
|
||||
func (g *Generator) DisallowUnknownFields() {
|
||||
g.disallowUnknownFields = true
|
||||
}
|
||||
|
||||
// OmitEmpty triggers `json=",omitempty"` behaviour by default.
|
||||
func (g *Generator) OmitEmpty() {
|
||||
g.omitEmpty = true
|
||||
@@ -284,7 +290,11 @@ func (g *Generator) getType(t reflect.Type) string {
|
||||
lines := make([]string, 0, nf)
|
||||
for i := 0; i < nf; i++ {
|
||||
f := t.Field(i)
|
||||
line := f.Name + " " + g.getType(f.Type)
|
||||
var line string
|
||||
if !f.Anonymous {
|
||||
line = f.Name + " "
|
||||
} // else the field is anonymous (an embedded type)
|
||||
line += g.getType(f.Type)
|
||||
t := f.Tag
|
||||
if t != "" {
|
||||
line += " " + escapeTag(t)
|
||||
|
||||
+39
-4
@@ -649,7 +649,7 @@ func (r *Lexer) Bytes() []byte {
|
||||
return nil
|
||||
}
|
||||
ret := make([]byte, base64.StdEncoding.DecodedLen(len(r.token.byteValue)))
|
||||
len, err := base64.StdEncoding.Decode(ret, r.token.byteValue)
|
||||
n, err := base64.StdEncoding.Decode(ret, r.token.byteValue)
|
||||
if err != nil {
|
||||
r.fatalError = &LexerError{
|
||||
Reason: err.Error(),
|
||||
@@ -658,7 +658,7 @@ func (r *Lexer) Bytes() []byte {
|
||||
}
|
||||
|
||||
r.consume()
|
||||
return ret[:len]
|
||||
return ret[:n]
|
||||
}
|
||||
|
||||
// Bool reads a true or false boolean keyword.
|
||||
@@ -997,6 +997,22 @@ func (r *Lexer) Float32() float32 {
|
||||
return float32(n)
|
||||
}
|
||||
|
||||
func (r *Lexer) Float32Str() float32 {
|
||||
s, b := r.unsafeString()
|
||||
if !r.Ok() {
|
||||
return 0
|
||||
}
|
||||
n, err := strconv.ParseFloat(s, 32)
|
||||
if err != nil {
|
||||
r.addNonfatalError(&LexerError{
|
||||
Offset: r.start,
|
||||
Reason: err.Error(),
|
||||
Data: string(b),
|
||||
})
|
||||
}
|
||||
return float32(n)
|
||||
}
|
||||
|
||||
func (r *Lexer) Float64() float64 {
|
||||
s := r.number()
|
||||
if !r.Ok() {
|
||||
@@ -1014,6 +1030,22 @@ func (r *Lexer) Float64() float64 {
|
||||
return n
|
||||
}
|
||||
|
||||
func (r *Lexer) Float64Str() float64 {
|
||||
s, b := r.unsafeString()
|
||||
if !r.Ok() {
|
||||
return 0
|
||||
}
|
||||
n, err := strconv.ParseFloat(s, 64)
|
||||
if err != nil {
|
||||
r.addNonfatalError(&LexerError{
|
||||
Offset: r.start,
|
||||
Reason: err.Error(),
|
||||
Data: string(b),
|
||||
})
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (r *Lexer) Error() error {
|
||||
return r.fatalError
|
||||
}
|
||||
@@ -1056,7 +1088,7 @@ func (r *Lexer) JsonNumber() json.Number {
|
||||
}
|
||||
if !r.Ok() {
|
||||
r.errInvalidToken("json.Number")
|
||||
return json.Number("0")
|
||||
return json.Number("")
|
||||
}
|
||||
|
||||
switch r.token.kind {
|
||||
@@ -1064,9 +1096,12 @@ func (r *Lexer) JsonNumber() json.Number {
|
||||
return json.Number(r.String())
|
||||
case tokenNumber:
|
||||
return json.Number(r.Raw())
|
||||
case tokenNull:
|
||||
r.Null()
|
||||
return json.Number("")
|
||||
default:
|
||||
r.errSyntax()
|
||||
return json.Number("0")
|
||||
return json.Number("")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-9
@@ -25,9 +25,9 @@ func TestString(t *testing.T) {
|
||||
|
||||
{toParse: `"test"junk`, want: "test"},
|
||||
|
||||
{toParse: `5`, wantError: true}, // not a string
|
||||
{toParse: `"\x"`, wantError: true}, // invalid escape
|
||||
{toParse: `"\ud800"`, want: "�"}, // invalid utf-8 char; return replacement char
|
||||
{toParse: `5`, wantError: true}, // not a string
|
||||
{toParse: `"\x"`, wantError: true}, // invalid escape
|
||||
{toParse: `"\ud800"`, want: "�"}, // invalid utf-8 char; return replacement char
|
||||
} {
|
||||
l := Lexer{Data: []byte(test.toParse)}
|
||||
|
||||
@@ -269,16 +269,19 @@ func TestJsonNumber(t *testing.T) {
|
||||
{toParse: `"0.12"`, want: json.Number("0.12"), wantValue: 0.12},
|
||||
{toParse: `"25E-4"`, want: json.Number("25E-4"), wantValue: 25E-4},
|
||||
|
||||
{toParse: `"a""`, wantValueError: true},
|
||||
{toParse: `"foo"`, want: json.Number("foo"), wantValueError: true},
|
||||
{toParse: `null`, want: json.Number(""), wantValueError: true},
|
||||
|
||||
{toParse: `[1]`, wantLexerError: true},
|
||||
{toParse: `{}`, wantLexerError: true},
|
||||
{toParse: `a`, wantLexerError: true},
|
||||
{toParse: `"a""`, want: json.Number("a"), wantValueError: true},
|
||||
|
||||
{toParse: `[1]`, want: json.Number(""), wantLexerError: true, wantValueError: true},
|
||||
{toParse: `{}`, want: json.Number(""), wantLexerError: true, wantValueError: true},
|
||||
{toParse: `a`, want: json.Number(""), wantLexerError: true, wantValueError: true},
|
||||
} {
|
||||
l := Lexer{Data: []byte(test.toParse)}
|
||||
|
||||
got := l.JsonNumber()
|
||||
if got != test.want && !test.wantLexerError && !test.wantValueError {
|
||||
if got != test.want {
|
||||
t.Errorf("[%d, %q] JsonNumber() = %v; want %v", i, test.toParse, got, test.want)
|
||||
}
|
||||
|
||||
@@ -303,7 +306,7 @@ func TestJsonNumber(t *testing.T) {
|
||||
}
|
||||
|
||||
if valueErr != nil && !test.wantValueError {
|
||||
t.Errorf("[%d, %q] JsonNumber() value error: %v", i, test.toParse, err)
|
||||
t.Errorf("[%d, %q] JsonNumber() value error: %v", i, test.toParse, valueErr)
|
||||
} else if valueErr == nil && test.wantValueError {
|
||||
t.Errorf("[%d, %q] JsonNumber() ok; want value error", i, test.toParse)
|
||||
}
|
||||
|
||||
+15
-2
@@ -240,11 +240,25 @@ func (w *Writer) Float32(n float32) {
|
||||
w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 32)
|
||||
}
|
||||
|
||||
func (w *Writer) Float32Str(n float32) {
|
||||
w.Buffer.EnsureSpace(20)
|
||||
w.Buffer.Buf = append(w.Buffer.Buf, '"')
|
||||
w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 32)
|
||||
w.Buffer.Buf = append(w.Buffer.Buf, '"')
|
||||
}
|
||||
|
||||
func (w *Writer) Float64(n float64) {
|
||||
w.Buffer.EnsureSpace(20)
|
||||
w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, n, 'g', -1, 64)
|
||||
}
|
||||
|
||||
func (w *Writer) Float64Str(n float64) {
|
||||
w.Buffer.EnsureSpace(20)
|
||||
w.Buffer.Buf = append(w.Buffer.Buf, '"')
|
||||
w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 64)
|
||||
w.Buffer.Buf = append(w.Buffer.Buf, '"')
|
||||
}
|
||||
|
||||
func (w *Writer) Bool(v bool) {
|
||||
w.Buffer.EnsureSpace(5)
|
||||
if v {
|
||||
@@ -340,12 +354,11 @@ func (w *Writer) base64(in []byte) {
|
||||
return
|
||||
}
|
||||
|
||||
w.Buffer.EnsureSpace(((len(in) - 1) / 3 + 1) * 4)
|
||||
w.Buffer.EnsureSpace(((len(in)-1)/3 + 1) * 4)
|
||||
|
||||
si := 0
|
||||
n := (len(in) / 3) * 3
|
||||
|
||||
|
||||
for si < n {
|
||||
// Convert 3x 8bit source bytes into 4 bytes
|
||||
val := uint(in[si+0])<<16 | uint(in[si+1])<<8 | uint(in[si+2])
|
||||
|
||||
+1
-1
@@ -93,5 +93,5 @@ func (p *Parser) Parse(fname string, isDir bool) error {
|
||||
|
||||
func getDefaultGoPath() (string, error) {
|
||||
output, err := exec.Command("go", "env", "GOPATH").Output()
|
||||
return string(output), err
|
||||
return strings.TrimSpace(string(output)), err
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ func getPkgPath(fname string, isDir bool) (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
for _, p := range strings.Split(os.Getenv("GOPATH"), ":") {
|
||||
for _, p := range strings.Split(gopath, ":") {
|
||||
prefix := path.Join(p, "src") + "/"
|
||||
if rel := strings.TrimPrefix(fname, prefix); rel != fname {
|
||||
if !isDir {
|
||||
|
||||
@@ -33,7 +33,7 @@ func getPkgPath(fname string, isDir bool) (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
for _, p := range strings.Split(os.Getenv("GOPATH"), ";") {
|
||||
for _, p := range strings.Split(gopath, ";") {
|
||||
prefix := path.Join(normalizePath(p), "src") + "/"
|
||||
if rel := strings.TrimPrefix(fname, prefix); rel != fname {
|
||||
if !isDir {
|
||||
|
||||
@@ -38,6 +38,8 @@ var testCases = []struct {
|
||||
{&IntsValue, IntsString},
|
||||
{&mapStringStringValue, mapStringStringString},
|
||||
{&namedTypeValue, namedTypeValueString},
|
||||
{&customMapKeyTypeValue, customMapKeyTypeValueString},
|
||||
{&embeddedTypeValue, embeddedTypeValueString},
|
||||
{&mapMyIntStringValue, mapMyIntStringValueString},
|
||||
{&mapIntStringValue, mapIntStringValueString},
|
||||
{&mapInt32StringValue, mapInt32StringValueString},
|
||||
@@ -47,6 +49,9 @@ var testCases = []struct {
|
||||
{&mapUint64StringValue, mapUint64StringValueString},
|
||||
{&mapUintptrStringValue, mapUintptrStringValueString},
|
||||
{&intKeyedMapStructValue, intKeyedMapStructValueString},
|
||||
{&intArrayStructValue, intArrayStructValueString},
|
||||
{&myUInt8SliceValue, myUInt8SliceString},
|
||||
{&myUInt8ArrayValue, myUInt8ArrayString},
|
||||
}
|
||||
|
||||
func TestMarshal(t *testing.T) {
|
||||
@@ -229,3 +234,11 @@ func TestUnmarshalStructWithEmbeddedPtrStruct(t *testing.T) {
|
||||
t.Errorf("easyjson.Unmarshal() = %#v; want %#v", s, structWithInterfaceValueFilled)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDisallowUnknown(t *testing.T) {
|
||||
var d DisallowUnknown
|
||||
err := easyjson.Unmarshal([]byte(disallowUnknownString), &d)
|
||||
if err == nil {
|
||||
t.Error("want error, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package tests
|
||||
|
||||
import fmt "fmt"
|
||||
|
||||
//easyjson:json
|
||||
type CustomMapKeyType struct {
|
||||
Map map[customKeyType]int
|
||||
}
|
||||
|
||||
type customKeyType [2]byte
|
||||
|
||||
func (k customKeyType) MarshalJSON() ([]byte, error) {
|
||||
return []byte(fmt.Sprintf(`"%02x"`, k)), nil
|
||||
}
|
||||
|
||||
func (k *customKeyType) UnmarshalJSON(b []byte) error {
|
||||
_, err := fmt.Sscanf(string(b), `"%02x%02x"`, &k[0], &k[1])
|
||||
return err
|
||||
}
|
||||
|
||||
var customMapKeyTypeValue CustomMapKeyType
|
||||
|
||||
func init() {
|
||||
customMapKeyTypeValue.Map = map[customKeyType]int{
|
||||
customKeyType{0x01, 0x02}: 3,
|
||||
}
|
||||
}
|
||||
|
||||
var customMapKeyTypeValueString = `{"Map":{"0102":3}}`
|
||||
@@ -41,6 +41,9 @@ type PrimitiveTypes struct {
|
||||
Float32 float32
|
||||
Float64 float64
|
||||
|
||||
Float32String float32 `json:",string"`
|
||||
Float64String float64 `json:",string"`
|
||||
|
||||
Ptr *string
|
||||
PtrNil *string
|
||||
}
|
||||
@@ -77,6 +80,9 @@ var primitiveTypesValue = PrimitiveTypes{
|
||||
Float32: 1.5,
|
||||
Float64: math.MaxFloat64,
|
||||
|
||||
Float32String: 1.5,
|
||||
Float64String: math.MaxFloat64,
|
||||
|
||||
Ptr: &str,
|
||||
}
|
||||
|
||||
@@ -110,6 +116,9 @@ var primitiveTypesString = "{" +
|
||||
`"Float32":` + fmt.Sprint(1.5) + `,` +
|
||||
`"Float64":` + fmt.Sprint(math.MaxFloat64) + `,` +
|
||||
|
||||
`"Float32String":"` + fmt.Sprint(1.5) + `",` +
|
||||
`"Float64String":"` + fmt.Sprint(math.MaxFloat64) + `",` +
|
||||
|
||||
`"Ptr":"bla",` +
|
||||
`"PtrNil":null` +
|
||||
|
||||
@@ -757,3 +766,37 @@ var intKeyedMapStructValueString = `{` +
|
||||
`"foo":{"42":"life"},` +
|
||||
`"bar":{"32":{"354634382":"life"}}` +
|
||||
`}`
|
||||
|
||||
type IntArray [2]int
|
||||
|
||||
//easyjson:json
|
||||
type IntArrayStruct struct {
|
||||
Pointer *IntArray `json:"pointer"`
|
||||
Value IntArray `json:"value"`
|
||||
}
|
||||
|
||||
var intArrayStructValue = IntArrayStruct{
|
||||
Pointer: &IntArray{1, 2},
|
||||
Value: IntArray{1, 2},
|
||||
}
|
||||
|
||||
var intArrayStructValueString = `{` +
|
||||
`"pointer":[1,2],` +
|
||||
`"value":[1,2]` +
|
||||
`}`
|
||||
|
||||
type MyUInt8 uint8
|
||||
|
||||
//easyjson:json
|
||||
type MyUInt8Slice []MyUInt8
|
||||
|
||||
var myUInt8SliceValue = MyUInt8Slice{1, 2, 3, 4, 5}
|
||||
|
||||
var myUInt8SliceString = `[1,2,3,4,5]`
|
||||
|
||||
//easyjson:json
|
||||
type MyUInt8Array [2]MyUInt8
|
||||
|
||||
var myUInt8ArrayValue = MyUInt8Array{1, 2}
|
||||
|
||||
var myUInt8ArrayString = `[1,2]`
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package tests
|
||||
|
||||
//easyjson:json
|
||||
type DisallowUnknown struct {
|
||||
FieldOne string `json:"field_one"`
|
||||
}
|
||||
|
||||
var disallowUnknownString = `{"field_one": "one", "field_two": "two"}`
|
||||
@@ -0,0 +1,24 @@
|
||||
package tests
|
||||
|
||||
//easyjson:json
|
||||
type EmbeddedType struct {
|
||||
EmbeddedInnerType
|
||||
Inner struct {
|
||||
EmbeddedInnerType
|
||||
}
|
||||
Field2 int
|
||||
}
|
||||
|
||||
type EmbeddedInnerType struct {
|
||||
Field1 int
|
||||
}
|
||||
|
||||
var embeddedTypeValue EmbeddedType
|
||||
|
||||
func init() {
|
||||
embeddedTypeValue.Field1 = 1
|
||||
embeddedTypeValue.Field2 = 2
|
||||
embeddedTypeValue.Inner.Field1 = 3
|
||||
}
|
||||
|
||||
var embeddedTypeValueString = `{"Inner":{"Field1":3},"Field2":2,"Field1":1}`
|
||||
Reference in New Issue
Block a user