Standard encoding/json marshaller interface support.

This commit is contained in:
Victor Starodub
2016-03-04 23:27:56 +10:00
parent 6579d07abd
commit 04b6211116
7 changed files with 66 additions and 23 deletions
+6 -2
View File
@@ -20,8 +20,9 @@ type Generator struct {
PkgPath, PkgName string
Types []string
SnakeCase bool
OmitEmpty bool
NoStdMarshalers bool
SnakeCase bool
OmitEmpty bool
OutName string
BuildTags string
@@ -78,6 +79,9 @@ func (g *Generator) writeMain() (path string, err error) {
if g.OmitEmpty {
fmt.Fprintln(f, " g.OmitEmpty()")
}
if g.NoStdMarshalers {
fmt.Fprintln(f, " g.NoStdMarshalers()")
}
for _, v := range g.Types {
fmt.Fprintln(f, " g.Add(pkg."+v+"{})")
}
+11 -9
View File
@@ -12,6 +12,7 @@ import (
var buildTags = flag.String("build_tags", "", "build tags to add to generated file")
var snakeCase = flag.Bool("snake_case", false, "use snake_case names instead of CamelCase by default")
var noStdMarshalers = flag.Bool("no_std_marshalers", false, "don't generate MarshalJSON/UnmarshalJSON methods")
var omitEmpty = flag.Bool("omit_empty", false, "omit empty fields by default")
var allStructs = flag.Bool("all", false, "generate un-/marshallers for all structs in a file")
var leaveTemps = flag.Bool("leave_temps", false, "do not delete temporary files")
@@ -31,15 +32,16 @@ func generate(fname string) (err error) {
}
g := bootstrap.Generator{
BuildTags: *buildTags,
PkgPath: p.PkgPath,
PkgName: p.PkgName,
Types: p.StructNames,
SnakeCase: *snakeCase,
OmitEmpty: *omitEmpty,
LeaveTemps: *leaveTemps,
OutName: outName,
StubsOnly: *stubs,
BuildTags: *buildTags,
PkgPath: p.PkgPath,
PkgName: p.PkgName,
Types: p.StructNames,
SnakeCase: *snakeCase,
NoStdMarshalers: *noStdMarshalers,
OmitEmpty: *omitEmpty,
LeaveTemps: *leaveTemps,
OutName: outName,
StubsOnly: *stubs,
}
if err := g.Run(); err != nil {
+17 -5
View File
@@ -1,6 +1,7 @@
package gen
import (
"encoding/json"
"fmt"
"reflect"
"strings"
@@ -42,6 +43,14 @@ func (g *Generator) genTypeDecoder(t reflect.Type, out string, indent int) error
return nil
}
unmarshalerIface = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()
if reflect.PtrTo(t).Implements(unmarshalerIface) {
fmt.Fprintln(g.out, ws+"if data := in.Raw(); in.Ok() {")
fmt.Fprintln(g.out, ws+" in.AddError( ("+out+").UnmarshalJSON(data) )")
fmt.Fprintln(g.out, ws+"}")
return nil
}
// Check whether type is primitive, needs to be done after interface check.
if dec := primitiveDecoders[t.Kind()]; dec != "" {
fmt.Fprintln(g.out, ws+out+" = "+dec)
@@ -224,11 +233,14 @@ func (g *Generator) genStructUnmarshaller(t reflect.Type) error {
fname := g.getStructDecoderName(t)
typ := g.getType(t)
fmt.Fprintln(g.out, "func (v *"+typ+") UnmarshalJSON(data []byte) error {")
fmt.Fprintln(g.out, " r := jlexer.Lexer{Data: data}")
fmt.Fprintln(g.out, " "+fname+"(&r, v)")
fmt.Fprintln(g.out, " return r.Error()")
fmt.Fprintln(g.out, "}")
if !g.noStdMarshalers {
fmt.Fprintln(g.out, "func (v *"+typ+") UnmarshalJSON(data []byte) error {")
fmt.Fprintln(g.out, " r := jlexer.Lexer{Data: data}")
fmt.Fprintln(g.out, " "+fname+"(&r, v)")
fmt.Fprintln(g.out, " return r.Error()")
fmt.Fprintln(g.out, "}")
}
fmt.Fprintln(g.out, "func (v *"+typ+") UnmarshalEasyJSON(l *jlexer.Lexer) {")
fmt.Fprintln(g.out, " "+fname+"(l, v)")
fmt.Fprintln(g.out, "}")
+14 -5
View File
@@ -1,6 +1,7 @@
package gen
import (
"encoding/json"
"fmt"
"reflect"
"strconv"
@@ -39,6 +40,12 @@ func (g *Generator) genTypeEncoder(t reflect.Type, in string, indent int) error
return nil
}
marshalerIface = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
if reflect.PtrTo(t).Implements(marshalerIface) {
fmt.Fprintln(g.out, ws+"out.Raw( ("+in+").MarshalJSON() )")
return nil
}
// Check whether type is primitive, needs to be done after interface check.
if enc := primitiveEncoders[t.Kind()]; enc != "" {
fmt.Fprintln(g.out, ws+enc+"("+in+")")
@@ -198,11 +205,13 @@ func (g *Generator) genStructMarshaller(t reflect.Type) error {
fname := g.getStructEncoderName(t)
typ := g.getType(t)
fmt.Fprintln(g.out, "func (v *"+typ+") MarshalJSON() ([]byte, error) {")
fmt.Fprintln(g.out, " w := jwriter.Writer{}")
fmt.Fprintln(g.out, " "+fname+"(&w, v)")
fmt.Fprintln(g.out, " return w.Buffer.BuildBytes(), w.Error")
fmt.Fprintln(g.out, "}")
if !g.noStdMarshalers {
fmt.Fprintln(g.out, "func (v *"+typ+") MarshalJSON() ([]byte, error) {")
fmt.Fprintln(g.out, " w := jwriter.Writer{}")
fmt.Fprintln(g.out, " "+fname+"(&w, v)")
fmt.Fprintln(g.out, " return w.Buffer.BuildBytes(), w.Error")
fmt.Fprintln(g.out, "}")
}
fmt.Fprintln(g.out, "func (v *"+typ+") MarshalEasyJSON(w *jwriter.Writer) {")
fmt.Fprintln(g.out, " "+fname+"(w, v)")
+9 -2
View File
@@ -29,8 +29,9 @@ type Generator struct {
varCounter int
omitEmpty bool
namer FieldNamer
noStdMarshalers bool
omitEmpty bool
namer FieldNamer
// package path to local alias map for tracking imports
imports map[string]string
@@ -85,6 +86,12 @@ func (g *Generator) UseSnakeCase() {
g.namer = SnakeCaseFieldNamer{}
}
// NoStdMarshalers instructs not to generate standard MarshalJSON/UnmarshalJSON
// methods (only the custom interface).
func (g *Generator) NoStdMarshalers() {
g.noStdMarshalers = true
}
// OmitEmpty triggers `json=",omitempty"` behaviour by default.
func (g *Generator) OmitEmpty() {
g.omitEmpty = true
+1
View File
@@ -23,6 +23,7 @@ var testCases = []struct {
{&omitEmptyDefaultValue, omitEmptyDefaultString},
{&optsValue, optsString},
{&rawValue, rawString},
{&stdMarshalerValue, stdMarshalerString},
}
func TestMarshal(t *testing.T) {
+8
View File
@@ -3,6 +3,7 @@ package tests
import (
"fmt"
"math"
"time"
"github.com/mailru/easyjson"
"github.com/mailru/easyjson/opt"
@@ -200,3 +201,10 @@ var rawString = `{` +
`"Field":{"a" : "b"},` +
`"Field2":"test"` +
`}`
type StdMarshaler struct {
T time.Time
}
var stdMarshalerValue = StdMarshaler{T: time.Date(2016, 01, 02, 14, 15, 10, 0, time.UTC)}
var stdMarshalerString = `{"T":"2016-01-02T14:15:10Z"}`