diff --git a/gen/decoder.go b/gen/decoder.go index 476130c..09c6e89 100644 --- a/gen/decoder.go +++ b/gen/decoder.go @@ -34,7 +34,21 @@ var primitiveDecoders = map[reflect.Kind]string{ reflect.Float64: "in.Float64()", } -func (g *Generator) genTypeDecoder(t reflect.Type, out string, indent int) error { +var primitiveStringDecoders = map[reflect.Kind]string{ + reflect.Int: "in.IntStr()", + reflect.Int8: "in.Int8Str()", + reflect.Int16: "in.Int16Str()", + reflect.Int32: "in.Int32Str()", + reflect.Int64: "in.Int64Str()", + reflect.Uint: "in.UintStr()", + reflect.Uint8: "in.Uint8Str()", + reflect.Uint16: "in.Uint16Str()", + reflect.Uint32: "in.Uint32Str()", + reflect.Uint64: "in.Uint64Str()", +} + +// genTypeDecoder generates decoding code for the type t. +func (g *Generator) genTypeDecoder(t reflect.Type, out string, tags fieldTags, indent int) error { ws := strings.Repeat(" ", indent) unmarshalerIface := reflect.TypeOf((*easyjson.Unmarshaler)(nil)).Elem() @@ -52,7 +66,10 @@ func (g *Generator) genTypeDecoder(t reflect.Type, out string, indent int) error } // Check whether type is primitive, needs to be done after interface check. - if dec := primitiveDecoders[t.Kind()]; dec != "" { + if dec := primitiveStringDecoders[t.Kind()]; dec != "" && tags.asString { + fmt.Fprintln(g.out, ws+out+" = "+g.getType(t)+"("+dec+")") + return nil + } else if dec := primitiveDecoders[t.Kind()]; dec != "" { fmt.Fprintln(g.out, ws+out+" = "+g.getType(t)+"("+dec+")") return nil } @@ -76,7 +93,7 @@ func (g *Generator) genTypeDecoder(t reflect.Type, out string, indent int) error fmt.Fprintln(g.out, ws+"for !in.IsDelim(']') {") fmt.Fprintln(g.out, ws+" var "+tmpVar+" "+g.getType(elem)) - g.genTypeDecoder(elem, tmpVar, indent+1) + g.genTypeDecoder(elem, tmpVar, tags, indent+1) fmt.Fprintln(g.out, ws+" "+out+" = append("+out+", "+tmpVar+")") fmt.Fprintln(g.out, ws+" in.WantComma()") @@ -96,7 +113,7 @@ func (g *Generator) genTypeDecoder(t reflect.Type, out string, indent int) error fmt.Fprintln(g.out, ws+"} else {") fmt.Fprintln(g.out, ws+" "+out+" = new("+g.getType(t.Elem())+")") - g.genTypeDecoder(t.Elem(), "*"+out, indent+1) + g.genTypeDecoder(t.Elem(), "*"+out, tags, indent+1) fmt.Fprintln(g.out, ws+"}") @@ -123,7 +140,7 @@ func (g *Generator) genTypeDecoder(t reflect.Type, out string, indent int) error fmt.Fprintln(g.out, ws+" in.WantColon()") fmt.Fprintln(g.out, ws+" var "+tmpVar+" "+g.getType(elem)) - g.genTypeDecoder(elem, tmpVar, indent+2) + g.genTypeDecoder(elem, tmpVar, tags, indent+2) fmt.Fprintln(g.out, ws+" ("+out+")[key] = "+tmpVar) fmt.Fprintln(g.out, ws+" in.WantComma()") @@ -146,9 +163,10 @@ func (g *Generator) genTypeDecoder(t reflect.Type, out string, indent int) error func (g *Generator) genStructFieldDecoder(t reflect.Type, f reflect.StructField) error { jsonName := g.namer.GetJSONFieldName(t, f) + tags := parseFieldTags(f) fmt.Fprintf(g.out, " case %q:\n", jsonName) - return g.genTypeDecoder(f.Type, "out."+f.Name, 3) + return g.genTypeDecoder(f.Type, "out."+f.Name, tags, 3) } func mergeStructFields(fields1, fields2 []reflect.StructField) (fields []reflect.StructField) { diff --git a/gen/encoder.go b/gen/encoder.go index 4579da6..2945d89 100644 --- a/gen/encoder.go +++ b/gen/encoder.go @@ -31,7 +31,53 @@ var primitiveEncoders = map[reflect.Kind]string{ reflect.Float64: "out.Float64(float64(%v))", } -func (g *Generator) genTypeEncoder(t reflect.Type, in string, indent int) error { +var primitiveStringEncoders = map[reflect.Kind]string{ + reflect.Int: "out.IntStr(int(%v))", + reflect.Int8: "out.Int8Str(int8(%v))", + reflect.Int16: "out.Int16Str(int16(%v))", + reflect.Int32: "out.Int32Str(int32(%v))", + reflect.Int64: "out.Int64Str(int64(%v))", + reflect.Uint: "out.UintStr(uint(%v))", + reflect.Uint8: "out.Uint8Str(uint8(%v))", + reflect.Uint16: "out.Uint16Str(uint16(%v))", + reflect.Uint32: "out.Uint32Str(uint32(%v))", + reflect.Uint64: "out.Uint64Str(uint64(%v))", +} + +// fieldTags contains parsed version of json struct field tags. +type fieldTags struct { + name string + + omit bool + omitEmpty bool + noOmitEmpty bool + asString bool +} + +// parseFieldTags parses the json field tag into a structure. +func parseFieldTags(f reflect.StructField) fieldTags { + var ret fieldTags + + for i, s := range strings.Split(f.Tag.Get("json"), ",") { + switch { + case i == 0 && s == "-": + ret.omit = true + case i == 0: + ret.name = s + case s == "omitempty": + ret.omitEmpty = true + case s == "!omitempty": + ret.noOmitEmpty = true + case s == "string": + ret.asString = true + } + } + + return ret +} + +// genTypeEncoder generates code that encodes in of type t into the writer. +func (g *Generator) genTypeEncoder(t reflect.Type, in string, tags fieldTags, indent int) error { ws := strings.Repeat(" ", indent) marshalerIface := reflect.TypeOf((*easyjson.Marshaler)(nil)).Elem() @@ -47,7 +93,10 @@ func (g *Generator) genTypeEncoder(t reflect.Type, in string, indent int) error } // Check whether type is primitive, needs to be done after interface check. - if enc := primitiveEncoders[t.Kind()]; enc != "" { + if enc := primitiveStringEncoders[t.Kind()]; enc != "" && tags.asString { + fmt.Fprintf(g.out, ws+enc+"\n", in) + return nil + } else if enc := primitiveEncoders[t.Kind()]; enc != "" { fmt.Fprintf(g.out, ws+enc+"\n", in) return nil } @@ -64,7 +113,7 @@ func (g *Generator) genTypeEncoder(t reflect.Type, in string, indent int) error fmt.Fprintln(g.out, ws+" out.RawByte(',')") fmt.Fprintln(g.out, ws+" }") - g.genTypeEncoder(elem, vVar, indent+1) + g.genTypeEncoder(elem, vVar, tags, indent+1) fmt.Fprintln(g.out, ws+"}") fmt.Fprintln(g.out, ws+"out.RawByte(']')") @@ -80,7 +129,7 @@ func (g *Generator) genTypeEncoder(t reflect.Type, in string, indent int) error fmt.Fprintln(g.out, ws+` out.RawString("null")`) fmt.Fprintln(g.out, ws+"} else {") - g.genTypeEncoder(t.Elem(), "*"+in, indent+1) + g.genTypeEncoder(t.Elem(), "*"+in, tags, indent+1) fmt.Fprintln(g.out, ws+"}") @@ -102,7 +151,7 @@ func (g *Generator) genTypeEncoder(t reflect.Type, in string, indent int) error fmt.Fprintln(g.out, ws+" out.String(string("+tmpVar+"_name))") fmt.Fprintln(g.out, ws+" out.RawByte(':')") - g.genTypeEncoder(t.Elem(), tmpVar+"_value", indent+2) + g.genTypeEncoder(t.Elem(), tmpVar+"_value", tags, indent+2) fmt.Fprintln(g.out, ws+" }") fmt.Fprintln(g.out, ws+" out.RawByte('}')") @@ -148,28 +197,16 @@ func (g *Generator) notEmptyCheck(t reflect.Type, v string) string { func (g *Generator) genStructFieldEncoder(t reflect.Type, f reflect.StructField) error { jsonName := g.namer.GetJSONFieldName(t, f) - omitEmpty := g.omitEmpty + tags := parseFieldTags(f) - for i, s := range strings.Split(f.Tag.Get("json"), ",") { - if i == 0 { - if s == "-" { - return nil - } - continue - } - - if s == "omitempty" { - omitEmpty = true - } else if s == "!omitempty" { - omitEmpty = false - } + if tags.omit { + return nil } - - if !omitEmpty { + 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, 1) + return g.genTypeEncoder(f.Type, "in."+f.Name, tags, 1) } fmt.Fprintln(g.out, " if", g.notEmptyCheck(f.Type, "in."+f.Name), "{") @@ -177,7 +214,7 @@ func (g *Generator) genStructFieldEncoder(t reflect.Type, f reflect.StructField) 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, 2); err != nil { + if err := g.genTypeEncoder(f.Type, "in."+f.Name, tags, 2); err != nil { return err } fmt.Fprintln(g.out, " }") diff --git a/jlexer/lexer.go b/jlexer/lexer.go index d4f5392..7c644e5 100644 --- a/jlexer/lexer.go +++ b/jlexer/lexer.go @@ -663,6 +663,94 @@ func (r *Lexer) Int() int { return int(r.Int64()) } +func (r *Lexer) Uint8Str() uint8 { + s := r.UnsafeString() + if !r.Ok() { + return 0 + } + var n uint64 + n, r.err = strconv.ParseUint(s, 10, 8) + return uint8(n) +} + +func (r *Lexer) Uint16Str() uint16 { + s := r.UnsafeString() + if !r.Ok() { + return 0 + } + var n uint64 + n, r.err = strconv.ParseUint(s, 10, 16) + return uint16(n) +} + +func (r *Lexer) Uint32Str() uint32 { + s := r.UnsafeString() + if !r.Ok() { + return 0 + } + var n uint64 + n, r.err = strconv.ParseUint(s, 10, 32) + return uint32(n) +} + +func (r *Lexer) Uint64Str() uint64 { + s := r.UnsafeString() + if !r.Ok() { + return 0 + } + var n uint64 + n, r.err = strconv.ParseUint(s, 10, 64) + return n +} + +func (r *Lexer) UintStr() uint { + return uint(r.Uint64Str()) +} + +func (r *Lexer) Int8Str() int8 { + s := r.UnsafeString() + if !r.Ok() { + return 0 + } + var n int64 + n, r.err = strconv.ParseInt(s, 10, 8) + return int8(n) +} + +func (r *Lexer) Int16Str() int16 { + s := r.UnsafeString() + if !r.Ok() { + return 0 + } + var n int64 + n, r.err = strconv.ParseInt(s, 10, 16) + return int16(n) +} + +func (r *Lexer) Int32Str() int32 { + s := r.UnsafeString() + if !r.Ok() { + return 0 + } + var n int64 + n, r.err = strconv.ParseInt(s, 10, 32) + return int32(n) +} + +func (r *Lexer) Int64Str() int64 { + s := r.UnsafeString() + if !r.Ok() { + return 0 + } + var n int64 + n, r.err = strconv.ParseInt(s, 10, 64) + return n +} + +func (r *Lexer) IntStr() int { + return int(r.Int64Str()) +} + func (r *Lexer) Float32() float32 { s := r.number() if !r.Ok() { diff --git a/jwriter/writer.go b/jwriter/writer.go index f6a3bd2..b0856ba 100644 --- a/jwriter/writer.go +++ b/jwriter/writer.go @@ -108,6 +108,76 @@ func (w *Writer) Int64(n int64) { w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, n, 10) } +func (w *Writer) Uint8Str(n uint8) { + w.Buffer.EnsureSpace(3) + w.Buffer.Buf = append(w.Buffer.Buf, '"') + w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) + w.Buffer.Buf = append(w.Buffer.Buf, '"') +} + +func (w *Writer) Uint16Str(n uint16) { + w.Buffer.EnsureSpace(5) + w.Buffer.Buf = append(w.Buffer.Buf, '"') + w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) + w.Buffer.Buf = append(w.Buffer.Buf, '"') +} + +func (w *Writer) Uint32Str(n uint32) { + w.Buffer.EnsureSpace(10) + w.Buffer.Buf = append(w.Buffer.Buf, '"') + w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) + w.Buffer.Buf = append(w.Buffer.Buf, '"') +} + +func (w *Writer) UintStr(n uint) { + w.Buffer.EnsureSpace(20) + w.Buffer.Buf = append(w.Buffer.Buf, '"') + w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) + w.Buffer.Buf = append(w.Buffer.Buf, '"') +} + +func (w *Writer) Uint64Str(n uint64) { + w.Buffer.EnsureSpace(20) + w.Buffer.Buf = append(w.Buffer.Buf, '"') + w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, n, 10) + w.Buffer.Buf = append(w.Buffer.Buf, '"') +} + +func (w *Writer) Int8Str(n int8) { + w.Buffer.EnsureSpace(4) + w.Buffer.Buf = append(w.Buffer.Buf, '"') + w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) + w.Buffer.Buf = append(w.Buffer.Buf, '"') +} + +func (w *Writer) Int16Str(n int16) { + w.Buffer.EnsureSpace(6) + w.Buffer.Buf = append(w.Buffer.Buf, '"') + w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) + w.Buffer.Buf = append(w.Buffer.Buf, '"') +} + +func (w *Writer) Int32Str(n int32) { + w.Buffer.EnsureSpace(11) + w.Buffer.Buf = append(w.Buffer.Buf, '"') + w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) + w.Buffer.Buf = append(w.Buffer.Buf, '"') +} + +func (w *Writer) IntStr(n int) { + w.Buffer.EnsureSpace(21) + w.Buffer.Buf = append(w.Buffer.Buf, '"') + w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) + w.Buffer.Buf = append(w.Buffer.Buf, '"') +} + +func (w *Writer) Int64Str(n int64) { + w.Buffer.EnsureSpace(21) + w.Buffer.Buf = append(w.Buffer.Buf, '"') + w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, n, 10) + w.Buffer.Buf = append(w.Buffer.Buf, '"') +} + func (w *Writer) Float32(n float32) { w.Buffer.EnsureSpace(20) w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 32) diff --git a/tests/data.go b/tests/data.go index 30d87d6..ee6af3c 100644 --- a/tests/data.go +++ b/tests/data.go @@ -25,6 +25,18 @@ type PrimitiveTypes struct { Uint32 uint32 Uint64 uint64 + IntString int `json:",string"` + Int8String int8 `json:",string"` + Int16String int16 `json:",string"` + Int32String int32 `json:",string"` + Int64String int64 `json:",string"` + + UintString uint `json:",string"` + Uint8String uint8 `json:",string"` + Uint16String uint16 `json:",string"` + Uint32String uint32 `json:",string"` + Uint64String uint64 `json:",string"` + Float32 float32 Float64 float64 @@ -49,6 +61,18 @@ var primitiveTypesValue = PrimitiveTypes{ Uint32: math.MaxUint32, Uint64: math.MaxUint64, + IntString: math.MinInt32, + Int8String: math.MinInt8, + Int16String: math.MinInt16, + Int32String: math.MinInt32, + Int64String: math.MinInt64, + + UintString: math.MaxUint32, + Uint8String: math.MaxUint8, + Uint16String: math.MaxUint16, + Uint32String: math.MaxUint32, + Uint64String: math.MaxUint64, + Float32: 1.5, Float64: math.MaxFloat64, @@ -70,6 +94,18 @@ var primitiveTypesString = "{" + `"Uint32":` + fmt.Sprint(math.MaxUint32) + `,` + `"Uint64":` + fmt.Sprint(uint64(math.MaxUint64)) + `,` + + `"IntString":"` + fmt.Sprint(math.MinInt32) + `",` + + `"Int8String":"` + fmt.Sprint(math.MinInt8) + `",` + + `"Int16String":"` + fmt.Sprint(math.MinInt16) + `",` + + `"Int32String":"` + fmt.Sprint(math.MinInt32) + `",` + + `"Int64String":"` + fmt.Sprint(int64(math.MinInt64)) + `",` + + + `"UintString":"` + fmt.Sprint(math.MaxUint32) + `",` + + `"Uint8String":"` + fmt.Sprint(math.MaxUint8) + `",` + + `"Uint16String":"` + fmt.Sprint(math.MaxUint16) + `",` + + `"Uint32String":"` + fmt.Sprint(math.MaxUint32) + `",` + + `"Uint64String":"` + fmt.Sprint(uint64(math.MaxUint64)) + `",` + + `"Float32":` + fmt.Sprint(1.5) + `,` + `"Float64":` + fmt.Sprint(math.MaxFloat64) + `,` +