Support json.Number

This commit is contained in:
Masaaki Goshima 2020-08-11 18:04:32 +09:00
parent f3d0d848f2
commit f9ffbb1940
7 changed files with 112 additions and 16 deletions

View File

@ -258,5 +258,5 @@ func (d *Decoder) InputOffset() int64 {
// UseNumber causes the Decoder to unmarshal a number into an interface{} as a
// Number instead of as a float64.
func (d *Decoder) UseNumber() {
d.s.useNumber = true
}

View File

@ -11,7 +11,20 @@ type interfaceDecoder struct {
}
func newInterfaceDecoder(typ *rtype) *interfaceDecoder {
return &interfaceDecoder{typ: typ}
return &interfaceDecoder{
typ: typ,
}
}
func (d *interfaceDecoder) numDecoder(s *stream) decoder {
if s.useNumber {
return newNumberDecoder(func(p uintptr, v Number) {
*(*interface{})(unsafe.Pointer(p)) = v
})
}
return newFloatDecoder(func(p uintptr, v float64) {
*(*interface{})(unsafe.Pointer(p)) = v
})
}
var (
@ -28,7 +41,11 @@ func (d *interfaceDecoder) decodeStream(s *stream, p uintptr) error {
var v map[interface{}]interface{}
ptr := unsafe.Pointer(&v)
d.dummy = ptr
dec := newMapDecoder(interfaceMapType, newInterfaceDecoder(d.typ), newInterfaceDecoder(d.typ))
dec := newMapDecoder(
interfaceMapType,
newInterfaceDecoder(d.typ),
newInterfaceDecoder(d.typ),
)
if err := dec.decodeStream(s, uintptr(ptr)); err != nil {
return err
}
@ -38,16 +55,18 @@ func (d *interfaceDecoder) decodeStream(s *stream, p uintptr) error {
var v []interface{}
ptr := unsafe.Pointer(&v)
d.dummy = ptr // escape ptr
dec := newSliceDecoder(newInterfaceDecoder(d.typ), d.typ, d.typ.Size())
dec := newSliceDecoder(
newInterfaceDecoder(d.typ),
d.typ,
d.typ.Size(),
)
if err := dec.decodeStream(s, uintptr(ptr)); err != nil {
return err
}
*(*interface{})(unsafe.Pointer(p)) = v
return nil
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
return newFloatDecoder(func(p uintptr, v float64) {
*(*interface{})(unsafe.Pointer(p)) = v
}).decodeStream(s, p)
return d.numDecoder(s).decodeStream(s, p)
case '"':
s.cursor++
start := s.cursor
@ -104,7 +123,11 @@ func (d *interfaceDecoder) decode(buf []byte, cursor int64, p uintptr) (int64, e
var v map[interface{}]interface{}
ptr := unsafe.Pointer(&v)
d.dummy = ptr
dec := newMapDecoder(interfaceMapType, newInterfaceDecoder(d.typ), newInterfaceDecoder(d.typ))
dec := newMapDecoder(
interfaceMapType,
newInterfaceDecoder(d.typ),
newInterfaceDecoder(d.typ),
)
cursor, err := dec.decode(buf, cursor, uintptr(ptr))
if err != nil {
return 0, err
@ -115,7 +138,11 @@ func (d *interfaceDecoder) decode(buf []byte, cursor int64, p uintptr) (int64, e
var v []interface{}
ptr := unsafe.Pointer(&v)
d.dummy = ptr // escape ptr
dec := newSliceDecoder(newInterfaceDecoder(d.typ), d.typ, d.typ.Size())
dec := newSliceDecoder(
newInterfaceDecoder(d.typ),
d.typ,
d.typ.Size(),
)
cursor, err := dec.decode(buf, cursor, uintptr(ptr))
if err != nil {
return 0, err

View File

@ -8,6 +8,7 @@ type mapDecoder struct {
mapType *rtype
keyDecoder decoder
valueDecoder decoder
dummy *interfaceHeader
}
func newMapDecoder(mapType *rtype, keyDec decoder, valueDec decoder) *mapDecoder {
@ -37,11 +38,13 @@ func (d *mapDecoder) setValue(buf []byte, cursor int64, key interface{}) (int64,
func (d *mapDecoder) setKeyStream(s *stream, key interface{}) error {
header := (*interfaceHeader)(unsafe.Pointer(&key))
d.dummy = header
return d.keyDecoder.decodeStream(s, uintptr(header.ptr))
}
func (d *mapDecoder) setValueStream(s *stream, key interface{}) error {
header := (*interfaceHeader)(unsafe.Pointer(&key))
d.dummy = header
return d.valueDecoder.decodeStream(s, uintptr(header.ptr))
}

38
decode_number.go Normal file
View File

@ -0,0 +1,38 @@
package json
import (
"unsafe"
)
type numberDecoder struct {
*floatDecoder
op func(uintptr, Number)
}
func newNumberDecoder(op func(uintptr, Number)) *numberDecoder {
return &numberDecoder{
floatDecoder: newFloatDecoder(nil),
op: op,
}
}
func (d *numberDecoder) decodeStream(s *stream, p uintptr) error {
bytes, err := d.floatDecoder.decodeStreamByte(s)
if err != nil {
return err
}
str := *(*string)(unsafe.Pointer(&bytes))
d.op(p, Number(str))
return nil
}
func (d *numberDecoder) decode(buf []byte, cursor int64, p uintptr) (int64, error) {
bytes, c, err := d.floatDecoder.decodeByte(buf, cursor)
if err != nil {
return 0, err
}
cursor = c
s := *(*string)(unsafe.Pointer(&bytes))
d.op(p, Number(s))
return cursor, nil
}

View File

@ -16,6 +16,7 @@ type stream struct {
offset int64
cursor int64
allRead bool
useNumber bool
}
func (s *stream) buffered() io.Reader {

View File

@ -217,6 +217,14 @@ func Test_Decoder(t *testing.T) {
})
}
func Test_Decoder_UseNumber(t *testing.T) {
dec := json.NewDecoder(strings.NewReader(`{"a": 3.14}`))
dec.UseNumber()
var v map[string]interface{}
assertErr(t, dec.Decode(&v))
assertEq(t, "json.Number", "json.Number", fmt.Sprintf("%T", v["a"]))
}
type unmarshalJSON struct {
v int
}

21
json.go
View File

@ -1,6 +1,9 @@
package json
import "bytes"
import (
"bytes"
"strconv"
)
// Marshaler is the interface implemented by types that
// can marshal themselves into valid JSON.
@ -275,3 +278,19 @@ func UnmarshalNoEscape(data []byte, v interface{}) error {
// nil, for JSON null
//
type Token interface{}
// A Number represents a JSON number literal.
type Number string
// String returns the literal text of the number.
func (n Number) String() string { return string(n) }
// Float64 returns the number as a float64.
func (n Number) Float64() (float64, error) {
return strconv.ParseFloat(string(n), 64)
}
// Int64 returns the number as an int64.
func (n Number) Int64() (int64, error) {
return strconv.ParseInt(string(n), 10, 64)
}