1
0
mirror of https://github.com/json-iterator/go.git synced 2025-06-15 22:50:24 +02:00

42 Commits

Author SHA1 Message Date
45bbb40a9f #34 implement NewEncoder 2017-06-02 18:46:44 +08:00
e36f926072 fix random go test failure 2017-06-02 17:34:40 +08:00
59e71bacc8 #36 handle anonymous 2017-06-02 16:52:20 +08:00
5cb0d35610 eof is not error 2017-06-02 16:06:33 +08:00
69b742e73a #34 support More() and Buffered() 2017-06-02 16:00:12 +08:00
a7f992f0e1 #35 fix json.Number matches string 2017-06-02 15:43:58 +08:00
4cc44e7380 #34 add decoder adapter 2017-06-02 15:38:20 +08:00
5310d4aa9a syntax highlight 2017-06-02 11:20:54 +08:00
2051e3b8ae simplify readme 2017-06-02 11:20:24 +08:00
fe9fa8900e #31 support json.RawMessage 2017-06-02 10:50:23 +08:00
ad3a7fde32 #30 support json.Number 2017-06-02 10:21:43 +08:00
377b892102 support big float and int 2017-05-31 12:40:50 +08:00
707ed3b091 support non empty interface 2017-05-27 00:36:21 +08:00
a7a7c7879a allocate less buffer for writing to []byte 2017-05-26 07:52:54 +08:00
f20f74519d RegisterTypeEncoder and RegisterTypeDecoder should have higher priority 2017-05-24 23:39:34 +08:00
7d2ae80c37 #27 support json.Unmarshaler 2017-05-24 16:04:11 +08:00
f6f159e108 #27 support json.Marshaler 2017-05-24 14:34:00 +08:00
e5a1e704ad #25 make fielding binding case insensitive 2017-05-24 13:16:09 +08:00
7d5f90261e #28 extension should support specifying encoder 2017-05-24 10:58:56 +08:00
6126a6d3ca #23 hide unexported fields by default 2017-05-24 09:39:11 +08:00
5fbe4e387d #21 #22 marshal float precisely by default 2017-05-24 09:08:15 +08:00
fc44cb2d91 #26 do not enforce pointer as optional 2017-05-23 18:46:11 +08:00
7e046e6aa7 simplify read string, and support null 2017-05-23 18:32:39 +08:00
5488fde97f fix one field struct interface{} optimization compatibility 2017-05-23 17:44:50 +08:00
53f8d370b5 fix wrap any 2017-05-19 19:44:27 +08:00
3f1fcaff87 demonstrate how to customize float encoding 2017-05-11 08:00:50 +08:00
1df353727b customize []byte encoder 2017-05-06 20:52:36 +08:00
b893a0359d trim end space 2017-05-05 17:44:09 +08:00
a92111261c fix struct with one pointer field 2017-05-05 17:27:41 +08:00
91b9e828b7 support recursive type 2017-05-05 16:51:05 +08:00
6bd835aeb1 test type encoder 2017-05-05 08:22:19 +08:00
90888390bc fix readFloat64SlowPath not advancing the cursor 2017-05-02 10:15:21 +08:00
ccb972f58c merge 2017-04-28 09:10:06 +08:00
8711c74c85 support Any as field type 2017-04-28 09:09:24 +08:00
abcf2759ed Merge pull request #14 from eruca/master
fix the omitempty bug
2017-04-18 10:23:05 +10:00
e5476f70e7 #16 fix slice of map 2017-04-16 14:05:08 +08:00
b986d86f26 add test 2017-03-11 18:19:39 +08:00
9a138f8b6a fix bug of another comma added
previous code will add comma if the field is empty when is not first, like 
```
{"account":"1120","name":"nick",,"department":"fsaf","role":1,"privilege":32,,"created_at":1489226500}
```
2017-03-11 18:17:34 +08:00
d1aa59e34e #12 implement omitempty 2017-03-08 07:38:25 -08:00
ceb8c8a733 create map if nil 2017-03-07 18:36:58 -08:00
62028f1ede Merge pull request #11 from Kisesy/patch-1
fix #10
2017-02-26 09:42:45 -06:00
696f962eda fix https://github.com/json-iterator/go/issues/10
Update feature_iter_string.go

Update feature_iter_string.go
2017-02-25 13:30:55 +08:00
26 changed files with 1388 additions and 482 deletions

View File

