diff --git a/gen/decoder.go b/gen/decoder.go index 4030b14..7080715 100644 --- a/gen/decoder.go +++ b/gen/decoder.go @@ -13,7 +13,7 @@ import ( // Target this byte size for initial slice allocation to reduce garbage collection. const minSliceBytes = 64 -func (g *Generator) getStructDecoderName(t reflect.Type) string { +func (g *Generator) getDecoderName(t reflect.Type) string { return g.functionName("decode_", t) } @@ -47,7 +47,7 @@ var primitiveStringDecoders = map[reflect.Kind]string{ reflect.Uint64: "in.Uint64Str()", } -// genTypeDecoder generates decoding code for the type t. +// genTypeDecoder generates decoding code for the type t, but uses unmarshaler interface if implemented by t. func (g *Generator) genTypeDecoder(t reflect.Type, out string, tags fieldTags, indent int) error { ws := strings.Repeat(" ", indent) @@ -65,6 +65,13 @@ func (g *Generator) genTypeDecoder(t reflect.Type, out string, tags fieldTags, i return nil } + err := g.genTypeDecoderNoCheck(t, out, tags, indent) + return err +} + +// 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 := primitiveStringDecoders[t.Kind()]; dec != "" && tags.asString { fmt.Fprintln(g.out, ws+out+" = "+g.getType(t)+"("+dec+")") @@ -101,7 +108,7 @@ func (g *Generator) genTypeDecoder(t reflect.Type, out string, tags fieldTags, i fmt.Fprintln(g.out, ws+"in.Delim(']')") case reflect.Struct: - dec := g.getStructDecoderName(t) + dec := g.getDecoderName(t) g.addType(t) fmt.Fprintln(g.out, ws+dec+"(in, &"+out+")") @@ -260,12 +267,39 @@ func getStructFields(t reflect.Type) ([]reflect.StructField, error) { return mergeStructFields(efields, fields), nil } +func (g *Generator) genDecoder(t reflect.Type) error { + switch t.Kind() { + case reflect.Slice: + return g.genSliceDecoder(t) + default: + return g.genStructDecoder(t) + } +} + +func (g *Generator) genSliceDecoder(t reflect.Type) error { + if t.Kind() != reflect.Slice { + return fmt.Errorf("cannot generate encoder/decoder for %v, not a slice type", t) + } + + fname := g.getDecoderName(t) + typ := g.getType(t) + + fmt.Fprintln(g.out, "func "+fname+"(in *jlexer.Lexer, out *"+typ+") {") + err := g.genTypeDecoderNoCheck(t, "*out", fieldTags{}, 1) + if err != nil { + return err + } + fmt.Fprintln(g.out, "}") + + return nil +} + func (g *Generator) genStructDecoder(t reflect.Type) error { if t.Kind() != reflect.Struct { return fmt.Errorf("cannot generate encoder/decoder for %v, not a struct type", t) } - fname := g.getStructDecoderName(t) + fname := g.getDecoderName(t) typ := g.getType(t) fmt.Fprintln(g.out, "func "+fname+"(in *jlexer.Lexer, out *"+typ+") {") @@ -326,11 +360,11 @@ func (g *Generator) genStructDecoder(t reflect.Type) error { } func (g *Generator) genStructUnmarshaller(t reflect.Type) error { - if t.Kind() != reflect.Struct { - return fmt.Errorf("cannot generate encoder/decoder for %v, not a struct type", t) + if t.Kind() != reflect.Struct && t.Kind() != reflect.Slice { + return fmt.Errorf("cannot generate encoder/decoder for %v, not a struct/slice type", t) } - fname := g.getStructDecoderName(t) + fname := g.getDecoderName(t) typ := g.getType(t) if !g.noStdMarshalers { diff --git a/gen/encoder.go b/gen/encoder.go index c263c21..b6cdee6 100644 --- a/gen/encoder.go +++ b/gen/encoder.go @@ -10,7 +10,7 @@ import ( "github.com/mailru/easyjson" ) -func (g *Generator) getStructEncoderName(t reflect.Type) string { +func (g *Generator) getEncoderName(t reflect.Type) string { return g.functionName("encode_", t) } @@ -79,7 +79,7 @@ func parseFieldTags(f reflect.StructField) fieldTags { return ret } -// genTypeEncoder generates code that encodes in of type t into the writer. +// 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 { ws := strings.Repeat(" ", indent) @@ -95,6 +95,14 @@ func (g *Generator) genTypeEncoder(t reflect.Type, in string, tags fieldTags, in return nil } + err := g.genTypeEncoderNoCheck(t, in, tags, indent) + return err +} + +// 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 { + ws := strings.Repeat(" ", indent) + // Check whether type is primitive, needs to be done after interface check. if enc := primitiveStringEncoders[t.Kind()]; enc != "" && tags.asString { fmt.Fprintf(g.out, ws+enc+"\n", in) @@ -122,7 +130,7 @@ func (g *Generator) genTypeEncoder(t reflect.Type, in string, tags fieldTags, in fmt.Fprintln(g.out, ws+"out.RawByte(']')") case reflect.Struct: - enc := g.getStructEncoderName(t) + enc := g.getEncoderName(t) g.addType(t) fmt.Fprintln(g.out, ws+enc+"(out, "+in+")") @@ -224,12 +232,38 @@ func (g *Generator) genStructFieldEncoder(t reflect.Type, f reflect.StructField) return nil } -func (g *Generator) genStructEncoder(t reflect.Type) error { - if t.Kind() != reflect.Struct { - return fmt.Errorf("cannot generate encoder/decoder for %v, not a struct type", t) +func (g *Generator) genEncoder(t reflect.Type) error { + switch t.Kind() { + case reflect.Slice: + return g.genSliceEncoder(t) + default: + return g.genStructEncoder(t) + } +} + +func (g *Generator) genSliceEncoder(t reflect.Type) error { + if t.Kind() != reflect.Slice { + return fmt.Errorf("cannot generate encoder/decoder for %v, not a slice type", t) } - fname := g.getStructEncoderName(t) + fname := g.getEncoderName(t) + typ := g.getType(t) + + fmt.Fprintln(g.out, "func "+fname+"(out *jwriter.Writer, in "+typ+") {") + err := g.genTypeEncoderNoCheck(t, "in", fieldTags{}, 1) + if err != nil { + return err + } + fmt.Fprintln(g.out, "}") + return nil +} + +func (g *Generator) genStructEncoder(t reflect.Type) error { + if t.Kind() != reflect.Struct { + return fmt.Errorf("cannot generate encoder/decoder for %v, not a struct type") + } + + fname := g.getEncoderName(t) typ := g.getType(t) fmt.Fprintln(g.out, "func "+fname+"(out *jwriter.Writer, in "+typ+") {") @@ -254,11 +288,11 @@ func (g *Generator) genStructEncoder(t reflect.Type) error { } func (g *Generator) genStructMarshaller(t reflect.Type) error { - if t.Kind() != reflect.Struct { - return fmt.Errorf("cannot generate encoder/decoder for %v, not a struct type", t) + if t.Kind() != reflect.Struct && t.Kind() != reflect.Slice { + return fmt.Errorf("cannot generate encoder/decoder for %v, not a struct/slice type", t) } - fname := g.getStructEncoderName(t) + fname := g.getEncoderName(t) typ := g.getType(t) if !g.noStdMarshalers { diff --git a/gen/generator.go b/gen/generator.go index 83bf812..fcf0dda 100644 --- a/gen/generator.go +++ b/gen/generator.go @@ -175,10 +175,10 @@ func (g *Generator) Run(out io.Writer) error { g.typesUnseen = g.typesUnseen[:len(g.typesUnseen)-1] g.typesSeen[t] = true - if err := g.genStructDecoder(t); err != nil { + if err := g.genDecoder(t); err != nil { return err } - if err := g.genStructEncoder(t); err != nil { + if err := g.genEncoder(t); err != nil { return err } diff --git a/tests/basic_test.go b/tests/basic_test.go index 8525240..d2eac28 100644 --- a/tests/basic_test.go +++ b/tests/basic_test.go @@ -32,6 +32,7 @@ var testCases = []struct { {&excludedFieldValue, excludedFieldString}, {&mapsValue, mapsString}, {&deepNestValue, deepNestString}, + {&IntsValue, IntsString}, } func TestMarshal(t *testing.T) { @@ -107,7 +108,7 @@ func TestParseNull(t *testing.T) { var testSpecialCases = []struct { EncodedString string - Value string + Value string }{ {`"Username \u003cuser@example.com\u003e"`, `Username `}, {`"Username\ufffd"`, "Username\xc5"}, @@ -129,4 +130,4 @@ func TestSpecialCases(t *testing.T) { t.Errorf("[%d] Encoded() = %+v; want %+v", i, got, test.EncodedString) } } -} \ No newline at end of file +} diff --git a/tests/data.go b/tests/data.go index 1378f89..faa3093 100644 --- a/tests/data.go +++ b/tests/data.go @@ -246,6 +246,8 @@ type Structs struct { AnonymousSlice []struct{ V int } AnonymousPtrSlice []*struct{ V int } + Slice []string + unexported bool } @@ -282,6 +284,8 @@ var structsValue = Structs{ AnonymousSlice: []struct{ V int }{{1}, {2}}, AnonymousPtrSlice: []*struct{ V int }{{3}, {4}}, + + Slice: []string{"test5", "test6"}, } var structsString = "{" + @@ -306,6 +310,8 @@ var structsString = "{" + `"AnonymousSlice":[{"V":1},{"V":2}],` + `"AnonymousPtrSlice":[{"V":3},{"V":4}],` + + `"Slice":["test5","test6"],` + + // Embedded fields go last. `"V":"subp",` + `"Value":"test"` + @@ -526,6 +532,13 @@ var deepNestString = `{` + `"NamedStringSlice":["value4","value5"]` + `}` +//easyjson:json +type Ints []int + +var IntsValue = Ints{1, 2, 3, 4, 5} + +var IntsString = `[1,2,3,4,5]` + type RequiredOptionalStruct struct { FirstName string `json:"first_name,required"` Lastname string `json:"last_name"`