diff --git a/gen/decoder.go b/gen/decoder.go index f4c328f..80f8d2c 100644 --- a/gen/decoder.go +++ b/gen/decoder.go @@ -1,6 +1,7 @@ package gen import ( + "encoding" "encoding/json" "fmt" "reflect" @@ -65,6 +66,14 @@ func (g *Generator) genTypeDecoder(t reflect.Type, out string, tags fieldTags, i return nil } + unmarshalerIface = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() + if reflect.PtrTo(t).Implements(unmarshalerIface) { + fmt.Fprintln(g.out, ws+"if data := in.UnsafeBytes(); in.Ok() {") + fmt.Fprintln(g.out, ws+" in.AddError( ("+out+").UnmarshalText(data) )") + fmt.Fprintln(g.out, ws+"}") + return nil + } + err := g.genTypeDecoderNoCheck(t, out, tags, indent) return err } diff --git a/gen/encoder.go b/gen/encoder.go index 202b9a4..a54f6e2 100644 --- a/gen/encoder.go +++ b/gen/encoder.go @@ -1,6 +1,7 @@ package gen import ( + "encoding" "encoding/json" "fmt" "reflect" @@ -95,6 +96,12 @@ func (g *Generator) genTypeEncoder(t reflect.Type, in string, tags fieldTags, in return nil } + marshalerIface = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() + if reflect.PtrTo(t).Implements(marshalerIface) { + fmt.Fprintln(g.out, ws+"out.RawText( ("+in+").MarshalText() )") + return nil + } + err := g.genTypeEncoderNoCheck(t, in, tags, indent) return err } diff --git a/gen/generator.go b/gen/generator.go index bd2a56b..0e10527 100644 --- a/gen/generator.go +++ b/gen/generator.go @@ -212,6 +212,16 @@ func fixPkgPathVendoring(pkgPath string) string { return pkgPath } +func fixAliasName(alias string) string { + alias := strings.Replace( + strings.Replace(path.Base(pkgPath), ".", "_", -1), + "-", + "_", + -1, + ) + return alias +} + // pkgAlias creates and returns and import alias for a given package. func (g *Generator) pkgAlias(pkgPath string) string { pkgPath = fixPkgPathVendoring(pkgPath) @@ -220,12 +230,7 @@ func (g *Generator) pkgAlias(pkgPath string) string { } for i := 0; ; i++ { - alias := strings.Replace( - strings.Replace(path.Base(pkgPath), ".", "_", -1), - "-", - "_", - -1, - ) + alias := fixAliasName(path.Base(pkgPath)) if i > 0 { alias += fmt.Sprint(i) } diff --git a/jlexer/lexer.go b/jlexer/lexer.go index ae8344e..d4c16f1 100644 --- a/jlexer/lexer.go +++ b/jlexer/lexer.go @@ -618,6 +618,12 @@ func (r *Lexer) UnsafeString() string { return ret } +// UnsafeBytes returns the byte slice if the token is a string literal. +func (r *Lexer) UnsafeBytes() []byte { + _, ret := r.unsafeString() + return ret +} + // String reads a string literal. func (r *Lexer) String() string { if r.token.kind == tokenUndef && r.Ok() { diff --git a/jwriter/writer.go b/jwriter/writer.go index 6e78466..7b55293 100644 --- a/jwriter/writer.go +++ b/jwriter/writer.go @@ -68,7 +68,7 @@ func (w *Writer) RawString(s string) { w.Buffer.AppendString(s) } -// RawByte appends raw binary data to the buffer or sets the error if it is given. Useful for +// Raw appends raw binary data to the buffer or sets the error if it is given. Useful for // calling with results of MarshalJSON-like functions. func (w *Writer) Raw(data []byte, err error) { switch { @@ -83,6 +83,21 @@ func (w *Writer) Raw(data []byte, err error) { } } +// RawText encloses raw binary data in quotes and appends in to the buffer. +// Useful for calling with results of MarshalText-like functions. +func (w *Writer) RawText(data []byte, err error) { + switch { + case w.Error != nil: + return + case err != nil: + w.Error = err + case len(data) > 0: + w.String(string(data)) + default: + w.RawString("null") + } +} + // Base64Bytes appends data to the buffer after base64 encoding it func (w *Writer) Base64Bytes(data []byte) { if data == nil { diff --git a/tests/basic_test.go b/tests/basic_test.go index 4e7c20c..b727c9e 100644 --- a/tests/basic_test.go +++ b/tests/basic_test.go @@ -28,6 +28,7 @@ var testCases = []struct { {&optsValue, optsString}, {&rawValue, rawString}, {&stdMarshalerValue, stdMarshalerString}, + {&userMarshalerValue, userMarshalerString}, {&unexportedStructValue, unexportedStructString}, {&excludedFieldValue, excludedFieldString}, {&sliceValue, sliceString}, diff --git a/tests/data.go b/tests/data.go index b13bcd8..ca8676e 100644 --- a/tests/data.go +++ b/tests/data.go @@ -3,6 +3,7 @@ package tests import ( "fmt" "math" + "net" "time" "github.com/mailru/easyjson" @@ -392,11 +393,54 @@ var rawString = `{` + `}` type StdMarshaler struct { - T time.Time + T time.Time + IP net.IP } -var stdMarshalerValue = StdMarshaler{T: time.Date(2016, 01, 02, 14, 15, 10, 0, time.UTC)} -var stdMarshalerString = `{"T":"2016-01-02T14:15:10Z"}` +var stdMarshalerValue = StdMarshaler{ + T: time.Date(2016, 01, 02, 14, 15, 10, 0, time.UTC), + IP: net.IPv4(192, 168, 0, 1), +} +var stdMarshalerString = `{` + + `"T":"2016-01-02T14:15:10Z",` + + `"IP":"192.168.0.1"` + + `}` + +type UserMarshaler struct { + V vMarshaler + T tMarshaler +} + +type vMarshaler net.IP + +func (v vMarshaler) MarshalJSON() ([]byte, error) { + return []byte(`"0::0"`), nil +} + +func (v *vMarshaler) UnmarshalJSON([]byte) error { + *v = vMarshaler(net.IPv6zero) + return nil +} + +type tMarshaler net.IP + +func (v tMarshaler) MarshalText() ([]byte, error) { + return []byte(`[0::0]`), nil +} + +func (v *tMarshaler) UnmarshalText([]byte) error { + *v = tMarshaler(net.IPv6zero) + return nil +} + +var userMarshalerValue = UserMarshaler{ + V: vMarshaler(net.IPv6zero), + T: tMarshaler(net.IPv6zero), +} +var userMarshalerString = `{` + + `"V":"0::0",` + + `"T":"[0::0]"` + + `}` type unexportedStruct struct { Value string