@ -2,61 +2,38 @@
jsoniter (json-iterator) is fast and flexible JSON parser available in [Java](https://github.com/json-iterator/java) and [Go](https://github.com/json-iterator/go)
# Why jsoniter?
# Usage
* Jsoniter is the fastest JSON parser. It could be up to 10x faster than normal parser, data binding included. Shameless self [benchmark](http://jsoniter.com/benchmark.html)
* Extremely flexible api. You can mix and match three different styles: bind-api, any-api or iterator-api. Checkout your [api choices](http://jsoniter.com/api.html)
* Unique iterator api can iterate through JSON directly, zero memory allocation! See how [iterator](http://jsoniter.com/api.html#iterator-api) works
100% compatibility with standard lib
# Show off
Replace
Here is a quick show off, for more complete report you can checkout the full [benchmark](http://jsoniter.com/benchmark.html) with [in-depth optimization](http://jsoniter.com/benchmark.html#optimization-used) to back the numbers up
```go
import "encoding/json"
json.Marshal(&data)
```
![go-medium](http://jsoniter.com/benchmarks/go-medium.png)
# Bind-API is the best
Bind-api should always be the first choice. Given this JSON document `[0,1,2,3]`
Parse with Go bind-api
with
```go
import "github.com/json-iterator/go"
iter := jsoniter.ParseString(`[0,1,2,3]`)
var := iter.Read()
fmt.Println(val)
jsoniter.Marshal(&data)
```
# Iterator-API for quick extraction
Replace
When you do not need to get all the data back, just extract some.
```go
import "encoding/json"
json.Unmarshal(input, &data)
```
Parse with Go iterator-api
with
```go
import "github.com/json-iterator/go"
iter := ParseString(`[0, [1, 2], [3, 4], 5]`)
count := 0
for iter.ReadArray() {
iter.Skip()
count++
}
fmt.Println(count) // 4
jsoniter.Unmarshal(input, &data)
```
# Any-API for maximum flexibility
Parse with Go any-api
```go
import "github.com/json-iterator/go"
iter := jsoniter.ParseString(`[{"field1":"11","field2":"12"},{"field1":"21","field2":"22"}]`)
val := iter.ReadAny()
fmt.Println(val.ToInt(1, "field2")) // 22
```
Notice you can extract from nested data structure, and convert any type to the type to you want.
# How to get
```

View File

@ -7,6 +7,7 @@ import (
// Unmarshal adapts to json/encoding APIs
func Unmarshal(data []byte, v interface{}) error {
data = data[:lastNotSpacePos(data)]
iter := ParseBytes(data)
iter.ReadVal(v)
if iter.head == iter.tail {
@ -22,6 +23,7 @@ func Unmarshal(data []byte, v interface{}) error {
}
func UnmarshalAny(data []byte) (Any, error) {
data = data[:lastNotSpacePos(data)]
iter := ParseBytes(data)
any := iter.ReadAny()
if iter.head == iter.tail {
@ -36,8 +38,18 @@ func UnmarshalAny(data []byte) (Any, error) {
return any, iter.Error
}
func lastNotSpacePos(data []byte) int {
for i := len(data) - 1; i >= 0; i-- {
if data[i] != ' ' && data[i] != '\t' && data[i] != '\r' && data[i] != '\n' {
return i + 1
}
}
return 0
}
func UnmarshalFromString(str string, v interface{}) error {
data := []byte(str)
data = data[:lastNotSpacePos(data)]
iter := ParseBytes(data)
iter.ReadVal(v)
if iter.head == iter.tail {
@ -54,6 +66,7 @@ func UnmarshalFromString(str string, v interface{}) error {
func UnmarshalAnyFromString(str string) (Any, error) {
data := []byte(str)
data = data[:lastNotSpacePos(data)]
iter := ParseBytes(data)
any := iter.ReadAny()
if iter.head == iter.tail {
@ -70,7 +83,7 @@ func UnmarshalAnyFromString(str string) (Any, error) {
func Marshal(v interface{}) ([]byte, error) {
buf := &bytes.Buffer{}
stream := NewStream(buf, 4096)
stream := NewStream(buf, 512)
stream.WriteVal(v)
stream.Flush()
if stream.Error != nil {
@ -85,4 +98,50 @@ func MarshalToString(v interface{}) (string, error) {
return "", err
}
return string(buf), nil
}
func NewDecoder(reader io.Reader) *AdaptedDecoder {
iter := Parse(reader, 512)
return &AdaptedDecoder{iter}
}
type AdaptedDecoder struct {
iter *Iterator
}
func (adapter *AdaptedDecoder) Decode(obj interface{}) error {
adapter.iter.ReadVal(obj)
err := adapter.iter.Error
if err == io.EOF {
return nil
}
return adapter.iter.Error
}
func (adapter *AdaptedDecoder) More() bool {
return adapter.iter.head != adapter.iter.tail
}
func (adapter *AdaptedDecoder) Buffered() io.Reader {
remaining := adapter.iter.buf[adapter.iter.head:adapter.iter.tail]
return bytes.NewReader(remaining)
}
func NewEncoder(writer io.Writer) *AdaptedEncoder {
stream := NewStream(writer, 512)
return &AdaptedEncoder{stream}
}
type AdaptedEncoder struct {
stream *Stream
}
func (adapter *AdaptedEncoder) Encode(val interface{}) error {
adapter.stream.WriteVal(val)
adapter.stream.Flush()
return adapter.stream.Error
}
func (adapter *AdaptedEncoder) SetIndent(prefix, indent string) {
// not implemented yet
}

View File

@ -98,6 +98,10 @@ func Wrap(val interface{}) Any {
if val == nil {
return &nilAny{}
}
asAny, isAny := val.(Any)
if isAny {
return asAny
}
type_ := reflect.TypeOf(val)
switch type_.Kind() {
case reflect.Slice:

View File

@ -322,6 +322,7 @@ func (any *objectLazyAny) IterateObject() (func() (string, Any, bool), bool) {
any.err = iter.Error
return key, value, true
} else {
nextKey = ""
remaining = nil
any.remaining = nil
any.err = iter.Error

View File

@ -4,6 +4,7 @@ import (
"io"
"strconv"
"unsafe"
"math/big"
)
var floatDigits []int8
@ -19,11 +20,45 @@ func init() {
for i := int8('0'); i <= int8('9'); i++ {
floatDigits[i] = i - int8('0')
}
floatDigits[','] = endOfNumber;
floatDigits[']'] = endOfNumber;
floatDigits['}'] = endOfNumber;
floatDigits[' '] = endOfNumber;
floatDigits['.'] = dotInNumber;
floatDigits[','] = endOfNumber
floatDigits[']'] = endOfNumber
floatDigits['}'] = endOfNumber
floatDigits[' '] = endOfNumber
floatDigits['\t'] = endOfNumber
floatDigits['\n'] = endOfNumber
floatDigits['.'] = dotInNumber
}
func (iter *Iterator) ReadBigFloat() (ret *big.Float) {
str := iter.readNumberAsString()
if iter.Error != nil && iter.Error != io.EOF {
return nil
}
prec := 64
if len(str) > prec {
prec = len(str)
}
val, _, err := big.ParseFloat(str, 10, uint(prec), big.ToZero)
if err != nil {
iter.Error = err
return nil
}
return val
}
func (iter *Iterator) ReadBigInt() (ret *big.Int) {
str := iter.readNumberAsString()
if iter.Error != nil && iter.Error != io.EOF {
return nil
}
ret = big.NewInt(0)
var success bool
ret, success = ret.SetString(str, 10)
if !success {
iter.reportError("ReadBigInt", "invalid big int")
return nil
}
return ret
}
func (iter *Iterator) ReadFloat32() (ret float32) {
@ -87,7 +122,7 @@ func (iter *Iterator) readPositiveFloat32() (ret float32) {
return iter.readFloat32SlowPath()
}
func (iter *Iterator) readFloat32SlowPath() (ret float32) {
func (iter *Iterator) readNumberAsString() (ret string) {
strBuf := [16]byte{}
str := strBuf[0:0]
load_loop:
@ -99,6 +134,7 @@ func (iter *Iterator) readFloat32SlowPath() (ret float32) {
str = append(str, c)
continue
default:
iter.head = i
break load_loop
}
}
@ -109,7 +145,18 @@ func (iter *Iterator) readFloat32SlowPath() (ret float32) {
if iter.Error != nil && iter.Error != io.EOF {
return
}
val, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&str)), 32)
if len(str) == 0 {
iter.reportError("readNumberAsString", "invalid number")
}
return *(*string)(unsafe.Pointer(&str))
}
func (iter *Iterator) readFloat32SlowPath() (ret float32) {
str := iter.readNumberAsString()
if iter.Error != nil && iter.Error != io.EOF {
return
}
val, err := strconv.ParseFloat(str, 32)
if err != nil {
iter.Error = err
return
@ -179,28 +226,11 @@ func (iter *Iterator) readPositiveFloat64() (ret float64) {
}
func (iter *Iterator) readFloat64SlowPath() (ret float64) {
strBuf := [16]byte{}
str := strBuf[0:0]
load_loop:
for {
for i := iter.head; i < iter.tail; i++ {
c := iter.buf[i]
switch c {
case '-', '.', 'e', 'E', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
str = append(str, c)
continue
default:
break load_loop
}
}
if !iter.loadMore() {
break
}
}
str := iter.readNumberAsString()
if iter.Error != nil && iter.Error != io.EOF {
return
}
val, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&str)), 64)
val, err := strconv.ParseFloat(str, 64)
if err != nil {
iter.Error = err
return

View File

@ -1,5 +1,10 @@
package jsoniter
import (
"fmt"
"unicode"
)
func (iter *Iterator) ReadObject() (ret string) {
c := iter.nextToken()
switch c {
@ -22,7 +27,7 @@ func (iter *Iterator) ReadObject() (ret string) {
case '}':
return "" // end of object
default:
iter.reportError("ReadObject", `expect { or , or } or n`)
iter.reportError("ReadObject", fmt.Sprintf(`expect { or , or } or n, but found %s`, string([]byte{c})))
return
}
}
@ -35,6 +40,9 @@ func (iter *Iterator) readFieldHash() int32 {
for i := iter.head; i < iter.tail; i++ {
// require ascii string and no escape
b := iter.buf[i]
if 'A' <= b && b <= 'Z' {
b += 'a' - 'A'
}
if b == '"' {
iter.head = i+1
c = iter.nextToken()
@ -59,7 +67,7 @@ func (iter *Iterator) readFieldHash() int32 {
func calcHash(str string) int32 {
hash := int64(0x811c9dc5)
for _, b := range str {
hash ^= int64(b)
hash ^= int64(unicode.ToLower(b))
hash *= 0x1000193
}
return int32(hash)
@ -106,8 +114,11 @@ func (iter *Iterator) readObjectStart() bool {
}
iter.unreadByte()
return true
} else if c == 'n' {
iter.skipFixedBytes(3)
return false
}
iter.reportError("readObjectStart", "expect { ")
iter.reportError("readObjectStart", "expect { or n")
return false
}

View File

@ -30,6 +30,17 @@ func (iter *Iterator) ReadBool() (ret bool) {
}
func (iter *Iterator) SkipAndReturnBytes() []byte {
if iter.reader != nil {
panic("reader input does not support this api")
}
before := iter.head
iter.Skip()
after := iter.head
return iter.buf[before:after]
}
// Skip skips a json object and positions to relatively the next json object
func (iter *Iterator) Skip() {
c := iter.nextToken()

View File

@ -2,64 +2,42 @@ package jsoniter
import (
"unicode/utf16"
"unsafe"
)
// TODO: avoid append
func (iter *Iterator) ReadString() (ret string) {
c := iter.nextToken()
if c == '"' {
copied := make([]byte, 32)
j := 0
fast_loop:
for {
i := iter.head
for ; i < iter.tail && j < len(copied); i++ {
c := iter.buf[i]
if c == '"' {
iter.head = i + 1
copied = copied[:j]
return *(*string)(unsafe.Pointer(&copied))
} else if c == '\\' {
iter.head = i
break fast_loop
}
copied[j] = c
j++
}
if i == iter.tail {
if iter.loadMore() {
i = iter.head
continue
} else {
iter.reportError("ReadString", "incomplete string")
return
}
}
iter.head = i
if j == len(copied) {
newBuf := make([]byte, len(copied) * 2)
copy(newBuf, copied)
copied = newBuf
for i := iter.head ; i < iter.tail; i++ {
c := iter.buf[i]
if c == '"' {
ret = string(iter.buf[iter.head:i])
iter.head = i + 1
return ret
} else if c == '\\' {
break
}
}
return iter.readStringSlowPath(copied[:j])
return iter.readStringSlowPath()
} else if c == 'n' {
iter.skipFixedBytes(3)
return ""
}
iter.reportError("ReadString", `expects " or n`)
return
}
func (iter *Iterator) readStringSlowPath(str []byte) (ret string) {
func (iter *Iterator) readStringSlowPath() (ret string) {
var str []byte
var c byte
for iter.Error == nil {
c = iter.readByte()
if c == '"' {
return *(*string)(unsafe.Pointer(&str))
return string(str)
}
if c == '\\' {
c = iter.readByte()
switch c {
case 'u':
case 'u', 'U':
r := iter.readU4()
if utf16.IsSurrogate(r) {
c = iter.readByte()
@ -75,7 +53,7 @@ func (iter *Iterator) readStringSlowPath(str []byte) (ret string) {
if iter.Error != nil {
return
}
if c != 'u' {
if c != 'u' && c != 'U' {
iter.reportError("ReadString",
`expects \u after utf16 surrogate, but \u not found`)
return
@ -157,6 +135,8 @@ func (iter *Iterator) readU4() (ret rune) {
ret = ret * 16 + rune(c - '0')
} else if c >= 'a' && c <= 'f' {
ret = ret * 16 + rune(c - 'a' + 10)
} else if c >= 'A' && c <= 'F' {
ret = ret * 16 + rune(c - 'A' + 10)
} else {
iter.reportError("readU4", "expects 0~9 or a~f")
return
@ -214,4 +194,4 @@ func appendRune(p []byte, r rune) []byte {
p = append(p, tx | byte(r) & maskx)
return p
}
}
}

View File

@ -5,7 +5,7 @@ import (
"reflect"
"sync/atomic"
"unsafe"
"errors"
"encoding/json"
)
/*
@ -22,11 +22,12 @@ type Decoder interface {
}
type Encoder interface {
isEmpty(ptr unsafe.Pointer) bool
encode(ptr unsafe.Pointer, stream *Stream)
encodeInterface(val interface{}, stream *Stream)
}
func WriteToStream(val interface{}, stream *Stream, encoder Encoder) {
func writeToStream(val interface{}, stream *Stream, encoder Encoder) {
e := (*emptyInterface)(unsafe.Pointer(&val))
if reflect.TypeOf(val).Kind() == reflect.Ptr {
encoder.encode(unsafe.Pointer(&e.word), stream)
@ -37,7 +38,7 @@ func WriteToStream(val interface{}, stream *Stream, encoder Encoder) {
type DecoderFunc func(ptr unsafe.Pointer, iter *Iterator)
type EncoderFunc func(ptr unsafe.Pointer, stream *Stream)
type ExtensionFunc func(typ reflect.Type, field *reflect.StructField) ([]string, DecoderFunc)
type ExtensionFunc func(typ reflect.Type, field *reflect.StructField) ([]string, EncoderFunc, DecoderFunc)
type funcDecoder struct {
fun DecoderFunc
@ -56,7 +57,11 @@ func (encoder *funcEncoder) encode(ptr unsafe.Pointer, stream *Stream) {
}
func (encoder *funcEncoder) encodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
writeToStream(val, stream, encoder)
}
func (encoder *funcEncoder) isEmpty(ptr unsafe.Pointer) bool {
return false
}
var DECODERS unsafe.Pointer
@ -67,7 +72,11 @@ var fieldDecoders map[string]Decoder
var typeEncoders map[string]Encoder
var fieldEncoders map[string]Encoder
var extensions []ExtensionFunc
var jsonNumberType reflect.Type
var jsonRawMessageType reflect.Type
var anyType reflect.Type
var marshalerType reflect.Type
var unmarshalerType reflect.Type
func init() {
typeDecoders = map[string]Decoder{}
@ -77,7 +86,11 @@ func init() {
extensions = []ExtensionFunc{}
atomic.StorePointer(&DECODERS, unsafe.Pointer(&map[string]Decoder{}))
atomic.StorePointer(&ENCODERS, unsafe.Pointer(&map[string]Encoder{}))
jsonNumberType = reflect.TypeOf((*json.Number)(nil)).Elem()
jsonRawMessageType = reflect.TypeOf((*json.RawMessage)(nil)).Elem()
anyType = reflect.TypeOf((*Any)(nil)).Elem()
marshalerType = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
unmarshalerType = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()
}
func addDecoderToCache(cacheKey reflect.Type, decoder Decoder) {
@ -149,6 +162,11 @@ func CleanDecoders() {
fieldDecoders = map[string]Decoder{}
}
func CleanEncoders() {
typeEncoders = map[string]Encoder{}
fieldEncoders = map[string]Encoder{}
}
type optionalDecoder struct {
valueType reflect.Type
valueDecoder Decoder
@ -171,7 +189,6 @@ func (decoder *optionalDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
}
type optionalEncoder struct {
valueType reflect.Type
valueEncoder Encoder
}
@ -184,92 +201,58 @@ func (encoder *optionalEncoder) encode(ptr unsafe.Pointer, stream *Stream) {
}
func (encoder *optionalEncoder) encodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
writeToStream(val, stream, encoder)
}
type mapDecoder struct {
mapType reflect.Type
elemType reflect.Type
elemDecoder Decoder
mapInterface emptyInterface
}
func (decoder *mapDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
// dark magic to cast unsafe.Pointer back to interface{} using reflect.Type
mapInterface := decoder.mapInterface
mapInterface.word = ptr
realInterface := (*interface{})(unsafe.Pointer(&mapInterface))
realVal := reflect.ValueOf(*realInterface).Elem()
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
elem := reflect.New(decoder.elemType)
decoder.elemDecoder.decode(unsafe.Pointer(elem.Pointer()), iter)
// to put into map, we have to use reflection
realVal.SetMapIndex(reflect.ValueOf(string([]byte(field))), elem.Elem())
func (encoder *optionalEncoder) isEmpty(ptr unsafe.Pointer) bool {
if *((*unsafe.Pointer)(ptr)) == nil {
return true
} else {
return encoder.valueEncoder.isEmpty(*((*unsafe.Pointer)(ptr)))
}
}
type mapEncoder struct {
mapType reflect.Type
elemType reflect.Type
elemEncoder Encoder
mapInterface emptyInterface
type placeholderEncoder struct {
valueEncoder Encoder
}
func (encoder *mapEncoder) encode(ptr unsafe.Pointer, stream *Stream) {
mapInterface := encoder.mapInterface
mapInterface.word = ptr
realInterface := (*interface{})(unsafe.Pointer(&mapInterface))
realVal := reflect.ValueOf(*realInterface)
stream.WriteObjectStart()
for i, key := range realVal.MapKeys() {
if i != 0 {
stream.WriteMore()
}
stream.WriteObjectField(key.String())
val := realVal.MapIndex(key).Interface()
encoder.elemEncoder.encodeInterface(val, stream)
}
stream.WriteObjectEnd()
func (encoder *placeholderEncoder) encode(ptr unsafe.Pointer, stream *Stream) {
encoder.valueEncoder.encode(ptr, stream)
}
func (encoder *mapEncoder) encodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
func (encoder *placeholderEncoder) encodeInterface(val interface{}, stream *Stream) {
writeToStream(val, stream, encoder)
}
type mapInterfaceEncoder struct {
mapType reflect.Type
elemType reflect.Type
elemEncoder Encoder
mapInterface emptyInterface
func (encoder *placeholderEncoder) isEmpty(ptr unsafe.Pointer) bool {
return encoder.valueEncoder.isEmpty(ptr)
}
func (encoder *mapInterfaceEncoder) encode(ptr unsafe.Pointer, stream *Stream) {
mapInterface := encoder.mapInterface
mapInterface.word = ptr
realInterface := (*interface{})(unsafe.Pointer(&mapInterface))
realVal := reflect.ValueOf(*realInterface)
stream.WriteObjectStart()
for i, key := range realVal.MapKeys() {
if i != 0 {
stream.WriteMore()
}
stream.WriteObjectField(key.String())
val := realVal.MapIndex(key).Interface()
encoder.elemEncoder.encode(unsafe.Pointer(&val), stream)
}
stream.WriteObjectEnd()
type placeholderDecoder struct {
valueDecoder Decoder
}
func (encoder *mapInterfaceEncoder) encodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
func (decoder *placeholderDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
decoder.valueDecoder.decode(ptr, iter)
}
// emptyInterface is the header for an interface{} value.
type emptyInterface struct {
typ *struct{}
typ unsafe.Pointer
word unsafe.Pointer
}
// emptyInterface is the header for an interface with method (not interface{})
type nonEmptyInterface struct {
// see ../runtime/iface.go:/Itab
itab *struct {
ityp unsafe.Pointer // static interface type
typ unsafe.Pointer // dynamic concrete type
link unsafe.Pointer
bad int32
unused int32
fun [100000]unsafe.Pointer // method table
}
word unsafe.Pointer
}
@ -334,6 +317,39 @@ func decoderOfType(typ reflect.Type) (Decoder, error) {
if typeDecoder != nil {
return typeDecoder, nil
}
if typ.Kind() == reflect.Ptr {
typeDecoder := typeDecoders[typ.Elem().String()]
if typeDecoder != nil {
return &optionalDecoder{typ.Elem(),typeDecoder}, nil
}
}
cacheKey := typ
cachedDecoder := getDecoderFromCache(cacheKey)
if cachedDecoder != nil {
return cachedDecoder, nil
}
placeholder := &placeholderDecoder{}
addDecoderToCache(cacheKey, placeholder)
newDecoder, err := createDecoderOfType(typ)
placeholder.valueDecoder = newDecoder
addDecoderToCache(cacheKey, newDecoder)
return newDecoder, err
}
func createDecoderOfType(typ reflect.Type) (Decoder, error) {
if typ.AssignableTo(jsonRawMessageType) {
return &jsonRawMessageCodec{}, nil
}
if typ.AssignableTo(jsonNumberType) {
return &jsonNumberCodec{}, nil
}
if typ.ConvertibleTo(unmarshalerType) {
templateInterface := reflect.New(typ).Elem().Interface()
return &optionalDecoder{typ, &unmarshalerDecoder{extractInterface(templateInterface)}}, nil
}
if typ.ConvertibleTo(anyType) {
return &anyCodec{}, nil
}
switch typ.Kind() {
case reflect.String:
return &stringCodec{}, nil
@ -365,12 +381,12 @@ func decoderOfType(typ reflect.Type) (Decoder, error) {
return &boolCodec{}, nil
case reflect.Interface:
if typ.NumMethod() == 0 {
return &interfaceCodec{}, nil
return &emptyInterfaceCodec{}, nil
} else {
return nil, errors.New("unsupportd type: " + typ.String())
return &nonEmptyInterfaceCodec{}, nil
}
case reflect.Struct:
return prefix(fmt.Sprintf("[%s]", typeName)).addToDecoder(decoderOfStruct(typ))
return prefix(fmt.Sprintf("[%s]", typ.String())).addToDecoder(decoderOfStruct(typ))
case reflect.Slice:
return prefix("[slice]").addToDecoder(decoderOfSlice(typ))
case reflect.Map:
@ -383,15 +399,46 @@ func decoderOfType(typ reflect.Type) (Decoder, error) {
}
func encoderOfType(typ reflect.Type) (Encoder, error) {
if typ.ConvertibleTo(anyType) {
return &anyCodec{}, nil
}
typeName := typ.String()
typeEncoder := typeEncoders[typeName]
if typeEncoder != nil {
return typeEncoder, nil
}
switch typ.Kind() {
if typ.Kind() == reflect.Ptr {
typeEncoder := typeEncoders[typ.Elem().String()]
if typeEncoder != nil {
return &optionalEncoder{typeEncoder}, nil
}
}
cacheKey := typ
cachedEncoder := getEncoderFromCache(cacheKey)
if cachedEncoder != nil {
return cachedEncoder, nil
}
placeholder := &placeholderEncoder{}
addEncoderToCache(cacheKey, placeholder)
newEncoder, err := createEncoderOfType(typ)
placeholder.valueEncoder = newEncoder
addEncoderToCache(cacheKey, newEncoder)
return newEncoder, err
}
func createEncoderOfType(typ reflect.Type) (Encoder, error) {
if typ.AssignableTo(jsonRawMessageType) {
return &jsonRawMessageCodec{}, nil
}
if typ.AssignableTo(jsonNumberType) {
return &jsonNumberCodec{}, nil
}
if typ.ConvertibleTo(marshalerType) {
templateInterface := reflect.New(typ).Elem().Interface()
return &marshalerEncoder{extractInterface(templateInterface)}, nil
}
if typ.ConvertibleTo(anyType) {
return &anyCodec{}, nil
}
kind := typ.Kind()
switch kind {
case reflect.String:
return &stringCodec{}, nil
case reflect.Int:
@ -421,9 +468,13 @@ func encoderOfType(typ reflect.Type) (Encoder, error) {
case reflect.Bool:
return &boolCodec{}, nil
case reflect.Interface:
return &interfaceCodec{}, nil
if typ.NumMethod() == 0 {
return &emptyInterfaceCodec{}, nil
} else {
return &nonEmptyInterfaceCodec{}, nil
}
case reflect.Struct:
return prefix(fmt.Sprintf("[%s]", typeName)).addToEncoder(encoderOfStruct(typ))
return prefix(fmt.Sprintf("[%s]", typ.String())).addToEncoder(encoderOfStruct(typ))
case reflect.Slice:
return prefix("[slice]").addToEncoder(encoderOfSlice(typ))
case reflect.Map:
@ -450,7 +501,7 @@ func encoderOfOptional(typ reflect.Type) (Encoder, error) {
if err != nil {
return nil, err
}
return &optionalEncoder{elemType, decoder}, nil
return &optionalEncoder{ decoder}, nil
}
func decoderOfMap(typ reflect.Type) (Decoder, error) {
@ -459,7 +510,11 @@ func decoderOfMap(typ reflect.Type) (Decoder, error) {
return nil, err
}
mapInterface := reflect.New(typ).Interface()
return &mapDecoder{typ, typ.Elem(), decoder, *((*emptyInterface)(unsafe.Pointer(&mapInterface)))}, nil
return &mapDecoder{typ, typ.Elem(), decoder, extractInterface(mapInterface)}, nil
}
func extractInterface(val interface{}) emptyInterface {
return *((*emptyInterface)(unsafe.Pointer(&val)))
}
func encoderOfMap(typ reflect.Type) (Encoder, error) {

View File

@ -20,6 +20,9 @@ func encoderOfSlice(typ reflect.Type) (Encoder, error) {
if err != nil {
return nil, err
}
if typ.Elem().Kind() == reflect.Map {
encoder = &optionalEncoder{ encoder}
}
return &sliceEncoder{typ, typ.Elem(), encoder}, nil
}
@ -50,7 +53,12 @@ func (encoder *sliceEncoder) encode(ptr unsafe.Pointer, stream *Stream) {
}
func (encoder *sliceEncoder) encodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
writeToStream(val, stream, encoder)
}
func (encoder *sliceEncoder) isEmpty(ptr unsafe.Pointer) bool {
slice := (*sliceHeader)(ptr)
return slice.Len == 0
}
type sliceDecoder struct {

105
feature_reflect_map.go Normal file
View File

@ -0,0 +1,105 @@
package jsoniter
import (
"unsafe"
"reflect"
)
type mapDecoder struct {
mapType reflect.Type
elemType reflect.Type
elemDecoder Decoder
mapInterface emptyInterface
}
func (decoder *mapDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
// dark magic to cast unsafe.Pointer back to interface{} using reflect.Type
mapInterface := decoder.mapInterface
mapInterface.word = ptr
realInterface := (*interface{})(unsafe.Pointer(&mapInterface))
realVal := reflect.ValueOf(*realInterface).Elem()
if realVal.IsNil() {
realVal.Set(reflect.MakeMap(realVal.Type()))
}
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
elem := reflect.New(decoder.elemType)
decoder.elemDecoder.decode(unsafe.Pointer(elem.Pointer()), iter)
// to put into map, we have to use reflection
realVal.SetMapIndex(reflect.ValueOf(string([]byte(field))), elem.Elem())
}
}
type mapEncoder struct {
mapType reflect.Type
elemType reflect.Type
elemEncoder Encoder
mapInterface emptyInterface
}
func (encoder *mapEncoder) encode(ptr unsafe.Pointer, stream *Stream) {
mapInterface := encoder.mapInterface
mapInterface.word = ptr
realInterface := (*interface{})(unsafe.Pointer(&mapInterface))
realVal := reflect.ValueOf(*realInterface)
stream.WriteObjectStart()
for i, key := range realVal.MapKeys() {
if i != 0 {
stream.WriteMore()
}
stream.WriteObjectField(key.String())
val := realVal.MapIndex(key).Interface()
encoder.elemEncoder.encodeInterface(val, stream)
}
stream.WriteObjectEnd()
}
func (encoder *mapEncoder) encodeInterface(val interface{}, stream *Stream) {
writeToStream(val, stream, encoder)
}
func (encoder *mapEncoder) isEmpty(ptr unsafe.Pointer) bool {
mapInterface := encoder.mapInterface
mapInterface.word = ptr
realInterface := (*interface{})(unsafe.Pointer(&mapInterface))
realVal := reflect.ValueOf(*realInterface)
return realVal.Len() == 0
}
type mapInterfaceEncoder struct {
mapType reflect.Type
elemType reflect.Type
elemEncoder Encoder
mapInterface emptyInterface
}
func (encoder *mapInterfaceEncoder) encode(ptr unsafe.Pointer, stream *Stream) {
mapInterface := encoder.mapInterface
mapInterface.word = ptr
realInterface := (*interface{})(unsafe.Pointer(&mapInterface))
realVal := reflect.ValueOf(*realInterface)
stream.WriteObjectStart()
for i, key := range realVal.MapKeys() {
if i != 0 {
stream.WriteMore()
}
stream.WriteObjectField(key.String())
val := realVal.MapIndex(key).Interface()
encoder.elemEncoder.encode(unsafe.Pointer(&val), stream)
}
stream.WriteObjectEnd()
}
func (encoder *mapInterfaceEncoder) encodeInterface(val interface{}, stream *Stream) {
writeToStream(val, stream, encoder)
}
func (encoder *mapInterfaceEncoder) isEmpty(ptr unsafe.Pointer) bool {
mapInterface := encoder.mapInterface
mapInterface.word = ptr
realInterface := (*interface{})(unsafe.Pointer(&mapInterface))
realVal := reflect.ValueOf(*realInterface)
return realVal.Len() == 0
}

View File

@ -2,6 +2,7 @@ package jsoniter
import (
"unsafe"
"encoding/json"
)
type stringCodec struct {
@ -12,11 +13,16 @@ func (codec *stringCodec) decode(ptr unsafe.Pointer, iter *Iterator) {
}
func (codec *stringCodec) encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteString(*((*string)(ptr)))
str := *((*string)(ptr))
stream.WriteString(str)
}
func (encoder *stringCodec) encodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
writeToStream(val, stream, encoder)
}
func (codec *stringCodec) isEmpty(ptr unsafe.Pointer) bool {
return *((*string)(ptr)) == ""
}
type intCodec struct {
@ -31,7 +37,11 @@ func (codec *intCodec) encode(ptr unsafe.Pointer, stream *Stream) {
}
func (encoder *intCodec) encodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
writeToStream(val, stream, encoder)
}
func (codec *intCodec) isEmpty(ptr unsafe.Pointer) bool {
return *((*int)(ptr)) == 0
}
type int8Codec struct {
@ -46,7 +56,11 @@ func (codec *int8Codec) encode(ptr unsafe.Pointer, stream *Stream) {
}
func (encoder *int8Codec) encodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
writeToStream(val, stream, encoder)
}
func (codec *int8Codec) isEmpty(ptr unsafe.Pointer) bool {
return *((*int8)(ptr)) == 0
}
type int16Codec struct {
@ -61,7 +75,11 @@ func (codec *int16Codec) encode(ptr unsafe.Pointer, stream *Stream) {
}
func (encoder *int16Codec) encodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
writeToStream(val, stream, encoder)
}
func (codec *int16Codec) isEmpty(ptr unsafe.Pointer) bool {
return *((*int16)(ptr)) == 0
}
type int32Codec struct {
@ -76,7 +94,11 @@ func (codec *int32Codec) encode(ptr unsafe.Pointer, stream *Stream) {
}
func (encoder *int32Codec) encodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
writeToStream(val, stream, encoder)
}
func (codec *int32Codec) isEmpty(ptr unsafe.Pointer) bool {
return *((*int32)(ptr)) == 0
}
type int64Codec struct {
@ -91,7 +113,11 @@ func (codec *int64Codec) encode(ptr unsafe.Pointer, stream *Stream) {
}
func (encoder *int64Codec) encodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
writeToStream(val, stream, encoder)
}
func (codec *int64Codec) isEmpty(ptr unsafe.Pointer) bool {
return *((*int64)(ptr)) == 0
}
type uintCodec struct {
@ -106,7 +132,11 @@ func (codec *uintCodec) encode(ptr unsafe.Pointer, stream *Stream) {
}
func (encoder *uintCodec) encodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
writeToStream(val, stream, encoder)
}
func (codec *uintCodec) isEmpty(ptr unsafe.Pointer) bool {
return *((*uint)(ptr)) == 0
}
type uint8Codec struct {
@ -121,7 +151,11 @@ func (codec *uint8Codec) encode(ptr unsafe.Pointer, stream *Stream) {
}
func (encoder *uint8Codec) encodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
writeToStream(val, stream, encoder)
}
func (codec *uint8Codec) isEmpty(ptr unsafe.Pointer) bool {
return *((*uint8)(ptr)) == 0
}
type uint16Codec struct {
@ -136,7 +170,11 @@ func (codec *uint16Codec) encode(ptr unsafe.Pointer, stream *Stream) {
}
func (encoder *uint16Codec) encodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
writeToStream(val, stream, encoder)
}
func (codec *uint16Codec) isEmpty(ptr unsafe.Pointer) bool {
return *((*uint16)(ptr)) == 0
}
type uint32Codec struct {
@ -151,7 +189,11 @@ func (codec *uint32Codec) encode(ptr unsafe.Pointer, stream *Stream) {
}
func (encoder *uint32Codec) encodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
writeToStream(val, stream, encoder)
}
func (codec *uint32Codec) isEmpty(ptr unsafe.Pointer) bool {
return *((*uint32)(ptr)) == 0
}
type uint64Codec struct {
@ -166,7 +208,11 @@ func (codec *uint64Codec) encode(ptr unsafe.Pointer, stream *Stream) {
}
func (encoder *uint64Codec) encodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
writeToStream(val, stream, encoder)
}
func (codec *uint64Codec) isEmpty(ptr unsafe.Pointer) bool {
return *((*uint64)(ptr)) == 0
}
type float32Codec struct {
@ -181,7 +227,11 @@ func (codec *float32Codec) encode(ptr unsafe.Pointer, stream *Stream) {
}
func (encoder *float32Codec) encodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
writeToStream(val, stream, encoder)
}
func (codec *float32Codec) isEmpty(ptr unsafe.Pointer) bool {
return *((*float32)(ptr)) == 0
}
type float64Codec struct {
@ -196,7 +246,11 @@ func (codec *float64Codec) encode(ptr unsafe.Pointer, stream *Stream) {
}
func (encoder *float64Codec) encodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
writeToStream(val, stream, encoder)
}
func (codec *float64Codec) isEmpty(ptr unsafe.Pointer) bool {
return *((*float64)(ptr)) == 0
}
type boolCodec struct {
@ -211,24 +265,63 @@ func (codec *boolCodec) encode(ptr unsafe.Pointer, stream *Stream) {
}
func (encoder *boolCodec) encodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
writeToStream(val, stream, encoder)
}
type interfaceCodec struct {
func (codec *boolCodec) isEmpty(ptr unsafe.Pointer) bool {
return !(*((*bool)(ptr)))
}
func (codec *interfaceCodec) decode(ptr unsafe.Pointer, iter *Iterator) {
type emptyInterfaceCodec struct {
}
func (codec *emptyInterfaceCodec) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*interface{})(ptr)) = iter.Read()
}
func (codec *interfaceCodec) encode(ptr unsafe.Pointer, stream *Stream) {
func (codec *emptyInterfaceCodec) encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteVal(*((*interface{})(ptr)))
}
func (encoder *interfaceCodec) encodeInterface(val interface{}, stream *Stream) {
func (encoder *emptyInterfaceCodec) encodeInterface(val interface{}, stream *Stream) {
stream.WriteVal(val)
}
func (codec *emptyInterfaceCodec) isEmpty(ptr unsafe.Pointer) bool {
return ptr == nil
}
type nonEmptyInterfaceCodec struct {
}
func (codec *nonEmptyInterfaceCodec) decode(ptr unsafe.Pointer, iter *Iterator) {
nonEmptyInterface := (*nonEmptyInterface)(ptr)
var i interface{}
e := (*emptyInterface)(unsafe.Pointer(&i))
e.typ = nonEmptyInterface.itab.typ
e.word = nonEmptyInterface.word
iter.ReadVal(&i)
nonEmptyInterface.word = e.word
}
func (codec *nonEmptyInterfaceCodec) encode(ptr unsafe.Pointer, stream *Stream) {
nonEmptyInterface := (*nonEmptyInterface)(ptr)
var i interface{}
e := (*emptyInterface)(unsafe.Pointer(&i))
e.typ = nonEmptyInterface.itab.typ
e.word = nonEmptyInterface.word
stream.WriteVal(i)
}
func (encoder *nonEmptyInterfaceCodec) encodeInterface(val interface{}, stream *Stream) {
stream.WriteVal(val)
}
func (codec *nonEmptyInterfaceCodec) isEmpty(ptr unsafe.Pointer) bool {
nonEmptyInterface := (*nonEmptyInterface)(ptr)
return nonEmptyInterface.word == nil
}
type anyCodec struct {
}
@ -244,6 +337,48 @@ func (encoder *anyCodec) encodeInterface(val interface{}, stream *Stream) {
(val.(Any)).WriteTo(stream)
}
func (encoder *anyCodec) isEmpty(ptr unsafe.Pointer) bool {
return (*((*Any)(ptr))).Size() == 0
}
type jsonNumberCodec struct {
}
func (codec *jsonNumberCodec) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*json.Number)(ptr)) = json.Number([]byte(iter.readNumberAsString()))
}
func (codec *jsonNumberCodec) encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteRaw(string(*((*json.Number)(ptr))))
}
func (encoder *jsonNumberCodec) encodeInterface(val interface{}, stream *Stream) {
stream.WriteRaw(string(val.(json.Number)))
}
func (encoder *jsonNumberCodec) isEmpty(ptr unsafe.Pointer) bool {
return len(*((*json.Number)(ptr))) == 0
}
type jsonRawMessageCodec struct {
}
func (codec *jsonRawMessageCodec) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*json.RawMessage)(ptr)) = json.RawMessage(iter.SkipAndReturnBytes())
}
func (codec *jsonRawMessageCodec) encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteRaw(string(*((*json.RawMessage)(ptr))))
}
func (encoder *jsonRawMessageCodec) encodeInterface(val interface{}, stream *Stream) {
stream.WriteRaw(string(val.(json.RawMessage)))
}
func (encoder *jsonRawMessageCodec) isEmpty(ptr unsafe.Pointer) bool {
return len(*((*json.RawMessage)(ptr))) == 0
}
type stringNumberDecoder struct {
elemDecoder Decoder
}
@ -263,4 +398,53 @@ func (decoder *stringNumberDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
iter.reportError("stringNumberDecoder", `expect "`)
return
}
}
type marshalerEncoder struct {
templateInterface emptyInterface
}
func (encoder *marshalerEncoder) encode(ptr unsafe.Pointer, stream *Stream) {
templateInterface := encoder.templateInterface
templateInterface.word = ptr
realInterface := (*interface{})(unsafe.Pointer(&templateInterface))
marshaler := (*realInterface).(json.Marshaler)
bytes, err := marshaler.MarshalJSON()
if err != nil {
stream.Error = err
} else {
stream.Write(bytes)
}
}
func (encoder *marshalerEncoder) encodeInterface(val interface{}, stream *Stream) {
writeToStream(val, stream, encoder)
}
func (encoder *marshalerEncoder) isEmpty(ptr unsafe.Pointer) bool {
templateInterface := encoder.templateInterface
templateInterface.word = ptr
realInterface := (*interface{})(unsafe.Pointer(&templateInterface))
marshaler := (*realInterface).(json.Marshaler)
bytes, err := marshaler.MarshalJSON()
if err != nil {
return true
} else {
return len(bytes) > 0
}
}
type unmarshalerDecoder struct {
templateInterface emptyInterface
}
func (decoder *unmarshalerDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
templateInterface := decoder.templateInterface
templateInterface.word = ptr
realInterface := (*interface{})(unsafe.Pointer(&templateInterface))
unmarshaler := (*realInterface).(json.Unmarshaler)
bytes := iter.SkipAndReturnBytes()
err := unmarshaler.UnmarshalJSON(bytes)
if err != nil {
iter.reportError("unmarshaler", err.Error())
}
}

View File

@ -6,66 +6,81 @@ import (
"reflect"
"unsafe"
"strings"
"unicode"
)
func encoderOfStruct(typ reflect.Type) (Encoder, error) {
structEncoder_ := &structEncoder{}
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
var fieldNames []string
fields := map[string]*structFieldEncoder{}
for _, field := range listStructFields(typ) {
fieldEncoderKey := fmt.Sprintf("%s/%s", typ.String(), field.Name)
var extensionProvidedFieldNames []string
for _, extension := range extensions {
alternativeFieldNames, _ := extension(typ, &field)
alternativeFieldNames, fun, _ := extension(typ, field)
if alternativeFieldNames != nil {
fieldNames = alternativeFieldNames
extensionProvidedFieldNames = alternativeFieldNames
}
if fun != nil {
fieldEncoders[fieldEncoderKey] = &funcEncoder{fun}
}
}
tagParts := strings.Split(field.Tag.Get("json"), ",")
// if fieldNames set by extension, use theirs, otherwise try tags
if fieldNames == nil {
/// tagParts[0] always present, even if no tags
switch tagParts[0] {
case "":
fieldNames = []string{field.Name}
case "-":
fieldNames = []string{}
default:
fieldNames = []string{tagParts[0]}
fieldNames := calcFieldNames(field.Name, tagParts[0], extensionProvidedFieldNames)
omitempty := false
for _, tagPart := range tagParts {
if tagPart == "omitempty" {
omitempty = true
}
}
encoder, err := encoderOfType(field.Type)
if err != nil {
return prefix(fmt.Sprintf("{%s}", field.Name)).addToEncoder(encoder, err)
}
// map is stored as pointer in the struct
// but if struct only has one map, it is inlined
if field.Type.Kind() == reflect.Map && typ.NumField() > 1 {
encoder = &optionalEncoder{field.Type, encoder}
encoder := fieldEncoders[fieldEncoderKey]
var err error
if encoder == nil && len(fieldNames) > 0 {
encoder, err = encoderOfType(field.Type)
if err != nil {
return prefix(fmt.Sprintf("{%s}", field.Name)).addToEncoder(encoder, err)
}
// map is stored as pointer in the struct
if field.Type.Kind() == reflect.Map {
encoder = &optionalEncoder{encoder}
}
}
for _, fieldName := range fieldNames {
if structEncoder_.firstField == nil {
structEncoder_.firstField = &structFieldEncoder{&field, fieldName, encoder}
} else {
structEncoder_.fields = append(structEncoder_.fields, &structFieldEncoder{&field, fieldName, encoder})
fields[fieldName] = &structFieldEncoder{field, fieldName, encoder, omitempty}
}
}
}
if structEncoder_.firstField == nil {
if len(fields) == 0 {
return &emptyStructEncoder{}, nil
}
for _, field := range fields {
structEncoder_.fields = append(structEncoder_.fields, field)
}
return structEncoder_, nil
}
func listStructFields(typ reflect.Type) []*reflect.StructField {
fields := []*reflect.StructField{}
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
if field.Anonymous {
fields = append(fields, listStructFields(field.Type)...)
} else {
fields = append(fields, &field)
}
}
return fields
}
func decoderOfStruct(typ reflect.Type) (Decoder, error) {
fields := map[string]*structFieldDecoder{}
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
fieldDecoderKey := fmt.Sprintf("%s/%s", typ.String(), field.Name)
var fieldNames []string
var extensionProviedFieldNames []string
for _, extension := range extensions {
alternativeFieldNames, fun := extension(typ, &field)
alternativeFieldNames, _, fun := extension(typ, &field)
if alternativeFieldNames != nil {
fieldNames = alternativeFieldNames
extensionProviedFieldNames = alternativeFieldNames
}
if fun != nil {
fieldDecoders[fieldDecoderKey] = &funcDecoder{fun}
@ -73,19 +88,8 @@ func decoderOfStruct(typ reflect.Type) (Decoder, error) {
}
decoder := fieldDecoders[fieldDecoderKey]
tagParts := strings.Split(field.Tag.Get("json"), ",")
// if fieldNames set by extension, use theirs, otherwise try tags
if fieldNames == nil {
/// tagParts[0] always present, even if no tags
switch tagParts[0] {
case "":
fieldNames = []string{field.Name}
case "-":
fieldNames = []string{}
default:
fieldNames = []string{tagParts[0]}
}
}
if decoder == nil {
fieldNames := calcFieldNames(field.Name, tagParts[0], extensionProviedFieldNames)
if decoder == nil && len(fieldNames) > 0 {
var err error
decoder, err = decoderOfType(field.Type)
if err != nil {
@ -102,6 +106,36 @@ func decoderOfStruct(typ reflect.Type) (Decoder, error) {
return createStructDecoder(typ, fields)
}
func calcFieldNames(originalFieldName string, tagProvidedFieldName string, extensionProvidedFieldNames []string) []string {
// tag => extension => exported? => original
isNotExported := unicode.IsLower(rune(originalFieldName[0]))
var fieldNames []string
/// tagParts[0] always present, even if no tags
switch tagProvidedFieldName {
case "":
if extensionProvidedFieldNames != nil {
fieldNames = extensionProvidedFieldNames
} else {
if isNotExported {
fieldNames = []string{}
} else {
fieldNames = []string{originalFieldName}
}
}
case "-":
fieldNames = []string{}
default:
fieldNames = []string{tagProvidedFieldName}
}
return fieldNames
}
func EnableUnexportedStructFieldsSupport() {
RegisterExtension(func(type_ reflect.Type, field *reflect.StructField) ([]string, EncoderFunc, DecoderFunc) {
return []string{field.Name}, nil, nil
})
}
func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder) (Decoder, error) {
knownHash := map[int32]struct{}{
0: struct{}{},
@ -169,7 +203,7 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
}
}
return &threeFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3}, nil
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3}, nil
case 4:
var fieldName1 int32
var fieldName2 int32
@ -202,8 +236,8 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
}
}
return &fourFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4}, nil
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4}, nil
case 5:
var fieldName1 int32
var fieldName2 int32
@ -241,8 +275,8 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
}
}
return &fiveFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5}, nil
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5}, nil
case 6:
var fieldName1 int32
var fieldName2 int32
@ -285,8 +319,8 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
}
}
return &sixFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6}, nil
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6}, nil
case 7:
var fieldName1 int32
var fieldName2 int32
@ -334,9 +368,9 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
}
}
return &sevenFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6,
fieldName7, fieldDecoder7}, nil
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6,
fieldName7, fieldDecoder7}, nil
case 8:
var fieldName1 int32
var fieldName2 int32
@ -389,9 +423,9 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
}
}
return &eightFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6,
fieldName7, fieldDecoder7, fieldName8, fieldDecoder8}, nil
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6,
fieldName7, fieldDecoder7, fieldName8, fieldDecoder8}, nil
case 9:
var fieldName1 int32
var fieldName2 int32
@ -449,9 +483,9 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
}
}
return &nineFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6,
fieldName7, fieldDecoder7, fieldName8, fieldDecoder8, fieldName9, fieldDecoder9}, nil
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6,
fieldName7, fieldDecoder7, fieldName8, fieldDecoder8, fieldName9, fieldDecoder9}, nil
case 10:
var fieldName1 int32
var fieldName2 int32
@ -514,10 +548,10 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
}
}
return &tenFieldsStructDecoder{typ,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6,
fieldName7, fieldDecoder7, fieldName8, fieldDecoder8, fieldName9, fieldDecoder9,
fieldName10, fieldDecoder10}, nil
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6,
fieldName7, fieldDecoder7, fieldName8, fieldDecoder8, fieldName9, fieldDecoder9,
fieldName10, fieldDecoder10}, nil
}
return &generalStructDecoder{typ, fields}, nil
}
@ -591,11 +625,11 @@ func (decoder *oneFieldStructDecoder) decode(ptr unsafe.Pointer, iter *Iterator)
}
type twoFieldsStructDecoder struct {
typ reflect.Type
fieldHash1 int32
fieldDecoder1 *structFieldDecoder
fieldHash2 int32
fieldDecoder2 *structFieldDecoder
typ reflect.Type
fieldHash1 int32
fieldDecoder1 *structFieldDecoder
fieldHash2 int32
fieldDecoder2 *structFieldDecoder
}
func (decoder *twoFieldsStructDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
@ -621,13 +655,13 @@ func (decoder *twoFieldsStructDecoder) decode(ptr unsafe.Pointer, iter *Iterator
}
type threeFieldsStructDecoder struct {
typ reflect.Type
fieldHash1 int32
fieldDecoder1 *structFieldDecoder
fieldHash2 int32
fieldDecoder2 *structFieldDecoder
fieldHash3 int32
fieldDecoder3 *structFieldDecoder
typ reflect.Type
fieldHash1 int32
fieldDecoder1 *structFieldDecoder
fieldHash2 int32
fieldDecoder2 *structFieldDecoder
fieldHash3 int32
fieldDecoder3 *structFieldDecoder
}
func (decoder *threeFieldsStructDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
@ -655,15 +689,15 @@ func (decoder *threeFieldsStructDecoder) decode(ptr unsafe.Pointer, iter *Iterat
}
type fourFieldsStructDecoder struct {
typ reflect.Type
fieldHash1 int32
fieldDecoder1 *structFieldDecoder
fieldHash2 int32
fieldDecoder2 *structFieldDecoder
fieldHash3 int32
fieldDecoder3 *structFieldDecoder
fieldHash4 int32
fieldDecoder4 *structFieldDecoder
typ reflect.Type
fieldHash1 int32
fieldDecoder1 *structFieldDecoder
fieldHash2 int32
fieldDecoder2 *structFieldDecoder
fieldHash3 int32
fieldDecoder3 *structFieldDecoder
fieldHash4 int32
fieldDecoder4 *structFieldDecoder
}
func (decoder *fourFieldsStructDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
@ -693,17 +727,17 @@ func (decoder *fourFieldsStructDecoder) decode(ptr unsafe.Pointer, iter *Iterato
}
type fiveFieldsStructDecoder struct {
typ reflect.Type
fieldHash1 int32
fieldDecoder1 *structFieldDecoder
fieldHash2 int32
fieldDecoder2 *structFieldDecoder
fieldHash3 int32
fieldDecoder3 *structFieldDecoder
fieldHash4 int32
fieldDecoder4 *structFieldDecoder
fieldHash5 int32
fieldDecoder5 *structFieldDecoder
typ reflect.Type
fieldHash1 int32
fieldDecoder1 *structFieldDecoder
fieldHash2 int32
fieldDecoder2 *structFieldDecoder
fieldHash3 int32
fieldDecoder3 *structFieldDecoder
fieldHash4 int32
fieldDecoder4 *structFieldDecoder
fieldHash5 int32
fieldDecoder5 *structFieldDecoder
}
func (decoder *fiveFieldsStructDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
@ -735,19 +769,19 @@ func (decoder *fiveFieldsStructDecoder) decode(ptr unsafe.Pointer, iter *Iterato
}
type sixFieldsStructDecoder struct {
typ reflect.Type
fieldHash1 int32
fieldDecoder1 *structFieldDecoder
fieldHash2 int32
fieldDecoder2 *structFieldDecoder
fieldHash3 int32
fieldDecoder3 *structFieldDecoder
fieldHash4 int32
fieldDecoder4 *structFieldDecoder
fieldHash5 int32
fieldDecoder5 *structFieldDecoder
fieldHash6 int32
fieldDecoder6 *structFieldDecoder
typ reflect.Type
fieldHash1 int32
fieldDecoder1 *structFieldDecoder
fieldHash2 int32
fieldDecoder2 *structFieldDecoder
fieldHash3 int32
fieldDecoder3 *structFieldDecoder
fieldHash4 int32
fieldDecoder4 *structFieldDecoder
fieldHash5 int32
fieldDecoder5 *structFieldDecoder
fieldHash6 int32
fieldDecoder6 *structFieldDecoder
}
func (decoder *sixFieldsStructDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
@ -781,21 +815,21 @@ func (decoder *sixFieldsStructDecoder) decode(ptr unsafe.Pointer, iter *Iterator
}
type sevenFieldsStructDecoder struct {
typ reflect.Type
fieldHash1 int32
fieldDecoder1 *structFieldDecoder
fieldHash2 int32
fieldDecoder2 *structFieldDecoder
fieldHash3 int32
fieldDecoder3 *structFieldDecoder
fieldHash4 int32
fieldDecoder4 *structFieldDecoder
fieldHash5 int32
fieldDecoder5 *structFieldDecoder
fieldHash6 int32
fieldDecoder6 *structFieldDecoder
fieldHash7 int32
fieldDecoder7 *structFieldDecoder
typ reflect.Type
fieldHash1 int32
fieldDecoder1 *structFieldDecoder
fieldHash2 int32
fieldDecoder2 *structFieldDecoder
fieldHash3 int32
fieldDecoder3 *structFieldDecoder
fieldHash4 int32
fieldDecoder4 *structFieldDecoder
fieldHash5 int32
fieldDecoder5 *structFieldDecoder
fieldHash6 int32
fieldDecoder6 *structFieldDecoder
fieldHash7 int32
fieldDecoder7 *structFieldDecoder
}
func (decoder *sevenFieldsStructDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
@ -831,23 +865,23 @@ func (decoder *sevenFieldsStructDecoder) decode(ptr unsafe.Pointer, iter *Iterat
}
type eightFieldsStructDecoder struct {
typ reflect.Type
fieldHash1 int32
fieldDecoder1 *structFieldDecoder
fieldHash2 int32
fieldDecoder2 *structFieldDecoder
fieldHash3 int32
fieldDecoder3 *structFieldDecoder
fieldHash4 int32
fieldDecoder4 *structFieldDecoder
fieldHash5 int32
fieldDecoder5 *structFieldDecoder
fieldHash6 int32
fieldDecoder6 *structFieldDecoder
fieldHash7 int32
fieldDecoder7 *structFieldDecoder
fieldHash8 int32
fieldDecoder8 *structFieldDecoder
typ reflect.Type
fieldHash1 int32
fieldDecoder1 *structFieldDecoder
fieldHash2 int32
fieldDecoder2 *structFieldDecoder
fieldHash3 int32
fieldDecoder3 *structFieldDecoder
fieldHash4 int32
fieldDecoder4 *structFieldDecoder
fieldHash5 int32
fieldDecoder5 *structFieldDecoder
fieldHash6 int32
fieldDecoder6 *structFieldDecoder
fieldHash7 int32
fieldDecoder7 *structFieldDecoder
fieldHash8 int32
fieldDecoder8 *structFieldDecoder
}
func (decoder *eightFieldsStructDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
@ -885,25 +919,25 @@ func (decoder *eightFieldsStructDecoder) decode(ptr unsafe.Pointer, iter *Iterat
}
type nineFieldsStructDecoder struct {
typ reflect.Type
fieldHash1 int32
fieldDecoder1 *structFieldDecoder
fieldHash2 int32
fieldDecoder2 *structFieldDecoder
fieldHash3 int32
fieldDecoder3 *structFieldDecoder
fieldHash4 int32
fieldDecoder4 *structFieldDecoder
fieldHash5 int32
fieldDecoder5 *structFieldDecoder
fieldHash6 int32
fieldDecoder6 *structFieldDecoder
fieldHash7 int32
fieldDecoder7 *structFieldDecoder
fieldHash8 int32
fieldDecoder8 *structFieldDecoder
fieldHash9 int32
fieldDecoder9 *structFieldDecoder
typ reflect.Type
fieldHash1 int32
fieldDecoder1 *structFieldDecoder
fieldHash2 int32
fieldDecoder2 *structFieldDecoder
fieldHash3 int32
fieldDecoder3 *structFieldDecoder
fieldHash4 int32
fieldDecoder4 *structFieldDecoder
fieldHash5 int32
fieldDecoder5 *structFieldDecoder
fieldHash6 int32
fieldDecoder6 *structFieldDecoder
fieldHash7 int32
fieldDecoder7 *structFieldDecoder
fieldHash8 int32
fieldDecoder8 *structFieldDecoder
fieldHash9 int32
fieldDecoder9 *structFieldDecoder
}
func (decoder *nineFieldsStructDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
@ -1021,6 +1055,7 @@ type structFieldEncoder struct {
field *reflect.StructField
fieldName string
fieldEncoder Encoder
omitempty bool
}
func (encoder *structFieldEncoder) encode(ptr unsafe.Pointer, stream *Stream) {
@ -1033,29 +1068,63 @@ func (encoder *structFieldEncoder) encode(ptr unsafe.Pointer, stream *Stream) {
}
func (encoder *structFieldEncoder) encodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
writeToStream(val, stream, encoder)
}
func (encoder *structFieldEncoder) isEmpty(ptr unsafe.Pointer) bool {
fieldPtr := uintptr(ptr) + encoder.field.Offset
return encoder.fieldEncoder.isEmpty(unsafe.Pointer(fieldPtr))
}
type structEncoder struct {
firstField *structFieldEncoder
fields []*structFieldEncoder
}
func (encoder *structEncoder) encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteObjectStart()
encoder.firstField.encode(ptr, stream)
isNotFirst := false
for _, field := range encoder.fields {
stream.WriteMore()
if field.omitempty && field.isEmpty(ptr) {
continue
}
if isNotFirst {
stream.WriteMore()
}
field.encode(ptr, stream)
isNotFirst = true
}
stream.WriteObjectEnd()
}
func (encoder *structEncoder) encodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
var encoderToUse Encoder
encoderToUse = encoder
if len(encoder.fields) == 1 {
firstEncoder := encoder.fields[0].fieldEncoder
firstEncoderName := reflect.TypeOf(firstEncoder).String()
// interface{} has inline optimization for this case
if firstEncoderName == "*jsoniter.optionalEncoder" {
encoderToUse = &structEncoder{
fields: []*structFieldEncoder{{
field: encoder.fields[0].field,
fieldName: encoder.fields[0].fieldName,
fieldEncoder: firstEncoder.(*optionalEncoder).valueEncoder,
omitempty: encoder.fields[0].omitempty,
}},
}
}
}
writeToStream(val, stream, encoderToUse)
}
func (encoder *structEncoder) isEmpty(ptr unsafe.Pointer) bool {
for _, field := range encoder.fields {
if !field.isEmpty(ptr) {
return false
}
}
return true
}
type emptyStructEncoder struct {
}
@ -1065,5 +1134,9 @@ func (encoder *emptyStructEncoder) encode(ptr unsafe.Pointer, stream *Stream) {
}
func (encoder *emptyStructEncoder) encodeInterface(val interface{}, stream *Stream) {
WriteToStream(val, stream, encoder)
}
writeToStream(val, stream, encoder)
}
func (encoder *emptyStructEncoder) isEmpty(ptr unsafe.Pointer) bool {
return true
}

View File

@ -2,6 +2,7 @@ package jsoniter
import (
"strconv"
"unsafe"
)
var POW10 []uint64
@ -11,6 +12,10 @@ func init() {
}
func (stream *Stream) WriteFloat32(val float32) {
stream.WriteRaw(strconv.FormatFloat(float64(val), 'f', -1, 32))
}
func (stream *Stream) WriteFloat32Lossy(val float32) {
if val < 0 {
stream.writeByte('-')
val = -val
@ -34,13 +39,17 @@ func (stream *Stream) WriteFloat32(val float32) {
for p := precision - 1; p > 0 && fval < POW10[p]; p-- {
stream.writeByte('0')
}
stream.WriteUint64(fval);
stream.WriteUint64(fval)
for stream.buf[stream.n - 1] == '0' {
stream.n--;
stream.n--
}
}
func (stream *Stream) WriteFloat64(val float64) {
stream.WriteRaw(strconv.FormatFloat(float64(val), 'f', -1, 64))
}
func (stream *Stream) WriteFloat64Lossy(val float64) {
if val < 0 {
stream.writeByte('-')
val = -val
@ -64,8 +73,20 @@ func (stream *Stream) WriteFloat64(val float64) {
for p := precision - 1; p > 0 && fval < POW10[p]; p-- {
stream.writeByte('0')
}
stream.WriteUint64(fval);
stream.WriteUint64(fval)
for stream.buf[stream.n - 1] == '0' {
stream.n--;
stream.n--
}
}
func EnableLossyFloatMarshalling() {
// for better performance
RegisterTypeEncoder("float32", func(ptr unsafe.Pointer, stream *Stream) {
val := *((*float32)(ptr))
stream.WriteFloat32Lossy(val)
})
RegisterTypeEncoder("float64", func(ptr unsafe.Pointer, stream *Stream) {
val := *((*float64)(ptr))
stream.WriteFloat64Lossy(val)
})
}

46
jsoniter_adapter_test.go Normal file
View File

@ -0,0 +1,46 @@
package jsoniter
import (
"testing"
"github.com/json-iterator/go/require"
"encoding/json"
"bytes"
"io/ioutil"
)
func Test_new_decoder(t *testing.T) {
should := require.New(t)
decoder1 := json.NewDecoder(bytes.NewBufferString(`[1][2]`))
decoder2 := NewDecoder(bytes.NewBufferString(`[1][2]`))
arr1 := []int{}
should.Nil(decoder1.Decode(&arr1))
should.Equal([]int{1}, arr1)
arr2 := []int{}
should.True(decoder1.More())
buffered, _ := ioutil.ReadAll(decoder1.Buffered())
should.Equal("[2]", string(buffered))
should.Nil(decoder2.Decode(&arr2))
should.Equal([]int{1}, arr2)
should.True(decoder2.More())
buffered, _ = ioutil.ReadAll(decoder2.Buffered())
should.Equal("[2]", string(buffered))
should.Nil(decoder1.Decode(&arr1))
should.Equal([]int{2}, arr1)
should.False(decoder1.More())
should.Nil(decoder2.Decode(&arr2))
should.Equal([]int{2}, arr2)
should.False(decoder2.More())
}
func Test_new_encoder(t *testing.T) {
should := require.New(t)
buf1 := &bytes.Buffer{}
encoder1 := json.NewEncoder(buf1)
encoder1.Encode([]int{1})
should.Equal("[1]\n", buf1.String())
buf2 := &bytes.Buffer{}
encoder2 := NewEncoder(buf2)
encoder2.Encode([]int{1})
should.Equal("[1]", buf2.String())
}

View File

@ -250,7 +250,18 @@ func Test_write_array_of_interface_in_struct(t *testing.T) {
val := TestObject{[]interface{}{1, 2}, ""}
str, err := MarshalToString(val)
should.Nil(err)
should.Equal(`{"Field":[1,2],"Field2":""}`, str)
should.Contains(str, `"Field":[1,2]`)
should.Contains(str, `"Field2":""`)
}
func Test_json_RawMessage(t *testing.T) {
should := require.New(t)
var data json.RawMessage
should.Nil(Unmarshal([]byte(`[1,2,3]`), &data))
should.Equal(`[1,2,3]`, string(data))
str, err := MarshalToString(data)
should.Nil(err)
should.Equal(`[1,2,3]`, str)
}
func Benchmark_jsoniter_array(b *testing.B) {

View File

@ -6,6 +6,8 @@ import (
"testing"
"time"
"unsafe"
"github.com/json-iterator/go/require"
"encoding/json"
)
func Test_customize_type_decoder(t *testing.T) {
@ -29,6 +31,41 @@ func Test_customize_type_decoder(t *testing.T) {
}
}
func Test_customize_type_encoder(t *testing.T) {
should := require.New(t)
RegisterTypeEncoder("time.Time", func(ptr unsafe.Pointer, stream *Stream) {
t := *((*time.Time)(ptr))
stream.WriteString(t.UTC().Format("2006-01-02 15:04:05"))
})
defer CleanEncoders()
val := time.Unix(0, 0)
str, err := MarshalToString(val)
should.Nil(err)
should.Equal(`"1970-01-01 00:00:00"`, str)
}
func Test_customize_byte_array_encoder(t *testing.T) {
should := require.New(t)
RegisterTypeEncoder("[]uint8", func(ptr unsafe.Pointer, stream *Stream) {
t := *((*[]byte)(ptr))
stream.WriteString(string(t))
})
defer CleanEncoders()
val := []byte("abc")
str, err := MarshalToString(val)
should.Nil(err)
should.Equal(`"abc"`, str)
}
func Test_customize_float_marshal(t *testing.T) {
should := require.New(t)
EnableLossyFloatMarshalling()
defer CleanEncoders()
str, err := MarshalToString(float32(1.23456789))
should.Nil(err)
should.Equal("1.234568", str)
}
type Tom struct {
field1 string
}
@ -50,20 +87,129 @@ type TestObject1 struct {
}
func Test_customize_field_by_extension(t *testing.T) {
RegisterExtension(func(type_ reflect.Type, field *reflect.StructField) ([]string, DecoderFunc) {
should := require.New(t)
RegisterExtension(func(type_ reflect.Type, field *reflect.StructField) ([]string, EncoderFunc, DecoderFunc) {
if type_.String() == "jsoniter.TestObject1" && field.Name == "field1" {
return []string{"field-1"}, func(ptr unsafe.Pointer, iter *Iterator) {
encode := func(ptr unsafe.Pointer, stream *Stream) {
str := *((*string)(ptr))
val, _ := strconv.Atoi(str)
stream.WriteInt(val)
}
decode := func(ptr unsafe.Pointer, iter *Iterator) {
*((*string)(ptr)) = strconv.Itoa(iter.ReadInt())
}
return []string{"field-1"}, encode, decode
}
return nil, nil
return nil, nil, nil
})
obj := TestObject1{}
err := Unmarshal([]byte(`{"field-1": 100}`), &obj)
if err != nil {
t.Fatal(err)
}
if obj.field1 != "100" {
t.Fatal(obj.field1)
}
err := UnmarshalFromString(`{"field-1": 100}`, &obj)
should.Nil(err)
should.Equal("100", obj.field1)
str, err := MarshalToString(obj)
should.Nil(err)
should.Equal(`{"field-1":100}`, str)
}
func Test_unexported_fields(t *testing.T) {
EnableUnexportedStructFieldsSupport()
should := require.New(t)
type TestObject struct {
field1 string
field2 string `json:"field-2"`
}
obj := TestObject{}
obj.field1 = "hello"
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("hello", obj.field1)
should.Nil(UnmarshalFromString(`{"field1": "world", "field-2": "abc"}`, &obj))
should.Equal("world", obj.field1)
should.Equal("abc", obj.field2)
str, err := MarshalToString(obj)
should.Nil(err)
should.Contains(str, `"field-2":"abc"`)
}
type ObjectImplementedMarshaler int
func (obj *ObjectImplementedMarshaler) MarshalJSON() ([]byte, error) {
return []byte(`"hello"`), nil
}
func Test_marshaler(t *testing.T) {
type TestObject struct {
Field *ObjectImplementedMarshaler
}
should := require.New(t)
val := ObjectImplementedMarshaler(100)
obj := TestObject{&val}
bytes, err := json.Marshal(obj)
should.Nil(err)
should.Equal(`{"Field":"hello"}`, string(bytes))
str, err := MarshalToString(obj)
should.Nil(err)
should.Equal(`{"Field":"hello"}`, str)
}
func Test_marshaler_and_encoder(t *testing.T) {
type TestObject struct {
Field *ObjectImplementedMarshaler
}
should := require.New(t)
RegisterTypeEncoder("jsoniter.ObjectImplementedMarshaler", func(ptr unsafe.Pointer, stream *Stream) {
stream.WriteString("hello from encoder")
})
val := ObjectImplementedMarshaler(100)
obj := TestObject{&val}
bytes, err := json.Marshal(obj)
should.Nil(err)
should.Equal(`{"Field":"hello"}`, string(bytes))
str, err := MarshalToString(obj)
should.Nil(err)
should.Equal(`{"Field":"hello from encoder"}`, str)
}
type ObjectImplementedUnmarshaler int
func (obj *ObjectImplementedUnmarshaler) UnmarshalJSON([]byte) error {
*obj = 100
return nil
}
func Test_unmarshaler(t *testing.T) {
type TestObject struct {
Field *ObjectImplementedUnmarshaler
Field2 string
}
should := require.New(t)
obj := TestObject{}
val := ObjectImplementedUnmarshaler(0)
obj.Field = &val
err := json.Unmarshal([]byte(`{"Field":"hello"}`), &obj)
should.Nil(err)
should.Equal(100, int(*obj.Field))
err = Unmarshal([]byte(`{"Field":"hello"}`), &obj)
should.Nil(err)
should.Equal(100, int(*obj.Field))
}
func Test_unmarshaler_and_decoder(t *testing.T) {
type TestObject struct {
Field *ObjectImplementedUnmarshaler
Field2 string
}
should := require.New(t)
RegisterTypeDecoder("jsoniter.ObjectImplementedUnmarshaler", func(ptr unsafe.Pointer, iter *Iterator) {
*(*ObjectImplementedUnmarshaler)(ptr) = 10
iter.Skip()
})
obj := TestObject{}
val := ObjectImplementedUnmarshaler(0)
obj.Field = &val
err := json.Unmarshal([]byte(`{"Field":"hello"}`), &obj)
should.Nil(err)
should.Equal(100, int(*obj.Field))
err = Unmarshal([]byte(`{"Field":"hello"}`), &obj)
should.Nil(err)
should.Equal(10, int(*obj.Field))
}

View File

@ -3,13 +3,15 @@ package jsoniter
import (
"fmt"
"testing"
"github.com/json-iterator/go/require"
)
func Test_bind_api_demo(t *testing.T) {
iter := ParseString(`[0,1,2,3]`)
should := require.New(t)
val := []int{}
iter.ReadVal(&val)
fmt.Println(val[3])
err := UnmarshalFromString(`[0,1,2,3] `, &val)
should.Nil(err)
should.Equal([]int{0, 1, 2, 3}, val)
}
func Test_iterator_api_demo(t *testing.T) {
@ -20,20 +22,3 @@ func Test_iterator_api_demo(t *testing.T) {
}
fmt.Println(total)
}
type User struct {
userID int
name string
tags []string
}
func Test_iterator_and_bind_api(t *testing.T) {
iter := ParseString(`[123, {"name": "taowen", "tags": ["crazy", "hacker"]}]`)
user := User{}
iter.ReadArray()
user.userID = iter.ReadInt()
iter.ReadArray()
iter.ReadVal(&user)
iter.ReadArray() // array end
fmt.Println(user)
}

View File

@ -9,6 +9,22 @@ import (
"strconv"
)
func Test_read_big_float(t *testing.T) {
should := require.New(t)
iter := ParseString(`12.3`)
val := iter.ReadBigFloat()
val64, _ := val.Float64()
should.Equal(12.3, val64)
}
func Test_read_big_int(t *testing.T) {
should := require.New(t)
iter := ParseString(`92233720368547758079223372036854775807`)
val := iter.ReadBigInt()
should.NotNil(val)
should.Equal(`92233720368547758079223372036854775807`, val.String())
}
func Test_read_float(t *testing.T) {
inputs := []string{`1.1`, `1000`, `9223372036854775807`, `12.3`, `-12.3`, `720368.54775807`, `720368.547758075`}
for _, input := range inputs {
@ -75,7 +91,7 @@ func Test_write_float32(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(buf, 4096)
stream.WriteFloat32(val)
stream.WriteFloat32Lossy(val)
stream.Flush()
should.Nil(stream.Error)
should.Equal(strconv.FormatFloat(float64(val), 'f', -1, 32), buf.String())
@ -94,7 +110,7 @@ func Test_write_float32(t *testing.T) {
buf := &bytes.Buffer{}
stream := NewStream(buf, 10)
stream.WriteRaw("abcdefg")
stream.WriteFloat32(1.123456)
stream.WriteFloat32Lossy(1.123456)
stream.Flush()
should.Nil(stream.Error)
should.Equal("abcdefg1.123456", buf.String())
@ -108,7 +124,7 @@ func Test_write_float64(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(buf, 4096)
stream.WriteFloat64(val)
stream.WriteFloat64Lossy(val)
stream.Flush()
should.Nil(stream.Error)
should.Equal(strconv.FormatFloat(val, 'f', -1, 64), buf.String())
@ -127,12 +143,21 @@ func Test_write_float64(t *testing.T) {
buf := &bytes.Buffer{}
stream := NewStream(buf, 10)
stream.WriteRaw("abcdefg")
stream.WriteFloat64(1.123456)
stream.WriteFloat64Lossy(1.123456)
stream.Flush()
should.Nil(stream.Error)
should.Equal("abcdefg1.123456", buf.String())
}
func Test_read_float64_cursor(t *testing.T) {
should := require.New(t)
iter := ParseString("[1.23456789\n,2,3]")
should.True(iter.ReadArray())
should.Equal(1.23456789, iter.Read())
should.True(iter.ReadArray())
should.Equal(float64(2), iter.Read())
}
func Benchmark_jsoniter_float(b *testing.B) {
b.ReportAllocs()
input := []byte(`1.1123,`)

View File

@ -421,6 +421,17 @@ func Test_write_val_int_ptr(t *testing.T) {
should.Equal("1001", buf.String())
}
func Test_json_number(t *testing.T) {
should := require.New(t)
var arr []json.Number
err := Unmarshal([]byte(`[1]`), &arr)
should.Nil(err)
should.Equal(json.Number("1"), arr[0])
str, err := MarshalToString(arr)
should.Nil(err)
should.Equal(`[1]`, str)
}
func Benchmark_jsoniter_encode_int(b *testing.B) {
stream := NewStream(ioutil.Discard, 64)
for n := 0; n < b.N; n++ {

View File

@ -42,7 +42,7 @@ func Test_write_map_of_interface_in_struct_with_two_fields(t *testing.T) {
val := TestObject{map[string]interface{}{"hello":"world"}, ""}
str, err := MarshalToString(val)
should.Nil(err)
should.Equal(`{"Field":{"hello":"world"},"Field2":""}`, str)
should.Contains(str, `"Field":{"hello":"world"}`)
}
type MyInterface interface {
@ -91,4 +91,50 @@ func Test_read_custom_interface(t *testing.T) {
err := UnmarshalFromString(`"hello"`, &val)
should.Nil(err)
should.Equal("hello", val.Hello())
}
func Test_decode_object_contain_empty_interface(t *testing.T) {
type TestObject struct {
Field interface{}
}
should := require.New(t)
obj := TestObject{}
obj.Field = 1024
should.Nil(UnmarshalFromString(`{"Field": "hello"}`, &obj))
should.Equal("hello", obj.Field)
}
func Test_decode_object_contain_non_empty_interface(t *testing.T) {
type TestObject struct {
Field MyInterface
}
should := require.New(t)
obj := TestObject{}
obj.Field = MyString("abc")
should.Nil(UnmarshalFromString(`{"Field": "hello"}`, &obj))
should.Equal(MyString("hello"), obj.Field)
}
func Test_encode_object_contain_empty_interface(t *testing.T) {
type TestObject struct {
Field interface{}
}
should := require.New(t)
obj := TestObject{}
obj.Field = 1024
str, err := MarshalToString(obj)
should.Nil(err)
should.Equal(`{"Field":1024}`, str)
}
func Test_encode_object_contain_non_empty_interface(t *testing.T) {
type TestObject struct {
Field MyInterface
}
should := require.New(t)
obj := TestObject{}
obj.Field = MyString("hello")
str, err := MarshalToString(obj)
should.Nil(err)
should.Equal(`{"Field":"hello"}`, str)
}

View File

@ -56,3 +56,14 @@ func Test_write_val_map(t *testing.T) {
should.Nil(err)
should.Equal(`{"1":"2"}`, str)
}
func Test_slice_of_map(t *testing.T) {
should := require.New(t)
val := []map[string]string{{"1": "2"}}
str, err := MarshalToString(val)
should.Nil(err)
should.Equal(`[{"1":"2"}]`, str)
val = []map[string]string{}
should.Nil(UnmarshalFromString(str, &val))
should.Equal("2", val[0]["1"])
}

View File

@ -39,6 +39,7 @@ func Test_encode_null(t *testing.T) {
}
func Test_decode_null_object(t *testing.T) {
should := require.New(t)
iter := ParseString(`[null,"a"]`)
iter.ReadArray()
if iter.ReadObject() != "" {
@ -48,6 +49,12 @@ func Test_decode_null_object(t *testing.T) {
if iter.ReadString() != "a" {
t.FailNow()
}
type TestObject struct {
Field string
}
objs := []TestObject{}
should.Nil(UnmarshalFromString("[null]", &objs))
should.Len(objs, 1)
}
func Test_decode_null_array(t *testing.T) {
@ -66,7 +73,7 @@ func Test_decode_null_string(t *testing.T) {
should := require.New(t)
iter := ParseString(`[null,"a"]`)
should.True(iter.ReadArray())
should.True(iter.ReadNil())
should.Equal("", iter.ReadString())
should.True(iter.ReadArray())
should.Equal("a", iter.ReadString())
}

View File

@ -102,8 +102,11 @@ func Test_object_any_lazy_iterator(t *testing.T) {
should.Equal(map[string]string{"a":"b", "c":"d"}, vals)
vals = map[string]string{}
for next, hasNext := any.IterateObject(); hasNext; k, v, hasNext = next() {
vals[k] = v.ToString()
for next, hasNext := any.IterateObject(); hasNext; {
k, v, hasNext = next()
if v.ValueType() == String {
vals[k] = v.ToString()
}
}
should.Equal(map[string]string{"a":"b", "c":"d"}, vals)
}
@ -150,7 +153,7 @@ func Test_object_lazy_any_get_all(t *testing.T) {
should := require.New(t)
any, err := UnmarshalAnyFromString(`{"a":[0],"b":[1]}`)
should.Nil(err)
should.Equal(`{"a":0,"b":1}`, any.Get('*', 0).ToString())
should.Contains(any.Get('*', 0).ToString(), `"a":0`)
}
func Test_object_lazy_any_get_invalid(t *testing.T) {
@ -201,7 +204,7 @@ func Test_object_wrapper_any_get_all(t *testing.T) {
Field2 []int
}
any := Wrap(TestObject{[]int{1, 2}, []int{3, 4}})
should.Equal(`{"Field2":3,"Field1":1}`, any.Get('*', 0).ToString())
should.Contains(any.Get('*', 0).ToString(), `"Field2":3`)
}
func Test_write_object(t *testing.T) {

View File

@ -41,5 +41,6 @@ func Test_encode_struct_with_optional_field(t *testing.T) {
obj.field2 = &world
str, err := MarshalToString(obj)
should.Nil(err)
should.Equal(`{"field1":null,"field2":"world"}`, str)
should.Contains(str, `"field1":null`)
should.Contains(str, `"field2":"world"`)
}

View File

@ -3,111 +3,112 @@ package jsoniter
import (
"testing"
"github.com/json-iterator/go/require"
"bytes"
)
func Test_decode_one_field_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
field1 string
Field1 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.field1)
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"field1": "hello"}`, &obj))
should.Equal("hello", obj.field1)
should.Equal("hello", obj.Field1)
}
func Test_decode_two_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
field1 string
field2 string
Field1 string
Field2 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.field1)
should.Nil(UnmarshalFromString(`{"field1": "a", "field2": "b"}`, &obj))
should.Equal("a", obj.field1)
should.Equal("b", obj.field2)
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "b"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("b", obj.Field2)
}
func Test_decode_three_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
field1 string
field2 string
field3 string
Field1 string
Field2 string
Field3 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.field1)
should.Nil(UnmarshalFromString(`{"field1": "a", "field2": "b", "field3": "c"}`, &obj))
should.Equal("a", obj.field1)
should.Equal("b", obj.field2)
should.Equal("c", obj.field3)
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "b", "Field3": "c"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("b", obj.Field2)
should.Equal("c", obj.Field3)
}
func Test_decode_four_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
field1 string
field2 string
field3 string
field4 string
Field1 string
Field2 string
Field3 string
Field4 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.field1)
should.Nil(UnmarshalFromString(`{"field1": "a", "field2": "b", "field3": "c", "field4": "d"}`, &obj))
should.Equal("a", obj.field1)
should.Equal("b", obj.field2)
should.Equal("c", obj.field3)
should.Equal("d", obj.field4)
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "b", "Field3": "c", "Field4": "d"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("b", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
}
func Test_decode_five_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
field1 string
field2 string
field3 string
field4 string
field5 string
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.field1)
should.Nil(UnmarshalFromString(`{"field1": "a", "field2": "b", "field3": "c", "field4": "d", "field5": "e"}`, &obj))
should.Equal("a", obj.field1)
should.Equal("b", obj.field2)
should.Equal("c", obj.field3)
should.Equal("d", obj.field4)
should.Equal("e", obj.field5)
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "b", "Field3": "c", "Field4": "d", "Field5": "e"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("b", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
}
func Test_decode_ten_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
field1 string
field2 string
field3 string
field4 string
field5 string
field6 string
field7 string
field8 string
field9 string
field10 string
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
Field6 string
Field7 string
Field8 string
Field9 string
Field10 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.field1)
should.Nil(UnmarshalFromString(`{"field1": "a", "field2": "b", "field3": "c", "field4": "d", "field5": "e"}`, &obj))
should.Equal("a", obj.field1)
should.Equal("b", obj.field2)
should.Equal("c", obj.field3)
should.Equal("d", obj.field4)
should.Equal("e", obj.field5)
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "b", "Field3": "c", "Field4": "d", "Field5": "e"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("b", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
}
func Test_decode_struct_field_with_tag(t *testing.T) {
@ -144,3 +145,97 @@ func Test_write_val_one_field_struct(t *testing.T) {
should.Nil(err)
should.Equal(`{"field-1":"hello"}`, str)
}
func Test_mixed(t *testing.T) {
should := require.New(t)
type AA struct {
ID int `json:"id"`
Payload map[string]interface{} `json:"payload"`
buf *bytes.Buffer `json:"-"`
}
aa := AA{}
err := UnmarshalFromString(` {"id":1, "payload":{"account":"123","password":"456"}}`, &aa)
should.Nil(err)
should.Equal(1, aa.ID)
should.Equal("123", aa.Payload["account"])
}
func Test_omit_empty(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string `json:"field-1,omitempty"`
Field2 string `json:"field-2,omitempty"`
Field3 string `json:"field-3,omitempty"`
}
obj := TestObject{}
obj.Field2 = "hello"
str, err := MarshalToString(&obj)
should.Nil(err)
should.Equal(`{"field-2":"hello"}`, str)
}
func Test_any_within_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 Any
Field2 Any
}
obj := TestObject{}
err := UnmarshalFromString(`{"Field1": "hello", "Field2": [1,2,3]}`, &obj)
should.Nil(err)
should.Equal("hello", obj.Field1.ToString())
should.Equal("[1,2,3]", obj.Field2.ToString())
}
func Test_recursive_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Me *TestObject
}
obj := TestObject{}
str, err := MarshalToString(obj)
should.Nil(err)
should.Contains(str, `"Field1":""`)
should.Contains(str, `"Me":null`)
err = UnmarshalFromString(str, &obj)
should.Nil(err)
}
func Test_one_field_struct(t *testing.T) {
should := require.New(t)
type YetYetAnotherObject struct {
Field string
}
type YetAnotherObject struct {
Field *YetYetAnotherObject
}
type AnotherObject struct {
Field *YetAnotherObject
}
type TestObject struct {
Me *AnotherObject
}
obj := TestObject{&AnotherObject{&YetAnotherObject{&YetYetAnotherObject{"abc"}}}}
str, err := MarshalToString(obj)
should.Nil(err)
should.Equal(`{"Me":{"Field":{"Field":{"Field":"abc"}}}}`, str)
str, err = MarshalToString(&obj)
should.Nil(err)
should.Equal(`{"Me":{"Field":{"Field":{"Field":"abc"}}}}`, str)
}
func Test_anonymous_struct_marshal(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field string
}
str, err := MarshalToString(struct{
TestObject
Field int
}{
Field: 100,
})
should.Nil(err)
should.Equal(`{"Field":100}`, str)
}