diff --git a/gen/decoder.go b/gen/decoder.go index 600bb32..f8f43e5 100644 --- a/gen/decoder.go +++ b/gen/decoder.go @@ -48,6 +48,10 @@ var primitiveStringDecoders = map[reflect.Kind]string{ reflect.Uint64: "in.Uint64Str()", } +var customDecoders = map[string]string{ + "json.Number": "in.JsonNumber()", +} + // 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) @@ -82,7 +86,10 @@ func (g *Generator) genTypeDecoder(t reflect.Type, out string, tags fieldTags, i 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 { + if dec := customDecoders[t.String()]; dec != "" { + fmt.Fprintln(g.out, ws+out+" = "+dec) + return nil + } else 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 != "" { diff --git a/jlexer/lexer.go b/jlexer/lexer.go index e81f103..563ca06 100644 --- a/jlexer/lexer.go +++ b/jlexer/lexer.go @@ -6,6 +6,7 @@ package jlexer import ( "encoding/base64" + "encoding/json" "errors" "fmt" "io" @@ -1043,6 +1044,28 @@ func (r *Lexer) GetNonFatalErrors() []*LexerError { return r.multipleErrors } +// JsonNumber fetches and json.Number from 'encoding/json' package. +// Both int, float or string, contains them are valid values +func (r *Lexer) JsonNumber() json.Number { + if r.token.kind == tokenUndef && r.Ok() { + r.FetchToken() + } + if !r.Ok() { + r.errInvalidToken("json.Number") + return json.Number("0") + } + + switch r.token.kind { + case tokenString: + return json.Number(r.String()) + case tokenNumber: + return json.Number(r.Raw()) + default: + r.errSyntax() + return json.Number("0") + } +} + // Interface fetches an interface{} analogous to the 'encoding/json' package. func (r *Lexer) Interface() interface{} { if r.token.kind == tokenUndef && r.Ok() { diff --git a/jlexer/lexer_test.go b/jlexer/lexer_test.go index b8a6498..4ce4abe 100644 --- a/jlexer/lexer_test.go +++ b/jlexer/lexer_test.go @@ -2,6 +2,7 @@ package jlexer import ( "bytes" + "encoding/json" "reflect" "testing" ) @@ -249,3 +250,62 @@ func TestConsumed(t *testing.T) { } } } + +func TestJsonNumber(t *testing.T) { + for i, test := range []struct { + toParse string + want json.Number + wantLexerError bool + wantValue interface{} + wantValueError bool + }{ + {toParse: `10`, want: json.Number("10"), wantValue: int64(10)}, + {toParse: `0`, want: json.Number("0"), wantValue: int64(0)}, + {toParse: `0.12`, want: json.Number("0.12"), wantValue: 0.12}, + {toParse: `25E-4`, want: json.Number("25E-4"), wantValue: 25E-4}, + + {toParse: `"10"`, want: json.Number("10"), wantValue: int64(10)}, + {toParse: `"0"`, want: json.Number("0"), wantValue: int64(0)}, + {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: `[1]`, wantLexerError: true}, + {toParse: `{}`, wantLexerError: true}, + {toParse: `a`, wantLexerError: true}, + } { + l := Lexer{Data: []byte(test.toParse)} + + got := l.JsonNumber() + if got != test.want && !test.wantLexerError && !test.wantValueError { + t.Errorf("[%d, %q] JsonNumber() = %v; want %v", i, test.toParse, got, test.want) + } + + err := l.Error() + if err != nil && !test.wantLexerError { + t.Errorf("[%d, %q] JsonNumber() lexer error: %v", i, test.toParse, err) + } else if err == nil && test.wantLexerError { + t.Errorf("[%d, %q] JsonNumber() ok; want lexer error", i, test.toParse) + } + + var valueErr error + var gotValue interface{} + switch test.wantValue.(type) { + case float64: + gotValue, valueErr = got.Float64() + default: + gotValue, valueErr = got.Int64() + } + + if !reflect.DeepEqual(gotValue, test.wantValue) && !test.wantLexerError && !test.wantValueError { + t.Errorf("[%d, %q] JsonNumber() = %v; want %v", i, test.toParse, gotValue, test.wantValue) + } + + if valueErr != nil && !test.wantValueError { + t.Errorf("[%d, %q] JsonNumber() value error: %v", i, test.toParse, err) + } else if valueErr == nil && test.wantValueError { + t.Errorf("[%d, %q] JsonNumber() ok; want value error", i, test.toParse) + } + } +}