├── .codecov.yml ├── .gitignore ├── .travis.yml ├── Gopkg.lock ├── Gopkg.toml ├── LICENSE ├── README.md ├── adapter.go ├── any.go ├── any_array.go ├── any_bool.go ├── any_float.go ├── any_int32.go ├── any_int64.go ├── any_invalid.go ├── any_nil.go ├── any_number.go ├── any_object.go ├── any_str.go ├── any_tests ├── jsoniter_any_array_test.go ├── jsoniter_any_bool_test.go ├── jsoniter_any_float_test.go ├── jsoniter_any_int_test.go ├── jsoniter_any_map_test.go ├── jsoniter_any_null_test.go ├── jsoniter_any_object_test.go ├── jsoniter_any_string_test.go ├── jsoniter_must_be_valid_test.go └── jsoniter_wrap_test.go ├── any_uint32.go ├── any_uint64.go ├── api_tests ├── config_test.go ├── decoder_test.go ├── encoder_18_test.go ├── encoder_test.go ├── marshal_indent_test.go ├── marshal_json_escape_test.go └── marshal_json_test.go ├── benchmarks ├── encode_string_test.go ├── jsoniter_large_file_test.go └── stream_test.go ├── build.sh ├── config.go ├── example_test.go ├── extension_tests ├── decoder_test.go └── extension_test.go ├── extra ├── binary_as_string_codec.go ├── binary_as_string_codec_test.go ├── fuzzy_decoder.go ├── fuzzy_decoder_test.go ├── naming_strategy.go ├── naming_strategy_test.go ├── privat_fields.go ├── private_fields_test.go ├── time_as_int64_codec.go └── time_as_int64_codec_test.go ├── fuzzy_mode_convert_table.md ├── go.mod ├── go.sum ├── iter.go ├── iter_array.go ├── iter_float.go ├── iter_int.go ├── iter_object.go ├── iter_skip.go ├── iter_skip_sloppy.go ├── iter_skip_sloppy_test.go ├── iter_skip_strict.go ├── iter_str.go ├── jsoniter.go ├── misc_tests ├── jsoniter_array_test.go ├── jsoniter_bool_test.go ├── jsoniter_float_test.go ├── jsoniter_int_test.go ├── jsoniter_interface_test.go ├── jsoniter_iterator_test.go ├── jsoniter_map_test.go ├── jsoniter_nested_test.go ├── jsoniter_null_test.go ├── jsoniter_object_test.go └── jsoniter_raw_message_test.go ├── pool.go ├── reflect.go ├── reflect_array.go ├── reflect_dynamic.go ├── reflect_extension.go ├── reflect_json_number.go ├── reflect_json_raw_message.go ├── reflect_map.go ├── reflect_marshaler.go ├── reflect_native.go ├── reflect_optional.go ├── reflect_slice.go ├── reflect_struct_decoder.go ├── reflect_struct_encoder.go ├── skip_tests ├── array_test.go ├── float64_test.go ├── jsoniter_skip_test.go ├── skip_test.go ├── string_test.go └── struct_test.go ├── stream.go ├── stream_float.go ├── stream_int.go ├── stream_str.go ├── stream_test.go ├── test.sh ├── type_tests ├── array_test.go ├── builtin_test.go ├── map_key_test.go ├── map_test.go ├── marshaler_string_test.go ├── marshaler_struct_test.go ├── slice_test.go ├── struct_embedded_test.go ├── struct_field_case_test.go ├── struct_tags_test.go ├── struct_test.go ├── text_marshaler_string_test.go ├── text_marshaler_struct_test.go └── type_test.go └── value_tests ├── array_test.go ├── bool_test.go ├── eface_test.go ├── error_test.go ├── float_test.go ├── iface_test.go ├── int_test.go ├── invalid_test.go ├── map_test.go ├── marshaler_test.go ├── number_test.go ├── ptr_114_test.go ├── ptr_test.go ├── raw_message_test.go ├── slice_test.go ├── string_test.go ├── struct_test.go └── value_test.go /.codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "output_tests/.*" 3 | 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | /bug_test.go 3 | /coverage.txt 4 | /.idea 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.8.x 5 | - 1.x 6 | 7 | before_install: 8 | - go get -t -v ./... 9 | 10 | script: 11 | - ./test.sh 12 | 13 | after_success: 14 | - bash <(curl -s https://codecov.io/bash) 15 | -------------------------------------------------------------------------------- /Gopkg.lock: -------------------------------------------------------------------------------- 1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. 2 | 3 | 4 | [[projects]] 5 | name = "github.com/modern-go/concurrent" 6 | packages = ["."] 7 | revision = "e0a39a4cb4216ea8db28e22a69f4ec25610d513a" 8 | version = "1.0.0" 9 | 10 | [[projects]] 11 | name = "github.com/modern-go/reflect2" 12 | packages = ["."] 13 | revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd" 14 | version = "1.0.1" 15 | 16 | [solve-meta] 17 | analyzer-name = "dep" 18 | analyzer-version = 1 19 | inputs-digest = "ea54a775e5a354cb015502d2e7aa4b74230fc77e894f34a838b268c25ec8eeb8" 20 | solver-name = "gps-cdcl" 21 | solver-version = 1 22 | -------------------------------------------------------------------------------- /Gopkg.toml: -------------------------------------------------------------------------------- 1 | # Gopkg.toml example 2 | # 3 | # Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md 4 | # for detailed Gopkg.toml documentation. 5 | # 6 | # required = ["github.com/user/thing/cmd/thing"] 7 | # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] 8 | # 9 | # [[constraint]] 10 | # name = "github.com/user/project" 11 | # version = "1.0.0" 12 | # 13 | # [[constraint]] 14 | # name = "github.com/user/project2" 15 | # branch = "dev" 16 | # source = "github.com/myfork/project2" 17 | # 18 | # [[override]] 19 | # name = "github.com/x/y" 20 | # version = "2.4.0" 21 | 22 | ignored = ["github.com/davecgh/go-spew*","github.com/google/gofuzz*","github.com/stretchr/testify*"] 23 | 24 | [[constraint]] 25 | name = "github.com/modern-go/reflect2" 26 | version = "1.0.1" 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 json-iterator 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Sourcegraph](https://sourcegraph.com/github.com/json-iterator/go/-/badge.svg)](https://sourcegraph.com/github.com/json-iterator/go?badge) 2 | [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](https://pkg.go.dev/github.com/json-iterator/go) 3 | [![Build Status](https://travis-ci.org/json-iterator/go.svg?branch=master)](https://travis-ci.org/json-iterator/go) 4 | [![codecov](https://codecov.io/gh/json-iterator/go/branch/master/graph/badge.svg)](https://codecov.io/gh/json-iterator/go) 5 | [![rcard](https://goreportcard.com/badge/github.com/json-iterator/go)](https://goreportcard.com/report/github.com/json-iterator/go) 6 | [![License](http://img.shields.io/badge/license-mit-blue.svg?style=flat-square)](https://raw.githubusercontent.com/json-iterator/go/master/LICENSE) 7 | [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/json-iterator/Lobby) 8 | 9 | A high-performance 100% compatible drop-in replacement of "encoding/json" 10 | 11 | # Benchmark 12 | 13 | ![benchmark](http://jsoniter.com/benchmarks/go-benchmark.png) 14 | 15 | Source code: https://github.com/json-iterator/go-benchmark/blob/master/src/github.com/json-iterator/go-benchmark/benchmark_medium_payload_test.go 16 | 17 | Raw Result (easyjson requires static code generation) 18 | 19 | | | ns/op | allocation bytes | allocation times | 20 | | --------------- | ----------- | ---------------- | ---------------- | 21 | | std decode | 35510 ns/op | 1960 B/op | 99 allocs/op | 22 | | easyjson decode | 8499 ns/op | 160 B/op | 4 allocs/op | 23 | | jsoniter decode | 5623 ns/op | 160 B/op | 3 allocs/op | 24 | | std encode | 2213 ns/op | 712 B/op | 5 allocs/op | 25 | | easyjson encode | 883 ns/op | 576 B/op | 3 allocs/op | 26 | | jsoniter encode | 837 ns/op | 384 B/op | 4 allocs/op | 27 | 28 | Always benchmark with your own workload. 29 | The result depends heavily on the data input. 30 | 31 | # Usage 32 | 33 | 100% compatibility with standard lib 34 | 35 | Replace 36 | 37 | ```go 38 | import "encoding/json" 39 | json.Marshal(&data) 40 | ``` 41 | 42 | with 43 | 44 | ```go 45 | import jsoniter "github.com/json-iterator/go" 46 | 47 | var json = jsoniter.ConfigCompatibleWithStandardLibrary 48 | json.Marshal(&data) 49 | ``` 50 | 51 | Replace 52 | 53 | ```go 54 | import "encoding/json" 55 | json.Unmarshal(input, &data) 56 | ``` 57 | 58 | with 59 | 60 | ```go 61 | import jsoniter "github.com/json-iterator/go" 62 | 63 | var json = jsoniter.ConfigCompatibleWithStandardLibrary 64 | json.Unmarshal(input, &data) 65 | ``` 66 | 67 | [More documentation](http://jsoniter.com/migrate-from-go-std.html) 68 | 69 | # How to get 70 | 71 | ``` 72 | go get github.com/json-iterator/go 73 | ``` 74 | 75 | # Contribution Welcomed ! 76 | 77 | Contributors 78 | 79 | - [thockin](https://github.com/thockin) 80 | - [mattn](https://github.com/mattn) 81 | - [cch123](https://github.com/cch123) 82 | - [Oleg Shaldybin](https://github.com/olegshaldybin) 83 | - [Jason Toffaletti](https://github.com/toffaletti) 84 | 85 | Report issue or pull request, or email taowen@gmail.com, or [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/json-iterator/Lobby) 86 | -------------------------------------------------------------------------------- /adapter.go: -------------------------------------------------------------------------------- 1 | package jsoniter 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | ) 7 | 8 | // RawMessage to make replace json with jsoniter 9 | type RawMessage []byte 10 | 11 | // Unmarshal adapts to json/encoding Unmarshal API 12 | // 13 | // Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v. 14 | // Refer to https://godoc.org/encoding/json#Unmarshal for more information 15 | func Unmarshal(data []byte, v interface{}) error { 16 | return ConfigDefault.Unmarshal(data, v) 17 | } 18 | 19 | // UnmarshalFromString is a convenient method to read from string instead of []byte 20 | func UnmarshalFromString(str string, v interface{}) error { 21 | return ConfigDefault.UnmarshalFromString(str, v) 22 | } 23 | 24 | // Get quick method to get value from deeply nested JSON structure 25 | func Get(data []byte, path ...interface{}) Any { 26 | return ConfigDefault.Get(data, path...) 27 | } 28 | 29 | // Marshal adapts to json/encoding Marshal API 30 | // 31 | // Marshal returns the JSON encoding of v, adapts to json/encoding Marshal API 32 | // Refer to https://godoc.org/encoding/json#Marshal for more information 33 | func Marshal(v interface{}) ([]byte, error) { 34 | return ConfigDefault.Marshal(v) 35 | } 36 | 37 | // MarshalIndent same as json.MarshalIndent. Prefix is not supported. 38 | func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) { 39 | return ConfigDefault.MarshalIndent(v, prefix, indent) 40 | } 41 | 42 | // MarshalToString convenient method to write as string instead of []byte 43 | func MarshalToString(v interface{}) (string, error) { 44 | return ConfigDefault.MarshalToString(v) 45 | } 46 | 47 | // NewDecoder adapts to json/stream NewDecoder API. 48 | // 49 | // NewDecoder returns a new decoder that reads from r. 50 | // 51 | // Instead of a json/encoding Decoder, an Decoder is returned 52 | // Refer to https://godoc.org/encoding/json#NewDecoder for more information 53 | func NewDecoder(reader io.Reader) *Decoder { 54 | return ConfigDefault.NewDecoder(reader) 55 | } 56 | 57 | // Decoder reads and decodes JSON values from an input stream. 58 | // Decoder provides identical APIs with json/stream Decoder (Token() and UseNumber() are in progress) 59 | type Decoder struct { 60 | iter *Iterator 61 | } 62 | 63 | // Decode decode JSON into interface{} 64 | func (adapter *Decoder) Decode(obj interface{}) error { 65 | if adapter.iter.head == adapter.iter.tail && adapter.iter.reader != nil { 66 | if !adapter.iter.loadMore() { 67 | return io.EOF 68 | } 69 | } 70 | adapter.iter.ReadVal(obj) 71 | err := adapter.iter.Error 72 | if err == io.EOF { 73 | return nil 74 | } 75 | return adapter.iter.Error 76 | } 77 | 78 | // More is there more? 79 | func (adapter *Decoder) More() bool { 80 | iter := adapter.iter 81 | if iter.Error != nil { 82 | return false 83 | } 84 | c := iter.nextToken() 85 | if c == 0 { 86 | return false 87 | } 88 | iter.unreadByte() 89 | return c != ']' && c != '}' 90 | } 91 | 92 | // Buffered remaining buffer 93 | func (adapter *Decoder) Buffered() io.Reader { 94 | remaining := adapter.iter.buf[adapter.iter.head:adapter.iter.tail] 95 | return bytes.NewReader(remaining) 96 | } 97 | 98 | // UseNumber causes the Decoder to unmarshal a number into an interface{} as a 99 | // Number instead of as a float64. 100 | func (adapter *Decoder) UseNumber() { 101 | cfg := adapter.iter.cfg.configBeforeFrozen 102 | cfg.UseNumber = true 103 | adapter.iter.cfg = cfg.frozeWithCacheReuse(adapter.iter.cfg.extraExtensions) 104 | } 105 | 106 | // DisallowUnknownFields causes the Decoder to return an error when the destination 107 | // is a struct and the input contains object keys which do not match any 108 | // non-ignored, exported fields in the destination. 109 | func (adapter *Decoder) DisallowUnknownFields() { 110 | cfg := adapter.iter.cfg.configBeforeFrozen 111 | cfg.DisallowUnknownFields = true 112 | adapter.iter.cfg = cfg.frozeWithCacheReuse(adapter.iter.cfg.extraExtensions) 113 | } 114 | 115 | // NewEncoder same as json.NewEncoder 116 | func NewEncoder(writer io.Writer) *Encoder { 117 | return ConfigDefault.NewEncoder(writer) 118 | } 119 | 120 | // Encoder same as json.Encoder 121 | type Encoder struct { 122 | stream *Stream 123 | } 124 | 125 | // Encode encode interface{} as JSON to io.Writer 126 | func (adapter *Encoder) Encode(val interface{}) error { 127 | adapter.stream.WriteVal(val) 128 | adapter.stream.WriteRaw("\n") 129 | adapter.stream.Flush() 130 | return adapter.stream.Error 131 | } 132 | 133 | // SetIndent set the indention. Prefix is not supported 134 | func (adapter *Encoder) SetIndent(prefix, indent string) { 135 | config := adapter.stream.cfg.configBeforeFrozen 136 | config.IndentionStep = len(indent) 137 | adapter.stream.cfg = config.frozeWithCacheReuse(adapter.stream.cfg.extraExtensions) 138 | } 139 | 140 | // SetEscapeHTML escape html by default, set to false to disable 141 | func (adapter *Encoder) SetEscapeHTML(escapeHTML bool) { 142 | config := adapter.stream.cfg.configBeforeFrozen 143 | config.EscapeHTML = escapeHTML 144 | adapter.stream.cfg = config.frozeWithCacheReuse(adapter.stream.cfg.extraExtensions) 145 | } 146 | 147 | // Valid reports whether data is a valid JSON encoding. 148 | func Valid(data []byte) bool { 149 | return ConfigDefault.Valid(data) 150 | } 151 | -------------------------------------------------------------------------------- /any_bool.go: -------------------------------------------------------------------------------- 1 | package jsoniter 2 | 3 | type trueAny struct { 4 | baseAny 5 | } 6 | 7 | func (any *trueAny) LastError() error { 8 | return nil 9 | } 10 | 11 | func (any *trueAny) ToBool() bool { 12 | return true 13 | } 14 | 15 | func (any *trueAny) ToInt() int { 16 | return 1 17 | } 18 | 19 | func (any *trueAny) ToInt32() int32 { 20 | return 1 21 | } 22 | 23 | func (any *trueAny) ToInt64() int64 { 24 | return 1 25 | } 26 | 27 | func (any *trueAny) ToUint() uint { 28 | return 1 29 | } 30 | 31 | func (any *trueAny) ToUint32() uint32 { 32 | return 1 33 | } 34 | 35 | func (any *trueAny) ToUint64() uint64 { 36 | return 1 37 | } 38 | 39 | func (any *trueAny) ToFloat32() float32 { 40 | return 1 41 | } 42 | 43 | func (any *trueAny) ToFloat64() float64 { 44 | return 1 45 | } 46 | 47 | func (any *trueAny) ToString() string { 48 | return "true" 49 | } 50 | 51 | func (any *trueAny) WriteTo(stream *Stream) { 52 | stream.WriteTrue() 53 | } 54 | 55 | func (any *trueAny) Parse() *Iterator { 56 | return nil 57 | } 58 | 59 | func (any *trueAny) GetInterface() interface{} { 60 | return true 61 | } 62 | 63 | func (any *trueAny) ValueType() ValueType { 64 | return BoolValue 65 | } 66 | 67 | func (any *trueAny) MustBeValid() Any { 68 | return any 69 | } 70 | 71 | type falseAny struct { 72 | baseAny 73 | } 74 | 75 | func (any *falseAny) LastError() error { 76 | return nil 77 | } 78 | 79 | func (any *falseAny) ToBool() bool { 80 | return false 81 | } 82 | 83 | func (any *falseAny) ToInt() int { 84 | return 0 85 | } 86 | 87 | func (any *falseAny) ToInt32() int32 { 88 | return 0 89 | } 90 | 91 | func (any *falseAny) ToInt64() int64 { 92 | return 0 93 | } 94 | 95 | func (any *falseAny) ToUint() uint { 96 | return 0 97 | } 98 | 99 | func (any *falseAny) ToUint32() uint32 { 100 | return 0 101 | } 102 | 103 | func (any *falseAny) ToUint64() uint64 { 104 | return 0 105 | } 106 | 107 | func (any *falseAny) ToFloat32() float32 { 108 | return 0 109 | } 110 | 111 | func (any *falseAny) ToFloat64() float64 { 112 | return 0 113 | } 114 | 115 | func (any *falseAny) ToString() string { 116 | return "false" 117 | } 118 | 119 | func (any *falseAny) WriteTo(stream *Stream) { 120 | stream.WriteFalse() 121 | } 122 | 123 | func (any *falseAny) Parse() *Iterator { 124 | return nil 125 | } 126 | 127 | func (any *falseAny) GetInterface() interface{} { 128 | return false 129 | } 130 | 131 | func (any *falseAny) ValueType() ValueType { 132 | return BoolValue 133 | } 134 | 135 | func (any *falseAny) MustBeValid() Any { 136 | return any 137 | } 138 | -------------------------------------------------------------------------------- /any_float.go: -------------------------------------------------------------------------------- 1 | package jsoniter 2 | 3 | import ( 4 | "strconv" 5 | ) 6 | 7 | type floatAny struct { 8 | baseAny 9 | val float64 10 | } 11 | 12 | func (any *floatAny) Parse() *Iterator { 13 | return nil 14 | } 15 | 16 | func (any *floatAny) ValueType() ValueType { 17 | return NumberValue 18 | } 19 | 20 | func (any *floatAny) MustBeValid() Any { 21 | return any 22 | } 23 | 24 | func (any *floatAny) LastError() error { 25 | return nil 26 | } 27 | 28 | func (any *floatAny) ToBool() bool { 29 | return any.ToFloat64() != 0 30 | } 31 | 32 | func (any *floatAny) ToInt() int { 33 | return int(any.val) 34 | } 35 | 36 | func (any *floatAny) ToInt32() int32 { 37 | return int32(any.val) 38 | } 39 | 40 | func (any *floatAny) ToInt64() int64 { 41 | return int64(any.val) 42 | } 43 | 44 | func (any *floatAny) ToUint() uint { 45 | if any.val > 0 { 46 | return uint(any.val) 47 | } 48 | return 0 49 | } 50 | 51 | func (any *floatAny) ToUint32() uint32 { 52 | if any.val > 0 { 53 | return uint32(any.val) 54 | } 55 | return 0 56 | } 57 | 58 | func (any *floatAny) ToUint64() uint64 { 59 | if any.val > 0 { 60 | return uint64(any.val) 61 | } 62 | return 0 63 | } 64 | 65 | func (any *floatAny) ToFloat32() float32 { 66 | return float32(any.val) 67 | } 68 | 69 | func (any *floatAny) ToFloat64() float64 { 70 | return any.val 71 | } 72 | 73 | func (any *floatAny) ToString() string { 74 | return strconv.FormatFloat(any.val, 'E', -1, 64) 75 | } 76 | 77 | func (any *floatAny) WriteTo(stream *Stream) { 78 | stream.WriteFloat64(any.val) 79 | } 80 | 81 | func (any *floatAny) GetInterface() interface{} { 82 | return any.val 83 | } 84 | -------------------------------------------------------------------------------- /any_int32.go: -------------------------------------------------------------------------------- 1 | package jsoniter 2 | 3 | import ( 4 | "strconv" 5 | ) 6 | 7 | type int32Any struct { 8 | baseAny 9 | val int32 10 | } 11 | 12 | func (any *int32Any) LastError() error { 13 | return nil 14 | } 15 | 16 | func (any *int32Any) ValueType() ValueType { 17 | return NumberValue 18 | } 19 | 20 | func (any *int32Any) MustBeValid() Any { 21 | return any 22 | } 23 | 24 | func (any *int32Any) ToBool() bool { 25 | return any.val != 0 26 | } 27 | 28 | func (any *int32Any) ToInt() int { 29 | return int(any.val) 30 | } 31 | 32 | func (any *int32Any) ToInt32() int32 { 33 | return any.val 34 | } 35 | 36 | func (any *int32Any) ToInt64() int64 { 37 | return int64(any.val) 38 | } 39 | 40 | func (any *int32Any) ToUint() uint { 41 | return uint(any.val) 42 | } 43 | 44 | func (any *int32Any) ToUint32() uint32 { 45 | return uint32(any.val) 46 | } 47 | 48 | func (any *int32Any) ToUint64() uint64 { 49 | return uint64(any.val) 50 | } 51 | 52 | func (any *int32Any) ToFloat32() float32 { 53 | return float32(any.val) 54 | } 55 | 56 | func (any *int32Any) ToFloat64() float64 { 57 | return float64(any.val) 58 | } 59 | 60 | func (any *int32Any) ToString() string { 61 | return strconv.FormatInt(int64(any.val), 10) 62 | } 63 | 64 | func (any *int32Any) WriteTo(stream *Stream) { 65 | stream.WriteInt32(any.val) 66 | } 67 | 68 | func (any *int32Any) Parse() *Iterator { 69 | return nil 70 | } 71 | 72 | func (any *int32Any) GetInterface() interface{} { 73 | return any.val 74 | } 75 | -------------------------------------------------------------------------------- /any_int64.go: -------------------------------------------------------------------------------- 1 | package jsoniter 2 | 3 | import ( 4 | "strconv" 5 | ) 6 | 7 | type int64Any struct { 8 | baseAny 9 | val int64 10 | } 11 | 12 | func (any *int64Any) LastError() error { 13 | return nil 14 | } 15 | 16 | func (any *int64Any) ValueType() ValueType { 17 | return NumberValue 18 | } 19 | 20 | func (any *int64Any) MustBeValid() Any { 21 | return any 22 | } 23 | 24 | func (any *int64Any) ToBool() bool { 25 | return any.val != 0 26 | } 27 | 28 | func (any *int64Any) ToInt() int { 29 | return int(any.val) 30 | } 31 | 32 | func (any *int64Any) ToInt32() int32 { 33 | return int32(any.val) 34 | } 35 | 36 | func (any *int64Any) ToInt64() int64 { 37 | return any.val 38 | } 39 | 40 | func (any *int64Any) ToUint() uint { 41 | return uint(any.val) 42 | } 43 | 44 | func (any *int64Any) ToUint32() uint32 { 45 | return uint32(any.val) 46 | } 47 | 48 | func (any *int64Any) ToUint64() uint64 { 49 | return uint64(any.val) 50 | } 51 | 52 | func (any *int64Any) ToFloat32() float32 { 53 | return float32(any.val) 54 | } 55 | 56 | func (any *int64Any) ToFloat64() float64 { 57 | return float64(any.val) 58 | } 59 | 60 | func (any *int64Any) ToString() string { 61 | return strconv.FormatInt(any.val, 10) 62 | } 63 | 64 | func (any *int64Any) WriteTo(stream *Stream) { 65 | stream.WriteInt64(any.val) 66 | } 67 | 68 | func (any *int64Any) Parse() *Iterator { 69 | return nil 70 | } 71 | 72 | func (any *int64Any) GetInterface() interface{} { 73 | return any.val 74 | } 75 | -------------------------------------------------------------------------------- /any_invalid.go: -------------------------------------------------------------------------------- 1 | package jsoniter 2 | 3 | import "fmt" 4 | 5 | type invalidAny struct { 6 | baseAny 7 | err error 8 | } 9 | 10 | func newInvalidAny(path []interface{}) *invalidAny { 11 | return &invalidAny{baseAny{}, fmt.Errorf("%v not found", path)} 12 | } 13 | 14 | func (any *invalidAny) LastError() error { 15 | return any.err 16 | } 17 | 18 | func (any *invalidAny) ValueType() ValueType { 19 | return InvalidValue 20 | } 21 | 22 | func (any *invalidAny) MustBeValid() Any { 23 | panic(any.err) 24 | } 25 | 26 | func (any *invalidAny) ToBool() bool { 27 | return false 28 | } 29 | 30 | func (any *invalidAny) ToInt() int { 31 | return 0 32 | } 33 | 34 | func (any *invalidAny) ToInt32() int32 { 35 | return 0 36 | } 37 | 38 | func (any *invalidAny) ToInt64() int64 { 39 | return 0 40 | } 41 | 42 | func (any *invalidAny) ToUint() uint { 43 | return 0 44 | } 45 | 46 | func (any *invalidAny) ToUint32() uint32 { 47 | return 0 48 | } 49 | 50 | func (any *invalidAny) ToUint64() uint64 { 51 | return 0 52 | } 53 | 54 | func (any *invalidAny) ToFloat32() float32 { 55 | return 0 56 | } 57 | 58 | func (any *invalidAny) ToFloat64() float64 { 59 | return 0 60 | } 61 | 62 | func (any *invalidAny) ToString() string { 63 | return "" 64 | } 65 | 66 | func (any *invalidAny) WriteTo(stream *Stream) { 67 | } 68 | 69 | func (any *invalidAny) Get(path ...interface{}) Any { 70 | if any.err == nil { 71 | return &invalidAny{baseAny{}, fmt.Errorf("get %v from invalid", path)} 72 | } 73 | return &invalidAny{baseAny{}, fmt.Errorf("%v, get %v from invalid", any.err, path)} 74 | } 75 | 76 | func (any *invalidAny) Parse() *Iterator { 77 | return nil 78 | } 79 | 80 | func (any *invalidAny) GetInterface() interface{} { 81 | return nil 82 | } 83 | -------------------------------------------------------------------------------- /any_nil.go: -------------------------------------------------------------------------------- 1 | package jsoniter 2 | 3 | type nilAny struct { 4 | baseAny 5 | } 6 | 7 | func (any *nilAny) LastError() error { 8 | return nil 9 | } 10 | 11 | func (any *nilAny) ValueType() ValueType { 12 | return NilValue 13 | } 14 | 15 | func (any *nilAny) MustBeValid() Any { 16 | return any 17 | } 18 | 19 | func (any *nilAny) ToBool() bool { 20 | return false 21 | } 22 | 23 | func (any *nilAny) ToInt() int { 24 | return 0 25 | } 26 | 27 | func (any *nilAny) ToInt32() int32 { 28 | return 0 29 | } 30 | 31 | func (any *nilAny) ToInt64() int64 { 32 | return 0 33 | } 34 | 35 | func (any *nilAny) ToUint() uint { 36 | return 0 37 | } 38 | 39 | func (any *nilAny) ToUint32() uint32 { 40 | return 0 41 | } 42 | 43 | func (any *nilAny) ToUint64() uint64 { 44 | return 0 45 | } 46 | 47 | func (any *nilAny) ToFloat32() float32 { 48 | return 0 49 | } 50 | 51 | func (any *nilAny) ToFloat64() float64 { 52 | return 0 53 | } 54 | 55 | func (any *nilAny) ToString() string { 56 | return "" 57 | } 58 | 59 | func (any *nilAny) WriteTo(stream *Stream) { 60 | stream.WriteNil() 61 | } 62 | 63 | func (any *nilAny) Parse() *Iterator { 64 | return nil 65 | } 66 | 67 | func (any *nilAny) GetInterface() interface{} { 68 | return nil 69 | } 70 | -------------------------------------------------------------------------------- /any_number.go: -------------------------------------------------------------------------------- 1 | package jsoniter 2 | 3 | import ( 4 | "io" 5 | "unsafe" 6 | ) 7 | 8 | type numberLazyAny struct { 9 | baseAny 10 | cfg *frozenConfig 11 | buf []byte 12 | err error 13 | } 14 | 15 | func (any *numberLazyAny) ValueType() ValueType { 16 | return NumberValue 17 | } 18 | 19 | func (any *numberLazyAny) MustBeValid() Any { 20 | return any 21 | } 22 | 23 | func (any *numberLazyAny) LastError() error { 24 | return any.err 25 | } 26 | 27 | func (any *numberLazyAny) ToBool() bool { 28 | return any.ToFloat64() != 0 29 | } 30 | 31 | func (any *numberLazyAny) ToInt() int { 32 | iter := any.cfg.BorrowIterator(any.buf) 33 | defer any.cfg.ReturnIterator(iter) 34 | val := iter.ReadInt() 35 | if iter.Error != nil && iter.Error != io.EOF { 36 | any.err = iter.Error 37 | } 38 | return val 39 | } 40 | 41 | func (any *numberLazyAny) ToInt32() int32 { 42 | iter := any.cfg.BorrowIterator(any.buf) 43 | defer any.cfg.ReturnIterator(iter) 44 | val := iter.ReadInt32() 45 | if iter.Error != nil && iter.Error != io.EOF { 46 | any.err = iter.Error 47 | } 48 | return val 49 | } 50 | 51 | func (any *numberLazyAny) ToInt64() int64 { 52 | iter := any.cfg.BorrowIterator(any.buf) 53 | defer any.cfg.ReturnIterator(iter) 54 | val := iter.ReadInt64() 55 | if iter.Error != nil && iter.Error != io.EOF { 56 | any.err = iter.Error 57 | } 58 | return val 59 | } 60 | 61 | func (any *numberLazyAny) ToUint() uint { 62 | iter := any.cfg.BorrowIterator(any.buf) 63 | defer any.cfg.ReturnIterator(iter) 64 | val := iter.ReadUint() 65 | if iter.Error != nil && iter.Error != io.EOF { 66 | any.err = iter.Error 67 | } 68 | return val 69 | } 70 | 71 | func (any *numberLazyAny) ToUint32() uint32 { 72 | iter := any.cfg.BorrowIterator(any.buf) 73 | defer any.cfg.ReturnIterator(iter) 74 | val := iter.ReadUint32() 75 | if iter.Error != nil && iter.Error != io.EOF { 76 | any.err = iter.Error 77 | } 78 | return val 79 | } 80 | 81 | func (any *numberLazyAny) ToUint64() uint64 { 82 | iter := any.cfg.BorrowIterator(any.buf) 83 | defer any.cfg.ReturnIterator(iter) 84 | val := iter.ReadUint64() 85 | if iter.Error != nil && iter.Error != io.EOF { 86 | any.err = iter.Error 87 | } 88 | return val 89 | } 90 | 91 | func (any *numberLazyAny) ToFloat32() float32 { 92 | iter := any.cfg.BorrowIterator(any.buf) 93 | defer any.cfg.ReturnIterator(iter) 94 | val := iter.ReadFloat32() 95 | if iter.Error != nil && iter.Error != io.EOF { 96 | any.err = iter.Error 97 | } 98 | return val 99 | } 100 | 101 | func (any *numberLazyAny) ToFloat64() float64 { 102 | iter := any.cfg.BorrowIterator(any.buf) 103 | defer any.cfg.ReturnIterator(iter) 104 | val := iter.ReadFloat64() 105 | if iter.Error != nil && iter.Error != io.EOF { 106 | any.err = iter.Error 107 | } 108 | return val 109 | } 110 | 111 | func (any *numberLazyAny) ToString() string { 112 | return *(*string)(unsafe.Pointer(&any.buf)) 113 | } 114 | 115 | func (any *numberLazyAny) WriteTo(stream *Stream) { 116 | stream.Write(any.buf) 117 | } 118 | 119 | func (any *numberLazyAny) GetInterface() interface{} { 120 | iter := any.cfg.BorrowIterator(any.buf) 121 | defer any.cfg.ReturnIterator(iter) 122 | return iter.Read() 123 | } 124 | -------------------------------------------------------------------------------- /any_str.go: -------------------------------------------------------------------------------- 1 | package jsoniter 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | ) 7 | 8 | type stringAny struct { 9 | baseAny 10 | val string 11 | } 12 | 13 | func (any *stringAny) Get(path ...interface{}) Any { 14 | if len(path) == 0 { 15 | return any 16 | } 17 | return &invalidAny{baseAny{}, fmt.Errorf("GetIndex %v from simple value", path)} 18 | } 19 | 20 | func (any *stringAny) Parse() *Iterator { 21 | return nil 22 | } 23 | 24 | func (any *stringAny) ValueType() ValueType { 25 | return StringValue 26 | } 27 | 28 | func (any *stringAny) MustBeValid() Any { 29 | return any 30 | } 31 | 32 | func (any *stringAny) LastError() error { 33 | return nil 34 | } 35 | 36 | func (any *stringAny) ToBool() bool { 37 | str := any.ToString() 38 | if str == "0" { 39 | return false 40 | } 41 | for _, c := range str { 42 | switch c { 43 | case ' ', '\n', '\r', '\t': 44 | default: 45 | return true 46 | } 47 | } 48 | return false 49 | } 50 | 51 | func (any *stringAny) ToInt() int { 52 | return int(any.ToInt64()) 53 | 54 | } 55 | 56 | func (any *stringAny) ToInt32() int32 { 57 | return int32(any.ToInt64()) 58 | } 59 | 60 | func (any *stringAny) ToInt64() int64 { 61 | if any.val == "" { 62 | return 0 63 | } 64 | 65 | flag := 1 66 | startPos := 0 67 | if any.val[0] == '+' || any.val[0] == '-' { 68 | startPos = 1 69 | } 70 | 71 | if any.val[0] == '-' { 72 | flag = -1 73 | } 74 | 75 | endPos := startPos 76 | for i := startPos; i < len(any.val); i++ { 77 | if any.val[i] >= '0' && any.val[i] <= '9' { 78 | endPos = i + 1 79 | } else { 80 | break 81 | } 82 | } 83 | parsed, _ := strconv.ParseInt(any.val[startPos:endPos], 10, 64) 84 | return int64(flag) * parsed 85 | } 86 | 87 | func (any *stringAny) ToUint() uint { 88 | return uint(any.ToUint64()) 89 | } 90 | 91 | func (any *stringAny) ToUint32() uint32 { 92 | return uint32(any.ToUint64()) 93 | } 94 | 95 | func (any *stringAny) ToUint64() uint64 { 96 | if any.val == "" { 97 | return 0 98 | } 99 | 100 | startPos := 0 101 | 102 | if any.val[0] == '-' { 103 | return 0 104 | } 105 | if any.val[0] == '+' { 106 | startPos = 1 107 | } 108 | 109 | endPos := startPos 110 | for i := startPos; i < len(any.val); i++ { 111 | if any.val[i] >= '0' && any.val[i] <= '9' { 112 | endPos = i + 1 113 | } else { 114 | break 115 | } 116 | } 117 | parsed, _ := strconv.ParseUint(any.val[startPos:endPos], 10, 64) 118 | return parsed 119 | } 120 | 121 | func (any *stringAny) ToFloat32() float32 { 122 | return float32(any.ToFloat64()) 123 | } 124 | 125 | func (any *stringAny) ToFloat64() float64 { 126 | if len(any.val) == 0 { 127 | return 0 128 | } 129 | 130 | // first char invalid 131 | if any.val[0] != '+' && any.val[0] != '-' && (any.val[0] > '9' || any.val[0] < '0') { 132 | return 0 133 | } 134 | 135 | // extract valid num expression from string 136 | // eg 123true => 123, -12.12xxa => -12.12 137 | endPos := 1 138 | for i := 1; i < len(any.val); i++ { 139 | if any.val[i] == '.' || any.val[i] == 'e' || any.val[i] == 'E' || any.val[i] == '+' || any.val[i] == '-' { 140 | endPos = i + 1 141 | continue 142 | } 143 | 144 | // end position is the first char which is not digit 145 | if any.val[i] >= '0' && any.val[i] <= '9' { 146 | endPos = i + 1 147 | } else { 148 | endPos = i 149 | break 150 | } 151 | } 152 | parsed, _ := strconv.ParseFloat(any.val[:endPos], 64) 153 | return parsed 154 | } 155 | 156 | func (any *stringAny) ToString() string { 157 | return any.val 158 | } 159 | 160 | func (any *stringAny) WriteTo(stream *Stream) { 161 | stream.WriteString(any.val) 162 | } 163 | 164 | func (any *stringAny) GetInterface() interface{} { 165 | return any.val 166 | } 167 | -------------------------------------------------------------------------------- /any_tests/jsoniter_any_array_test.go: -------------------------------------------------------------------------------- 1 | package any_tests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/json-iterator/go" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func Test_read_empty_array_as_any(t *testing.T) { 11 | should := require.New(t) 12 | any := jsoniter.Get([]byte("[]")) 13 | should.Equal(jsoniter.ArrayValue, any.Get().ValueType()) 14 | should.Equal(jsoniter.InvalidValue, any.Get(0.3).ValueType()) 15 | should.Equal(0, any.Size()) 16 | should.Equal(jsoniter.ArrayValue, any.ValueType()) 17 | should.Nil(any.LastError()) 18 | should.Equal(0, any.ToInt()) 19 | should.Equal(int32(0), any.ToInt32()) 20 | should.Equal(int64(0), any.ToInt64()) 21 | should.Equal(uint(0), any.ToUint()) 22 | should.Equal(uint32(0), any.ToUint32()) 23 | should.Equal(uint64(0), any.ToUint64()) 24 | should.Equal(float32(0), any.ToFloat32()) 25 | should.Equal(float64(0), any.ToFloat64()) 26 | } 27 | 28 | func Test_read_one_element_array_as_any(t *testing.T) { 29 | should := require.New(t) 30 | any := jsoniter.Get([]byte("[1]")) 31 | should.Equal(1, any.Size()) 32 | } 33 | 34 | func Test_read_two_element_array_as_any(t *testing.T) { 35 | should := require.New(t) 36 | any := jsoniter.Get([]byte("[1,2]")) 37 | should.Equal(1, any.Get(0).ToInt()) 38 | should.Equal(2, any.Size()) 39 | should.True(any.ToBool()) 40 | should.Equal(1, any.ToInt()) 41 | should.Equal([]interface{}{float64(1), float64(2)}, any.GetInterface()) 42 | stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32) 43 | any.WriteTo(stream) 44 | should.Equal("[1,2]", string(stream.Buffer())) 45 | arr := []int{} 46 | any.ToVal(&arr) 47 | should.Equal([]int{1, 2}, arr) 48 | } 49 | 50 | func Test_wrap_array_and_convert_to_any(t *testing.T) { 51 | should := require.New(t) 52 | any := jsoniter.Wrap([]int{1, 2, 3}) 53 | any2 := jsoniter.Wrap([]int{}) 54 | 55 | should.Equal("[1,2,3]", any.ToString()) 56 | should.True(any.ToBool()) 57 | should.False(any2.ToBool()) 58 | 59 | should.Equal(1, any.ToInt()) 60 | should.Equal(0, any2.ToInt()) 61 | should.Equal(int32(1), any.ToInt32()) 62 | should.Equal(int32(0), any2.ToInt32()) 63 | should.Equal(int64(1), any.ToInt64()) 64 | should.Equal(int64(0), any2.ToInt64()) 65 | should.Equal(uint(1), any.ToUint()) 66 | should.Equal(uint(0), any2.ToUint()) 67 | should.Equal(uint32(1), any.ToUint32()) 68 | should.Equal(uint32(0), any2.ToUint32()) 69 | should.Equal(uint64(1), any.ToUint64()) 70 | should.Equal(uint64(0), any2.ToUint64()) 71 | should.Equal(float32(1), any.ToFloat32()) 72 | should.Equal(float32(0), any2.ToFloat32()) 73 | should.Equal(float64(1), any.ToFloat64()) 74 | should.Equal(float64(0), any2.ToFloat64()) 75 | should.Equal(3, any.Size()) 76 | should.Equal(0, any2.Size()) 77 | 78 | var i interface{} = []int{1, 2, 3} 79 | should.Equal(i, any.GetInterface()) 80 | } 81 | 82 | func Test_array_lazy_any_get(t *testing.T) { 83 | should := require.New(t) 84 | any := jsoniter.Get([]byte("[1,[2,3],4]")) 85 | should.Equal(3, any.Get(1, 1).ToInt()) 86 | should.Equal("[1,[2,3],4]", any.ToString()) 87 | } 88 | 89 | func Test_array_lazy_any_get_all(t *testing.T) { 90 | should := require.New(t) 91 | any := jsoniter.Get([]byte("[[1],[2],[3,4]]")) 92 | should.Equal("[1,2,3]", any.Get('*', 0).ToString()) 93 | any = jsoniter.Get([]byte("[[[1],[2],[3,4]]]"), 0, '*', 0) 94 | should.Equal("[1,2,3]", any.ToString()) 95 | } 96 | 97 | func Test_array_wrapper_any_get_all(t *testing.T) { 98 | should := require.New(t) 99 | any := jsoniter.Wrap([][]int{ 100 | {1, 2}, 101 | {3, 4}, 102 | {5, 6}, 103 | }) 104 | should.Equal("[1,3,5]", any.Get('*', 0).ToString()) 105 | should.Equal(jsoniter.ArrayValue, any.ValueType()) 106 | should.True(any.ToBool()) 107 | should.Equal(1, any.Get(0, 0).ToInt()) 108 | } 109 | 110 | func Test_array_lazy_any_get_invalid(t *testing.T) { 111 | should := require.New(t) 112 | any := jsoniter.Get([]byte("[]")) 113 | should.Equal(jsoniter.InvalidValue, any.Get(1, 1).ValueType()) 114 | should.NotNil(any.Get(1, 1).LastError()) 115 | should.Equal(jsoniter.InvalidValue, any.Get("1").ValueType()) 116 | should.NotNil(any.Get("1").LastError()) 117 | } 118 | 119 | func Test_invalid_array(t *testing.T) { 120 | should := require.New(t) 121 | any := jsoniter.Get([]byte("["), 0) 122 | should.Equal(jsoniter.InvalidValue, any.ValueType()) 123 | } 124 | -------------------------------------------------------------------------------- /any_tests/jsoniter_any_bool_test.go: -------------------------------------------------------------------------------- 1 | package any_tests 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/json-iterator/go" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | var boolConvertMap = map[string]bool{ 12 | "null": false, 13 | "true": true, 14 | "false": false, 15 | 16 | `"true"`: true, 17 | `"false"`: true, 18 | 19 | "123": true, 20 | `"123"`: true, 21 | "0": false, 22 | `"0"`: false, 23 | "-1": true, 24 | `"-1"`: true, 25 | 26 | "1.1": true, 27 | "0.0": false, 28 | "-1.1": true, 29 | `""`: false, 30 | "[1,2]": true, 31 | "[]": false, 32 | "{}": true, 33 | `{"abc":1}`: true, 34 | } 35 | 36 | func Test_read_bool_as_any(t *testing.T) { 37 | should := require.New(t) 38 | 39 | var any jsoniter.Any 40 | for k, v := range boolConvertMap { 41 | any = jsoniter.Get([]byte(k)) 42 | if v { 43 | should.True(any.ToBool(), fmt.Sprintf("origin val is %v", k)) 44 | } else { 45 | should.False(any.ToBool(), fmt.Sprintf("origin val is %v", k)) 46 | } 47 | } 48 | 49 | } 50 | 51 | func Test_write_bool_to_stream(t *testing.T) { 52 | should := require.New(t) 53 | any := jsoniter.Get([]byte("true")) 54 | stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32) 55 | any.WriteTo(stream) 56 | should.Equal("true", string(stream.Buffer())) 57 | should.Equal(any.ValueType(), jsoniter.BoolValue) 58 | 59 | any = jsoniter.Get([]byte("false")) 60 | stream = jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32) 61 | any.WriteTo(stream) 62 | should.Equal("false", string(stream.Buffer())) 63 | 64 | should.Equal(any.ValueType(), jsoniter.BoolValue) 65 | } 66 | -------------------------------------------------------------------------------- /any_tests/jsoniter_any_float_test.go: -------------------------------------------------------------------------------- 1 | package any_tests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/json-iterator/go" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | var floatConvertMap = map[string]float64{ 11 | "null": 0, 12 | "true": 1, 13 | "false": 0, 14 | 15 | `"true"`: 0, 16 | `"false"`: 0, 17 | 18 | "1e1": 10, 19 | "1e+1": 10, 20 | "1e-1": .1, 21 | "1E1": 10, 22 | "1E+1": 10, 23 | "1E-1": .1, 24 | 25 | "-1e1": -10, 26 | "-1e+1": -10, 27 | "-1e-1": -.1, 28 | "-1E1": -10, 29 | "-1E+1": -10, 30 | "-1E-1": -.1, 31 | 32 | `"1e1"`: 10, 33 | `"1e+1"`: 10, 34 | `"1e-1"`: .1, 35 | `"1E1"`: 10, 36 | `"1E+1"`: 10, 37 | `"1E-1"`: .1, 38 | 39 | `"-1e1"`: -10, 40 | `"-1e+1"`: -10, 41 | `"-1e-1"`: -.1, 42 | `"-1E1"`: -10, 43 | `"-1E+1"`: -10, 44 | `"-1E-1"`: -.1, 45 | 46 | "123": 123, 47 | `"123true"`: 123, 48 | `"+"`: 0, 49 | `"-"`: 0, 50 | 51 | `"-123true"`: -123, 52 | `"-99.9true"`: -99.9, 53 | "0": 0, 54 | `"0"`: 0, 55 | "-1": -1, 56 | 57 | "1.1": 1.1, 58 | "0.0": 0, 59 | "-1.1": -1.1, 60 | `"+1.1"`: 1.1, 61 | `""`: 0, 62 | "[1,2]": 1, 63 | "[]": 0, 64 | "{}": 0, 65 | `{"abc":1}`: 0, 66 | } 67 | 68 | func Test_read_any_to_float(t *testing.T) { 69 | should := require.New(t) 70 | for k, v := range floatConvertMap { 71 | any := jsoniter.Get([]byte(k)) 72 | should.Equal(float64(v), any.ToFloat64(), "the original val is "+k) 73 | } 74 | 75 | for k, v := range floatConvertMap { 76 | any := jsoniter.Get([]byte(k)) 77 | should.Equal(float32(v), any.ToFloat32(), "the original val is "+k) 78 | } 79 | } 80 | 81 | func Test_read_float_to_any(t *testing.T) { 82 | should := require.New(t) 83 | any := jsoniter.WrapFloat64(12.3) 84 | anyFloat64 := float64(12.3) 85 | any2 := jsoniter.WrapFloat64(-1.1) 86 | should.Equal(float64(12.3), any.ToFloat64()) 87 | should.True(any.ToBool()) 88 | should.Equal(float32(anyFloat64), any.ToFloat32()) 89 | should.Equal(int(anyFloat64), any.ToInt()) 90 | should.Equal(int32(anyFloat64), any.ToInt32()) 91 | should.Equal(int64(anyFloat64), any.ToInt64()) 92 | should.Equal(uint(anyFloat64), any.ToUint()) 93 | should.Equal(uint32(anyFloat64), any.ToUint32()) 94 | should.Equal(uint64(anyFloat64), any.ToUint64()) 95 | should.Equal(uint(0), any2.ToUint()) 96 | should.Equal(uint32(0), any2.ToUint32()) 97 | should.Equal(uint64(0), any2.ToUint64()) 98 | should.Equal(any.ValueType(), jsoniter.NumberValue) 99 | 100 | should.Equal("1.23E+01", any.ToString()) 101 | } 102 | -------------------------------------------------------------------------------- /any_tests/jsoniter_any_map_test.go: -------------------------------------------------------------------------------- 1 | package any_tests 2 | 3 | import ( 4 | "github.com/json-iterator/go" 5 | "github.com/stretchr/testify/require" 6 | "testing" 7 | ) 8 | 9 | func Test_wrap_map(t *testing.T) { 10 | should := require.New(t) 11 | any := jsoniter.Wrap(map[string]string{"Field1": "hello"}) 12 | should.Equal("hello", any.Get("Field1").ToString()) 13 | any = jsoniter.Wrap(map[string]string{"Field1": "hello"}) 14 | should.Equal(1, any.Size()) 15 | } 16 | 17 | func Test_map_wrapper_any_get_all(t *testing.T) { 18 | should := require.New(t) 19 | any := jsoniter.Wrap(map[string][]int{"Field1": {1, 2}}) 20 | should.Equal(`{"Field1":1}`, any.Get('*', 0).ToString()) 21 | should.Contains(any.Keys(), "Field1") 22 | 23 | // map write to 24 | stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 0) 25 | any.WriteTo(stream) 26 | // TODO cannot pass 27 | //should.Equal(string(stream.buf), "") 28 | } 29 | -------------------------------------------------------------------------------- /any_tests/jsoniter_any_null_test.go: -------------------------------------------------------------------------------- 1 | package any_tests 2 | 3 | import ( 4 | "github.com/json-iterator/go" 5 | "github.com/stretchr/testify/require" 6 | "testing" 7 | ) 8 | 9 | func Test_read_null_as_any(t *testing.T) { 10 | should := require.New(t) 11 | any := jsoniter.Get([]byte(`null`)) 12 | should.Equal(0, any.ToInt()) 13 | should.Equal(float64(0), any.ToFloat64()) 14 | should.Equal("", any.ToString()) 15 | should.False(any.ToBool()) 16 | } 17 | -------------------------------------------------------------------------------- /any_tests/jsoniter_any_object_test.go: -------------------------------------------------------------------------------- 1 | package any_tests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/json-iterator/go" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func Test_read_object_as_any(t *testing.T) { 11 | should := require.New(t) 12 | any := jsoniter.Get([]byte(`{"a":"stream","c":"d"}`)) 13 | should.Equal(`{"a":"stream","c":"d"}`, any.ToString()) 14 | // partial parse 15 | should.Equal("stream", any.Get("a").ToString()) 16 | should.Equal("d", any.Get("c").ToString()) 17 | should.Equal(2, len(any.Keys())) 18 | any = jsoniter.Get([]byte(`{"a":"stream","c":"d"}`)) 19 | // full parse 20 | should.Equal(2, len(any.Keys())) 21 | should.Equal(2, any.Size()) 22 | should.True(any.ToBool()) 23 | should.Equal(0, any.ToInt()) 24 | should.Equal(jsoniter.ObjectValue, any.ValueType()) 25 | should.Nil(any.LastError()) 26 | obj := struct { 27 | A string 28 | }{} 29 | any.ToVal(&obj) 30 | should.Equal("stream", obj.A) 31 | } 32 | 33 | func Test_object_lazy_any_get(t *testing.T) { 34 | should := require.New(t) 35 | any := jsoniter.Get([]byte(`{"a":{"stream":{"c":"d"}}}`)) 36 | should.Equal("d", any.Get("a", "stream", "c").ToString()) 37 | } 38 | 39 | func Test_object_lazy_any_get_all(t *testing.T) { 40 | should := require.New(t) 41 | any := jsoniter.Get([]byte(`{"a":[0],"stream":[1]}`)) 42 | should.Contains(any.Get('*', 0).ToString(), `"a":0`) 43 | } 44 | 45 | func Test_object_lazy_any_get_invalid(t *testing.T) { 46 | should := require.New(t) 47 | any := jsoniter.Get([]byte(`{}`)) 48 | should.Equal(jsoniter.InvalidValue, any.Get("a", "stream", "c").ValueType()) 49 | should.Equal(jsoniter.InvalidValue, any.Get(1).ValueType()) 50 | } 51 | 52 | func Test_wrap_map_and_convert_to_any(t *testing.T) { 53 | should := require.New(t) 54 | any := jsoniter.Wrap(map[string]interface{}{"a": 1}) 55 | should.True(any.ToBool()) 56 | should.Equal(0, any.ToInt()) 57 | should.Equal(int32(0), any.ToInt32()) 58 | should.Equal(int64(0), any.ToInt64()) 59 | should.Equal(float32(0), any.ToFloat32()) 60 | should.Equal(float64(0), any.ToFloat64()) 61 | should.Equal(uint(0), any.ToUint()) 62 | should.Equal(uint32(0), any.ToUint32()) 63 | should.Equal(uint64(0), any.ToUint64()) 64 | } 65 | 66 | func Test_wrap_object_and_convert_to_any(t *testing.T) { 67 | should := require.New(t) 68 | type TestObject struct { 69 | Field1 string 70 | field2 string 71 | } 72 | any := jsoniter.Wrap(TestObject{"hello", "world"}) 73 | should.Equal("hello", any.Get("Field1").ToString()) 74 | any = jsoniter.Wrap(TestObject{"hello", "world"}) 75 | should.Equal(2, any.Size()) 76 | should.Equal(`{"Field1":"hello"}`, any.Get('*').ToString()) 77 | 78 | should.Equal(0, any.ToInt()) 79 | should.Equal(int32(0), any.ToInt32()) 80 | should.Equal(int64(0), any.ToInt64()) 81 | should.Equal(float32(0), any.ToFloat32()) 82 | should.Equal(float64(0), any.ToFloat64()) 83 | should.Equal(uint(0), any.ToUint()) 84 | should.Equal(uint32(0), any.ToUint32()) 85 | should.Equal(uint64(0), any.ToUint64()) 86 | should.True(any.ToBool()) 87 | should.Equal(`{"Field1":"hello"}`, any.ToString()) 88 | 89 | // cannot pass! 90 | //stream := NewStream(ConfigDefault, nil, 32) 91 | //any.WriteTo(stream) 92 | //should.Equal(`{"Field1":"hello"}`, string(stream.Buffer())) 93 | // cannot pass! 94 | 95 | } 96 | 97 | func Test_any_within_struct(t *testing.T) { 98 | should := require.New(t) 99 | type TestObject struct { 100 | Field1 jsoniter.Any 101 | Field2 jsoniter.Any 102 | } 103 | obj := TestObject{} 104 | err := jsoniter.UnmarshalFromString(`{"Field1": "hello", "Field2": [1,2,3]}`, &obj) 105 | should.Nil(err) 106 | should.Equal("hello", obj.Field1.ToString()) 107 | should.Equal("[1,2,3]", obj.Field2.ToString()) 108 | } 109 | 110 | func Test_object_wrapper_any_get_all(t *testing.T) { 111 | should := require.New(t) 112 | type TestObject struct { 113 | Field1 []int 114 | Field2 []int 115 | } 116 | any := jsoniter.Wrap(TestObject{[]int{1, 2}, []int{3, 4}}) 117 | should.Contains(any.Get('*', 0).ToString(), `"Field2":3`) 118 | should.Contains(any.Keys(), "Field1") 119 | should.Contains(any.Keys(), "Field2") 120 | should.NotContains(any.Keys(), "Field3") 121 | } 122 | -------------------------------------------------------------------------------- /any_tests/jsoniter_any_string_test.go: -------------------------------------------------------------------------------- 1 | package any_tests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/json-iterator/go" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | var stringConvertMap = map[string]string{ 11 | "null": "", 12 | "321.1": "321.1", 13 | `"1.1"`: "1.1", 14 | `"-123.1"`: "-123.1", 15 | "0.0": "0.0", 16 | "0": "0", 17 | `"0"`: "0", 18 | `"0.0"`: "0.0", 19 | `"00.0"`: "00.0", 20 | "true": "true", 21 | "false": "false", 22 | `"true"`: "true", 23 | `"false"`: "false", 24 | `"true123"`: "true123", 25 | `"+1"`: "+1", 26 | "[]": "[]", 27 | "[1,2]": "[1,2]", 28 | "{}": "{}", 29 | `{"a":1, "stream":true}`: `{"a":1, "stream":true}`, 30 | } 31 | 32 | func Test_read_any_to_string(t *testing.T) { 33 | should := require.New(t) 34 | for k, v := range stringConvertMap { 35 | any := jsoniter.Get([]byte(k)) 36 | should.Equal(v, any.ToString(), "original val "+k) 37 | } 38 | } 39 | 40 | func Test_read_string_as_any(t *testing.T) { 41 | should := require.New(t) 42 | any := jsoniter.Get([]byte(`"hello"`)) 43 | should.Equal("hello", any.ToString()) 44 | should.True(any.ToBool()) 45 | any = jsoniter.Get([]byte(`" "`)) 46 | should.False(any.ToBool()) 47 | any = jsoniter.Get([]byte(`"false"`)) 48 | should.True(any.ToBool()) 49 | any = jsoniter.Get([]byte(`"123"`)) 50 | should.Equal(123, any.ToInt()) 51 | } 52 | 53 | func Test_wrap_string(t *testing.T) { 54 | should := require.New(t) 55 | any := jsoniter.Get([]byte("-32000")).MustBeValid() 56 | should.Equal(-32000, any.ToInt()) 57 | should.NoError(any.LastError()) 58 | } 59 | -------------------------------------------------------------------------------- /any_tests/jsoniter_must_be_valid_test.go: -------------------------------------------------------------------------------- 1 | package any_tests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/json-iterator/go" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | // if must be valid is useless, just drop this test 11 | func Test_must_be_valid(t *testing.T) { 12 | should := require.New(t) 13 | any := jsoniter.Get([]byte("123")) 14 | should.Equal(any.MustBeValid().ToInt(), 123) 15 | 16 | any = jsoniter.Wrap(int8(10)) 17 | should.Equal(any.MustBeValid().ToInt(), 10) 18 | 19 | any = jsoniter.Wrap(int16(10)) 20 | should.Equal(any.MustBeValid().ToInt(), 10) 21 | 22 | any = jsoniter.Wrap(int32(10)) 23 | should.Equal(any.MustBeValid().ToInt(), 10) 24 | 25 | any = jsoniter.Wrap(int64(10)) 26 | should.Equal(any.MustBeValid().ToInt(), 10) 27 | 28 | any = jsoniter.Wrap(uint(10)) 29 | should.Equal(any.MustBeValid().ToInt(), 10) 30 | 31 | any = jsoniter.Wrap(uint8(10)) 32 | should.Equal(any.MustBeValid().ToInt(), 10) 33 | 34 | any = jsoniter.Wrap(uint16(10)) 35 | should.Equal(any.MustBeValid().ToInt(), 10) 36 | 37 | any = jsoniter.Wrap(uint32(10)) 38 | should.Equal(any.MustBeValid().ToInt(), 10) 39 | 40 | any = jsoniter.Wrap(uint64(10)) 41 | should.Equal(any.MustBeValid().ToInt(), 10) 42 | 43 | any = jsoniter.Wrap(float32(10)) 44 | should.Equal(any.MustBeValid().ToFloat64(), float64(10)) 45 | 46 | any = jsoniter.Wrap(float64(10)) 47 | should.Equal(any.MustBeValid().ToFloat64(), float64(10)) 48 | 49 | any = jsoniter.Wrap(true) 50 | should.Equal(any.MustBeValid().ToFloat64(), float64(1)) 51 | 52 | any = jsoniter.Wrap(false) 53 | should.Equal(any.MustBeValid().ToFloat64(), float64(0)) 54 | 55 | any = jsoniter.Wrap(nil) 56 | should.Equal(any.MustBeValid().ToFloat64(), float64(0)) 57 | 58 | any = jsoniter.Wrap(struct{ age int }{age: 1}) 59 | should.Equal(any.MustBeValid().ToFloat64(), float64(0)) 60 | 61 | any = jsoniter.Wrap(map[string]interface{}{"abc": 1}) 62 | should.Equal(any.MustBeValid().ToFloat64(), float64(0)) 63 | 64 | any = jsoniter.Wrap("abc") 65 | should.Equal(any.MustBeValid().ToFloat64(), float64(0)) 66 | 67 | any = jsoniter.Wrap([]int{}) 68 | should.Equal(any.MustBeValid().ToFloat64(), float64(0)) 69 | 70 | any = jsoniter.Wrap([]int{1, 2}) 71 | should.Equal(any.MustBeValid().ToFloat64(), float64(1)) 72 | } 73 | -------------------------------------------------------------------------------- /any_tests/jsoniter_wrap_test.go: -------------------------------------------------------------------------------- 1 | package any_tests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/json-iterator/go" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func Test_wrap_and_valuetype_everything(t *testing.T) { 11 | should := require.New(t) 12 | var i interface{} 13 | any := jsoniter.Get([]byte("123")) 14 | // default of number type is float64 15 | i = float64(123) 16 | should.Equal(i, any.GetInterface()) 17 | 18 | any = jsoniter.Wrap(int8(10)) 19 | should.Equal(any.ValueType(), jsoniter.NumberValue) 20 | should.Equal(any.LastError(), nil) 21 | // get interface is not int8 interface 22 | // i = int8(10) 23 | // should.Equal(i, any.GetInterface()) 24 | 25 | any = jsoniter.Wrap(int16(10)) 26 | should.Equal(any.ValueType(), jsoniter.NumberValue) 27 | should.Equal(any.LastError(), nil) 28 | //i = int16(10) 29 | //should.Equal(i, any.GetInterface()) 30 | 31 | any = jsoniter.Wrap(int32(10)) 32 | should.Equal(any.ValueType(), jsoniter.NumberValue) 33 | should.Equal(any.LastError(), nil) 34 | i = int32(10) 35 | should.Equal(i, any.GetInterface()) 36 | any = jsoniter.Wrap(int64(10)) 37 | should.Equal(any.ValueType(), jsoniter.NumberValue) 38 | should.Equal(any.LastError(), nil) 39 | i = int64(10) 40 | should.Equal(i, any.GetInterface()) 41 | 42 | any = jsoniter.Wrap(uint(10)) 43 | should.Equal(any.ValueType(), jsoniter.NumberValue) 44 | should.Equal(any.LastError(), nil) 45 | // not equal 46 | //i = uint(10) 47 | //should.Equal(i, any.GetInterface()) 48 | any = jsoniter.Wrap(uint8(10)) 49 | should.Equal(any.ValueType(), jsoniter.NumberValue) 50 | should.Equal(any.LastError(), nil) 51 | // not equal 52 | // i = uint8(10) 53 | // should.Equal(i, any.GetInterface()) 54 | any = jsoniter.Wrap(uint16(10)) 55 | should.Equal(any.ValueType(), jsoniter.NumberValue) 56 | should.Equal(any.LastError(), nil) 57 | any = jsoniter.Wrap(uint32(10)) 58 | should.Equal(any.ValueType(), jsoniter.NumberValue) 59 | should.Equal(any.LastError(), nil) 60 | i = uint32(10) 61 | should.Equal(i, any.GetInterface()) 62 | any = jsoniter.Wrap(uint64(10)) 63 | should.Equal(any.ValueType(), jsoniter.NumberValue) 64 | should.Equal(any.LastError(), nil) 65 | i = uint64(10) 66 | should.Equal(i, any.GetInterface()) 67 | 68 | any = jsoniter.Wrap(float32(10)) 69 | should.Equal(any.ValueType(), jsoniter.NumberValue) 70 | should.Equal(any.LastError(), nil) 71 | // not equal 72 | //i = float32(10) 73 | //should.Equal(i, any.GetInterface()) 74 | any = jsoniter.Wrap(float64(10)) 75 | should.Equal(any.ValueType(), jsoniter.NumberValue) 76 | should.Equal(any.LastError(), nil) 77 | i = float64(10) 78 | should.Equal(i, any.GetInterface()) 79 | 80 | any = jsoniter.Wrap(true) 81 | should.Equal(any.ValueType(), jsoniter.BoolValue) 82 | should.Equal(any.LastError(), nil) 83 | i = true 84 | should.Equal(i, any.GetInterface()) 85 | any = jsoniter.Wrap(false) 86 | should.Equal(any.ValueType(), jsoniter.BoolValue) 87 | should.Equal(any.LastError(), nil) 88 | i = false 89 | should.Equal(i, any.GetInterface()) 90 | 91 | any = jsoniter.Wrap(nil) 92 | should.Equal(any.ValueType(), jsoniter.NilValue) 93 | should.Equal(any.LastError(), nil) 94 | i = nil 95 | should.Equal(i, any.GetInterface()) 96 | 97 | stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32) 98 | any.WriteTo(stream) 99 | should.Equal("null", string(stream.Buffer())) 100 | should.Equal(any.LastError(), nil) 101 | 102 | any = jsoniter.Wrap(struct{ age int }{age: 1}) 103 | should.Equal(any.ValueType(), jsoniter.ObjectValue) 104 | should.Equal(any.LastError(), nil) 105 | i = struct{ age int }{age: 1} 106 | should.Equal(i, any.GetInterface()) 107 | 108 | any = jsoniter.Wrap(map[string]interface{}{"abc": 1}) 109 | should.Equal(any.ValueType(), jsoniter.ObjectValue) 110 | should.Equal(any.LastError(), nil) 111 | i = map[string]interface{}{"abc": 1} 112 | should.Equal(i, any.GetInterface()) 113 | 114 | any = jsoniter.Wrap("abc") 115 | i = "abc" 116 | should.Equal(i, any.GetInterface()) 117 | should.Equal(nil, any.LastError()) 118 | 119 | } 120 | -------------------------------------------------------------------------------- /any_uint32.go: -------------------------------------------------------------------------------- 1 | package jsoniter 2 | 3 | import ( 4 | "strconv" 5 | ) 6 | 7 | type uint32Any struct { 8 | baseAny 9 | val uint32 10 | } 11 | 12 | func (any *uint32Any) LastError() error { 13 | return nil 14 | } 15 | 16 | func (any *uint32Any) ValueType() ValueType { 17 | return NumberValue 18 | } 19 | 20 | func (any *uint32Any) MustBeValid() Any { 21 | return any 22 | } 23 | 24 | func (any *uint32Any) ToBool() bool { 25 | return any.val != 0 26 | } 27 | 28 | func (any *uint32Any) ToInt() int { 29 | return int(any.val) 30 | } 31 | 32 | func (any *uint32Any) ToInt32() int32 { 33 | return int32(any.val) 34 | } 35 | 36 | func (any *uint32Any) ToInt64() int64 { 37 | return int64(any.val) 38 | } 39 | 40 | func (any *uint32Any) ToUint() uint { 41 | return uint(any.val) 42 | } 43 | 44 | func (any *uint32Any) ToUint32() uint32 { 45 | return any.val 46 | } 47 | 48 | func (any *uint32Any) ToUint64() uint64 { 49 | return uint64(any.val) 50 | } 51 | 52 | func (any *uint32Any) ToFloat32() float32 { 53 | return float32(any.val) 54 | } 55 | 56 | func (any *uint32Any) ToFloat64() float64 { 57 | return float64(any.val) 58 | } 59 | 60 | func (any *uint32Any) ToString() string { 61 | return strconv.FormatInt(int64(any.val), 10) 62 | } 63 | 64 | func (any *uint32Any) WriteTo(stream *Stream) { 65 | stream.WriteUint32(any.val) 66 | } 67 | 68 | func (any *uint32Any) Parse() *Iterator { 69 | return nil 70 | } 71 | 72 | func (any *uint32Any) GetInterface() interface{} { 73 | return any.val 74 | } 75 | -------------------------------------------------------------------------------- /any_uint64.go: -------------------------------------------------------------------------------- 1 | package jsoniter 2 | 3 | import ( 4 | "strconv" 5 | ) 6 | 7 | type uint64Any struct { 8 | baseAny 9 | val uint64 10 | } 11 | 12 | func (any *uint64Any) LastError() error { 13 | return nil 14 | } 15 | 16 | func (any *uint64Any) ValueType() ValueType { 17 | return NumberValue 18 | } 19 | 20 | func (any *uint64Any) MustBeValid() Any { 21 | return any 22 | } 23 | 24 | func (any *uint64Any) ToBool() bool { 25 | return any.val != 0 26 | } 27 | 28 | func (any *uint64Any) ToInt() int { 29 | return int(any.val) 30 | } 31 | 32 | func (any *uint64Any) ToInt32() int32 { 33 | return int32(any.val) 34 | } 35 | 36 | func (any *uint64Any) ToInt64() int64 { 37 | return int64(any.val) 38 | } 39 | 40 | func (any *uint64Any) ToUint() uint { 41 | return uint(any.val) 42 | } 43 | 44 | func (any *uint64Any) ToUint32() uint32 { 45 | return uint32(any.val) 46 | } 47 | 48 | func (any *uint64Any) ToUint64() uint64 { 49 | return any.val 50 | } 51 | 52 | func (any *uint64Any) ToFloat32() float32 { 53 | return float32(any.val) 54 | } 55 | 56 | func (any *uint64Any) ToFloat64() float64 { 57 | return float64(any.val) 58 | } 59 | 60 | func (any *uint64Any) ToString() string { 61 | return strconv.FormatUint(any.val, 10) 62 | } 63 | 64 | func (any *uint64Any) WriteTo(stream *Stream) { 65 | stream.WriteUint64(any.val) 66 | } 67 | 68 | func (any *uint64Any) Parse() *Iterator { 69 | return nil 70 | } 71 | 72 | func (any *uint64Any) GetInterface() interface{} { 73 | return any.val 74 | } 75 | -------------------------------------------------------------------------------- /api_tests/decoder_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "github.com/json-iterator/go" 7 | "github.com/stretchr/testify/require" 8 | "io/ioutil" 9 | "testing" 10 | ) 11 | 12 | func Test_disallowUnknownFields(t *testing.T) { 13 | should := require.New(t) 14 | type TestObject struct{} 15 | var obj TestObject 16 | decoder := jsoniter.NewDecoder(bytes.NewBufferString(`{"field1":100}`)) 17 | decoder.DisallowUnknownFields() 18 | should.Error(decoder.Decode(&obj)) 19 | } 20 | 21 | func Test_new_decoder(t *testing.T) { 22 | should := require.New(t) 23 | decoder1 := json.NewDecoder(bytes.NewBufferString(`[1][2]`)) 24 | decoder2 := jsoniter.NewDecoder(bytes.NewBufferString(`[1][2]`)) 25 | arr1 := []int{} 26 | should.Nil(decoder1.Decode(&arr1)) 27 | should.Equal([]int{1}, arr1) 28 | arr2 := []int{} 29 | should.True(decoder1.More()) 30 | buffered, _ := ioutil.ReadAll(decoder1.Buffered()) 31 | should.Equal("[2]", string(buffered)) 32 | should.Nil(decoder2.Decode(&arr2)) 33 | should.Equal([]int{1}, arr2) 34 | should.True(decoder2.More()) 35 | buffered, _ = ioutil.ReadAll(decoder2.Buffered()) 36 | should.Equal("[2]", string(buffered)) 37 | 38 | should.Nil(decoder1.Decode(&arr1)) 39 | should.Equal([]int{2}, arr1) 40 | should.False(decoder1.More()) 41 | should.Nil(decoder2.Decode(&arr2)) 42 | should.Equal([]int{2}, arr2) 43 | should.False(decoder2.More()) 44 | } 45 | 46 | func Test_use_number(t *testing.T) { 47 | should := require.New(t) 48 | decoder1 := json.NewDecoder(bytes.NewBufferString(`123`)) 49 | decoder1.UseNumber() 50 | decoder2 := jsoniter.NewDecoder(bytes.NewBufferString(`123`)) 51 | decoder2.UseNumber() 52 | var obj1 interface{} 53 | should.Nil(decoder1.Decode(&obj1)) 54 | should.Equal(json.Number("123"), obj1) 55 | var obj2 interface{} 56 | should.Nil(decoder2.Decode(&obj2)) 57 | should.Equal(json.Number("123"), obj2) 58 | } 59 | 60 | func Test_decoder_more(t *testing.T) { 61 | should := require.New(t) 62 | decoder := jsoniter.NewDecoder(bytes.NewBufferString("abcde")) 63 | should.True(decoder.More()) 64 | } 65 | -------------------------------------------------------------------------------- /api_tests/encoder_18_test.go: -------------------------------------------------------------------------------- 1 | //+build go1.8 2 | 3 | package test 4 | 5 | import ( 6 | "bytes" 7 | "encoding/json" 8 | "testing" 9 | "unicode/utf8" 10 | 11 | "github.com/json-iterator/go" 12 | "github.com/stretchr/testify/require" 13 | ) 14 | 15 | func Test_new_encoder(t *testing.T) { 16 | should := require.New(t) 17 | buf1 := &bytes.Buffer{} 18 | encoder1 := json.NewEncoder(buf1) 19 | encoder1.SetEscapeHTML(false) 20 | encoder1.Encode([]int{1}) 21 | should.Equal("[1]\n", buf1.String()) 22 | buf2 := &bytes.Buffer{} 23 | encoder2 := jsoniter.NewEncoder(buf2) 24 | encoder2.SetEscapeHTML(false) 25 | encoder2.Encode([]int{1}) 26 | should.Equal("[1]\n", buf2.String()) 27 | } 28 | 29 | func Test_string_encode_with_std_without_html_escape(t *testing.T) { 30 | api := jsoniter.Config{EscapeHTML: false}.Froze() 31 | should := require.New(t) 32 | for i := 0; i < utf8.RuneSelf; i++ { 33 | input := string([]byte{byte(i)}) 34 | buf := &bytes.Buffer{} 35 | encoder := json.NewEncoder(buf) 36 | encoder.SetEscapeHTML(false) 37 | err := encoder.Encode(input) 38 | should.Nil(err) 39 | stdOutput := buf.String() 40 | stdOutput = stdOutput[:len(stdOutput)-1] 41 | jsoniterOutputBytes, err := api.Marshal(input) 42 | should.Nil(err) 43 | jsoniterOutput := string(jsoniterOutputBytes) 44 | should.Equal(stdOutput, jsoniterOutput) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /api_tests/encoder_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "github.com/json-iterator/go" 7 | "github.com/stretchr/testify/require" 8 | "testing" 9 | ) 10 | 11 | // Standard Encoder has trailing newline. 12 | func TestEncoderHasTrailingNewline(t *testing.T) { 13 | should := require.New(t) 14 | var buf, stdbuf bytes.Buffer 15 | enc := jsoniter.ConfigCompatibleWithStandardLibrary.NewEncoder(&buf) 16 | enc.Encode(1) 17 | stdenc := json.NewEncoder(&stdbuf) 18 | stdenc.Encode(1) 19 | should.Equal(stdbuf.Bytes(), buf.Bytes()) 20 | } 21 | -------------------------------------------------------------------------------- /api_tests/marshal_indent_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/json-iterator/go" 6 | "github.com/stretchr/testify/require" 7 | "testing" 8 | ) 9 | 10 | func Test_marshal_indent(t *testing.T) { 11 | should := require.New(t) 12 | obj := struct { 13 | F1 int 14 | F2 []int 15 | }{1, []int{2, 3, 4}} 16 | output, err := json.MarshalIndent(obj, "", " ") 17 | should.Nil(err) 18 | should.Equal("{\n \"F1\": 1,\n \"F2\": [\n 2,\n 3,\n 4\n ]\n}", string(output)) 19 | output, err = jsoniter.MarshalIndent(obj, "", " ") 20 | should.Nil(err) 21 | should.Equal("{\n \"F1\": 1,\n \"F2\": [\n 2,\n 3,\n 4\n ]\n}", string(output)) 22 | } 23 | 24 | func Test_marshal_indent_map(t *testing.T) { 25 | should := require.New(t) 26 | obj := map[int]int{1: 2} 27 | output, err := json.MarshalIndent(obj, "", " ") 28 | should.Nil(err) 29 | should.Equal("{\n \"1\": 2\n}", string(output)) 30 | output, err = jsoniter.MarshalIndent(obj, "", " ") 31 | should.Nil(err) 32 | should.Equal("{\n \"1\": 2\n}", string(output)) 33 | output, err = jsoniter.ConfigCompatibleWithStandardLibrary.MarshalIndent(obj, "", " ") 34 | should.Nil(err) 35 | should.Equal("{\n \"1\": 2\n}", string(output)) 36 | } 37 | -------------------------------------------------------------------------------- /api_tests/marshal_json_escape_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "testing" 7 | 8 | jsoniter "github.com/json-iterator/go" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | var marshalConfig = jsoniter.Config{ 13 | EscapeHTML: false, 14 | SortMapKeys: true, 15 | ValidateJsonRawMessage: true, 16 | }.Froze() 17 | 18 | type Container struct { 19 | Bar interface{} 20 | } 21 | 22 | func (c *Container) MarshalJSON() ([]byte, error) { 23 | return marshalConfig.Marshal(&c.Bar) 24 | } 25 | 26 | func TestEncodeEscape(t *testing.T) { 27 | should := require.New(t) 28 | 29 | container := &Container{ 30 | Bar: []string{"123", "ooo"}, 31 | } 32 | out, err := marshalConfig.Marshal(container) 33 | should.Nil(err) 34 | bufout := string(out) 35 | 36 | var stdbuf bytes.Buffer 37 | stdenc := json.NewEncoder(&stdbuf) 38 | stdenc.SetEscapeHTML(false) 39 | err = stdenc.Encode(container) 40 | should.Nil(err) 41 | stdout := string(stdbuf.Bytes()) 42 | if stdout[len(stdout)-1:] == "\n" { 43 | stdout = stdout[:len(stdout)-1] 44 | } 45 | 46 | should.Equal(stdout, bufout) 47 | } 48 | -------------------------------------------------------------------------------- /api_tests/marshal_json_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "github.com/json-iterator/go" 7 | "testing" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | 12 | type Foo struct { 13 | Bar interface{} 14 | } 15 | 16 | func (f Foo) MarshalJSON() ([]byte, error) { 17 | var buf bytes.Buffer 18 | err := json.NewEncoder(&buf).Encode(f.Bar) 19 | return buf.Bytes(), err 20 | } 21 | 22 | 23 | // Standard Encoder has trailing newline. 24 | func TestEncodeMarshalJSON(t *testing.T) { 25 | 26 | foo := Foo { 27 | Bar: 123, 28 | } 29 | should := require.New(t) 30 | var buf, stdbuf bytes.Buffer 31 | enc := jsoniter.ConfigCompatibleWithStandardLibrary.NewEncoder(&buf) 32 | enc.Encode(foo) 33 | stdenc := json.NewEncoder(&stdbuf) 34 | stdenc.Encode(foo) 35 | should.Equal(stdbuf.Bytes(), buf.Bytes()) 36 | } 37 | -------------------------------------------------------------------------------- /benchmarks/encode_string_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "bytes" 5 | "github.com/json-iterator/go" 6 | "testing" 7 | ) 8 | 9 | func Benchmark_encode_string_with_SetEscapeHTML(b *testing.B) { 10 | type V struct { 11 | S string 12 | B bool 13 | I int 14 | } 15 | var json = jsoniter.ConfigCompatibleWithStandardLibrary 16 | b.ReportAllocs() 17 | for i := 0; i < b.N; i++ { 18 | buf := &bytes.Buffer{} 19 | enc := json.NewEncoder(buf) 20 | enc.SetEscapeHTML(true) 21 | if err := enc.Encode(V{S: "s", B: true, I: 233}); err != nil { 22 | b.Fatal(err) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /benchmarks/jsoniter_large_file_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/json-iterator/go" 6 | "io/ioutil" 7 | "os" 8 | "testing" 9 | ) 10 | 11 | //func Test_large_file(t *testing.T) { 12 | // file, err := os.Open("/tmp/large-file.json") 13 | // if err != nil { 14 | // t.Fatal(err) 15 | // } 16 | // iter := Parse(file, 4096) 17 | // count := 0 18 | // for iter.ReadArray() { 19 | // iter.Skip() 20 | // count++ 21 | // } 22 | // if count != 11351 { 23 | // t.Fatal(count) 24 | // } 25 | //} 26 | 27 | func init() { 28 | ioutil.WriteFile("/tmp/large-file.json", []byte(`[{ 29 | "person": { 30 | "id": "d50887ca-a6ce-4e59-b89f-14f0b5d03b03", 31 | "name": { 32 | "fullName": "Leonid Bugaev", 33 | "givenName": "Leonid", 34 | "familyName": "Bugaev" 35 | }, 36 | "email": "leonsbox@gmail.com", 37 | "gender": "male", 38 | "location": "Saint Petersburg, Saint Petersburg, RU", 39 | "geo": { 40 | "city": "Saint Petersburg", 41 | "state": "Saint Petersburg", 42 | "country": "Russia", 43 | "lat": 59.9342802, 44 | "lng": 30.3350986 45 | }, 46 | "bio": "Senior engineer at Granify.com", 47 | "site": "http://flickfaver.com", 48 | "avatar": "https://d1ts43dypk8bqh.cloudfront.net/v1/avatars/d50887ca-a6ce-4e59-b89f-14f0b5d03b03", 49 | "employment": { 50 | "name": "www.latera.ru", 51 | "title": "Software Engineer", 52 | "domain": "gmail.com" 53 | }, 54 | "facebook": { 55 | "handle": "leonid.bugaev" 56 | }, 57 | "github": { 58 | "handle": "buger", 59 | "id": 14009, 60 | "avatar": "https://avatars.githubusercontent.com/u/14009?v=3", 61 | "company": "Granify", 62 | "blog": "http://leonsbox.com", 63 | "followers": 95, 64 | "following": 10 65 | }, 66 | "twitter": { 67 | "handle": "flickfaver", 68 | "id": 77004410, 69 | "bio": null, 70 | "followers": 2, 71 | "following": 1, 72 | "statuses": 5, 73 | "favorites": 0, 74 | "location": "", 75 | "site": "http://flickfaver.com", 76 | "avatar": null 77 | }, 78 | "linkedin": { 79 | "handle": "in/leonidbugaev" 80 | }, 81 | "googleplus": { 82 | "handle": null 83 | }, 84 | "angellist": { 85 | "handle": "leonid-bugaev", 86 | "id": 61541, 87 | "bio": "Senior engineer at Granify.com", 88 | "blog": "http://buger.github.com", 89 | "site": "http://buger.github.com", 90 | "followers": 41, 91 | "avatar": "https://d1qb2nb5cznatu.cloudfront.net/users/61541-medium_jpg?1405474390" 92 | }, 93 | "klout": { 94 | "handle": null, 95 | "score": null 96 | }, 97 | "foursquare": { 98 | "handle": null 99 | }, 100 | "aboutme": { 101 | "handle": "leonid.bugaev", 102 | "bio": null, 103 | "avatar": null 104 | }, 105 | "gravatar": { 106 | "handle": "buger", 107 | "urls": [ 108 | ], 109 | "avatar": "http://1.gravatar.com/avatar/f7c8edd577d13b8930d5522f28123510", 110 | "avatars": [ 111 | { 112 | "url": "http://1.gravatar.com/avatar/f7c8edd577d13b8930d5522f28123510", 113 | "type": "thumbnail" 114 | } 115 | ] 116 | }, 117 | "fuzzy": false 118 | }, 119 | "company": "hello" 120 | }]`), 0666) 121 | } 122 | 123 | /* 124 | 200000 8886 ns/op 4336 B/op 6 allocs/op 125 | 50000 34244 ns/op 6744 B/op 14 allocs/op 126 | */ 127 | func Benchmark_jsoniter_large_file(b *testing.B) { 128 | b.ReportAllocs() 129 | for n := 0; n < b.N; n++ { 130 | file, _ := os.Open("/tmp/large-file.json") 131 | iter := jsoniter.Parse(jsoniter.ConfigDefault, file, 4096) 132 | count := 0 133 | iter.ReadArrayCB(func(iter *jsoniter.Iterator) bool { 134 | // Skip() is strict by default, use --tags jsoniter-sloppy to skip without validation 135 | iter.Skip() 136 | count++ 137 | return true 138 | }) 139 | file.Close() 140 | if iter.Error != nil { 141 | b.Error(iter.Error) 142 | } 143 | } 144 | } 145 | 146 | func Benchmark_json_large_file(b *testing.B) { 147 | b.ReportAllocs() 148 | for n := 0; n < b.N; n++ { 149 | file, _ := os.Open("/tmp/large-file.json") 150 | bytes, _ := ioutil.ReadAll(file) 151 | file.Close() 152 | result := []struct{}{} 153 | err := json.Unmarshal(bytes, &result) 154 | if err != nil { 155 | b.Error(err) 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | set -x 4 | 5 | if [ ! -d /tmp/build-golang/src/github.com/json-iterator ]; then 6 | mkdir -p /tmp/build-golang/src/github.com/json-iterator 7 | ln -s $PWD /tmp/build-golang/src/github.com/json-iterator/go 8 | fi 9 | export GOPATH=/tmp/build-golang 10 | go get -u github.com/golang/dep/cmd/dep 11 | cd /tmp/build-golang/src/github.com/json-iterator/go 12 | exec $GOPATH/bin/dep ensure -update 13 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | package jsoniter 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | ) 8 | 9 | func ExampleMarshal() { 10 | type ColorGroup struct { 11 | ID int 12 | Name string 13 | Colors []string 14 | } 15 | group := ColorGroup{ 16 | ID: 1, 17 | Name: "Reds", 18 | Colors: []string{"Crimson", "Red", "Ruby", "Maroon"}, 19 | } 20 | b, err := Marshal(group) 21 | if err != nil { 22 | fmt.Println("error:", err) 23 | } 24 | os.Stdout.Write(b) 25 | // Output: 26 | // {"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]} 27 | } 28 | 29 | func ExampleUnmarshal() { 30 | var jsonBlob = []byte(`[ 31 | {"Name": "Platypus", "Order": "Monotremata"}, 32 | {"Name": "Quoll", "Order": "Dasyuromorphia"} 33 | ]`) 34 | type Animal struct { 35 | Name string 36 | Order string 37 | } 38 | var animals []Animal 39 | err := Unmarshal(jsonBlob, &animals) 40 | if err != nil { 41 | fmt.Println("error:", err) 42 | } 43 | fmt.Printf("%+v", animals) 44 | // Output: 45 | // [{Name:Platypus Order:Monotremata} {Name:Quoll Order:Dasyuromorphia}] 46 | } 47 | 48 | func ExampleConfigFastest_Marshal() { 49 | type ColorGroup struct { 50 | ID int 51 | Name string 52 | Colors []string 53 | } 54 | group := ColorGroup{ 55 | ID: 1, 56 | Name: "Reds", 57 | Colors: []string{"Crimson", "Red", "Ruby", "Maroon"}, 58 | } 59 | stream := ConfigFastest.BorrowStream(nil) 60 | defer ConfigFastest.ReturnStream(stream) 61 | stream.WriteVal(group) 62 | if stream.Error != nil { 63 | fmt.Println("error:", stream.Error) 64 | } 65 | os.Stdout.Write(stream.Buffer()) 66 | // Output: 67 | // {"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]} 68 | } 69 | 70 | func ExampleConfigFastest_Unmarshal() { 71 | var jsonBlob = []byte(`[ 72 | {"Name": "Platypus", "Order": "Monotremata"}, 73 | {"Name": "Quoll", "Order": "Dasyuromorphia"} 74 | ]`) 75 | type Animal struct { 76 | Name string 77 | Order string 78 | } 79 | var animals []Animal 80 | iter := ConfigFastest.BorrowIterator(jsonBlob) 81 | defer ConfigFastest.ReturnIterator(iter) 82 | iter.ReadVal(&animals) 83 | if iter.Error != nil { 84 | fmt.Println("error:", iter.Error) 85 | } 86 | fmt.Printf("%+v", animals) 87 | // Output: 88 | // [{Name:Platypus Order:Monotremata} {Name:Quoll Order:Dasyuromorphia}] 89 | } 90 | 91 | func ExampleGet() { 92 | val := []byte(`{"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]}`) 93 | fmt.Printf(Get(val, "Colors", 0).ToString()) 94 | // Output: 95 | // Crimson 96 | } 97 | 98 | func ExampleMyKey() { 99 | hello := MyKey("hello") 100 | output, _ := Marshal(map[*MyKey]string{&hello: "world"}) 101 | fmt.Println(string(output)) 102 | obj := map[*MyKey]string{} 103 | Unmarshal(output, &obj) 104 | for k, v := range obj { 105 | fmt.Println(*k, v) 106 | } 107 | // Output: 108 | // {"Hello":"world"} 109 | // Hel world 110 | } 111 | 112 | type MyKey string 113 | 114 | func (m *MyKey) MarshalText() ([]byte, error) { 115 | return []byte(strings.Replace(string(*m), "h", "H", -1)), nil 116 | } 117 | 118 | func (m *MyKey) UnmarshalText(text []byte) error { 119 | *m = MyKey(text[:3]) 120 | return nil 121 | } 122 | -------------------------------------------------------------------------------- /extension_tests/extension_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "github.com/json-iterator/go" 5 | "github.com/modern-go/reflect2" 6 | "github.com/stretchr/testify/require" 7 | "reflect" 8 | "strconv" 9 | "testing" 10 | "unsafe" 11 | ) 12 | 13 | type TestObject1 struct { 14 | Field1 string 15 | } 16 | 17 | type testExtension struct { 18 | jsoniter.DummyExtension 19 | } 20 | 21 | func (extension *testExtension) UpdateStructDescriptor(structDescriptor *jsoniter.StructDescriptor) { 22 | if structDescriptor.Type.String() != "test.TestObject1" { 23 | return 24 | } 25 | binding := structDescriptor.GetField("Field1") 26 | binding.Encoder = &funcEncoder{fun: func(ptr unsafe.Pointer, stream *jsoniter.Stream) { 27 | str := *((*string)(ptr)) 28 | val, _ := strconv.Atoi(str) 29 | stream.WriteInt(val) 30 | }} 31 | binding.Decoder = &funcDecoder{func(ptr unsafe.Pointer, iter *jsoniter.Iterator) { 32 | *((*string)(ptr)) = strconv.Itoa(iter.ReadInt()) 33 | }} 34 | binding.ToNames = []string{"field-1"} 35 | binding.FromNames = []string{"field-1"} 36 | } 37 | 38 | func Test_customize_field_by_extension(t *testing.T) { 39 | should := require.New(t) 40 | cfg := jsoniter.Config{}.Froze() 41 | cfg.RegisterExtension(&testExtension{}) 42 | obj := TestObject1{} 43 | err := cfg.UnmarshalFromString(`{"field-1": 100}`, &obj) 44 | should.Nil(err) 45 | should.Equal("100", obj.Field1) 46 | str, err := cfg.MarshalToString(obj) 47 | should.Nil(err) 48 | should.Equal(`{"field-1":100}`, str) 49 | } 50 | 51 | func Test_customize_map_key_encoder(t *testing.T) { 52 | should := require.New(t) 53 | cfg := jsoniter.Config{}.Froze() 54 | cfg.RegisterExtension(&testMapKeyExtension{}) 55 | m := map[int]int{1: 2} 56 | output, err := cfg.MarshalToString(m) 57 | should.NoError(err) 58 | should.Equal(`{"2":2}`, output) 59 | m = map[int]int{} 60 | should.NoError(cfg.UnmarshalFromString(output, &m)) 61 | should.Equal(map[int]int{1: 2}, m) 62 | } 63 | 64 | type testMapKeyExtension struct { 65 | jsoniter.DummyExtension 66 | } 67 | 68 | func (extension *testMapKeyExtension) CreateMapKeyEncoder(typ reflect2.Type) jsoniter.ValEncoder { 69 | if typ.Kind() == reflect.Int { 70 | return &funcEncoder{ 71 | fun: func(ptr unsafe.Pointer, stream *jsoniter.Stream) { 72 | stream.WriteRaw(`"`) 73 | stream.WriteInt(*(*int)(ptr) + 1) 74 | stream.WriteRaw(`"`) 75 | }, 76 | } 77 | } 78 | return nil 79 | } 80 | 81 | func (extension *testMapKeyExtension) CreateMapKeyDecoder(typ reflect2.Type) jsoniter.ValDecoder { 82 | if typ.Kind() == reflect.Int { 83 | return &funcDecoder{ 84 | fun: func(ptr unsafe.Pointer, iter *jsoniter.Iterator) { 85 | i, err := strconv.Atoi(iter.ReadString()) 86 | if err != nil { 87 | iter.ReportError("read map key", err.Error()) 88 | return 89 | } 90 | i-- 91 | *(*int)(ptr) = i 92 | }, 93 | } 94 | } 95 | return nil 96 | } 97 | 98 | type funcDecoder struct { 99 | fun jsoniter.DecoderFunc 100 | } 101 | 102 | func (decoder *funcDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { 103 | decoder.fun(ptr, iter) 104 | } 105 | 106 | type funcEncoder struct { 107 | fun jsoniter.EncoderFunc 108 | isEmptyFunc func(ptr unsafe.Pointer) bool 109 | } 110 | 111 | func (encoder *funcEncoder) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) { 112 | encoder.fun(ptr, stream) 113 | } 114 | 115 | func (encoder *funcEncoder) IsEmpty(ptr unsafe.Pointer) bool { 116 | if encoder.isEmptyFunc == nil { 117 | return false 118 | } 119 | return encoder.isEmptyFunc(ptr) 120 | } 121 | -------------------------------------------------------------------------------- /extra/binary_as_string_codec_test.go: -------------------------------------------------------------------------------- 1 | package extra 2 | 3 | import ( 4 | "github.com/json-iterator/go" 5 | "github.com/stretchr/testify/require" 6 | "testing" 7 | ) 8 | 9 | func init() { 10 | jsoniter.RegisterExtension(&BinaryAsStringExtension{}) 11 | } 12 | 13 | func TestBinaryAsStringCodec(t *testing.T) { 14 | t.Run("safe set", func(t *testing.T) { 15 | should := require.New(t) 16 | output, err := jsoniter.Marshal([]byte("hello")) 17 | should.NoError(err) 18 | should.Equal(`"hello"`, string(output)) 19 | var val []byte 20 | should.NoError(jsoniter.Unmarshal(output, &val)) 21 | should.Equal(`hello`, string(val)) 22 | }) 23 | t.Run("non safe set", func(t *testing.T) { 24 | should := require.New(t) 25 | output, err := jsoniter.Marshal([]byte{1, 2, 3, 23}) 26 | should.NoError(err) 27 | should.Equal(`"\\x01\\x02\\x03\\x17"`, string(output)) 28 | var val []byte 29 | should.NoError(jsoniter.Unmarshal(output, &val)) 30 | should.Equal([]byte{1, 2, 3, 23}, val) 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /extra/naming_strategy.go: -------------------------------------------------------------------------------- 1 | package extra 2 | 3 | import ( 4 | "github.com/json-iterator/go" 5 | "strings" 6 | "unicode" 7 | ) 8 | 9 | // SetNamingStrategy rename struct fields uniformly 10 | func SetNamingStrategy(translate func(string) string) { 11 | jsoniter.RegisterExtension(&namingStrategyExtension{jsoniter.DummyExtension{}, translate}) 12 | } 13 | 14 | type namingStrategyExtension struct { 15 | jsoniter.DummyExtension 16 | translate func(string) string 17 | } 18 | 19 | func (extension *namingStrategyExtension) UpdateStructDescriptor(structDescriptor *jsoniter.StructDescriptor) { 20 | for _, binding := range structDescriptor.Fields { 21 | if unicode.IsLower(rune(binding.Field.Name()[0])) || binding.Field.Name()[0] == '_'{ 22 | continue 23 | } 24 | tag, hastag := binding.Field.Tag().Lookup("json") 25 | if hastag { 26 | tagParts := strings.Split(tag, ",") 27 | if tagParts[0] == "-" { 28 | continue // hidden field 29 | } 30 | if tagParts[0] != "" { 31 | continue // field explicitly named 32 | } 33 | } 34 | binding.ToNames = []string{extension.translate(binding.Field.Name())} 35 | binding.FromNames = []string{extension.translate(binding.Field.Name())} 36 | } 37 | } 38 | 39 | // LowerCaseWithUnderscores one strategy to SetNamingStrategy for. It will change HelloWorld to hello_world. 40 | func LowerCaseWithUnderscores(name string) string { 41 | newName := []rune{} 42 | for i, c := range name { 43 | if i == 0 { 44 | newName = append(newName, unicode.ToLower(c)) 45 | } else { 46 | if unicode.IsUpper(c) { 47 | newName = append(newName, '_') 48 | newName = append(newName, unicode.ToLower(c)) 49 | } else { 50 | newName = append(newName, c) 51 | } 52 | } 53 | } 54 | return string(newName) 55 | } 56 | -------------------------------------------------------------------------------- /extra/naming_strategy_test.go: -------------------------------------------------------------------------------- 1 | package extra 2 | 3 | import ( 4 | "github.com/json-iterator/go" 5 | "github.com/stretchr/testify/require" 6 | "testing" 7 | ) 8 | 9 | func Test_lower_case_with_underscores(t *testing.T) { 10 | should := require.New(t) 11 | should.Equal("hello_world", LowerCaseWithUnderscores("helloWorld")) 12 | should.Equal("hello_world", LowerCaseWithUnderscores("HelloWorld")) 13 | SetNamingStrategy(LowerCaseWithUnderscores) 14 | output, err := jsoniter.Marshal(struct { 15 | UserName string 16 | FirstLanguage string 17 | }{ 18 | UserName: "taowen", 19 | FirstLanguage: "Chinese", 20 | }) 21 | should.Nil(err) 22 | should.Equal(`{"user_name":"taowen","first_language":"Chinese"}`, string(output)) 23 | } 24 | 25 | func Test_set_naming_strategy_with_overrides(t *testing.T) { 26 | should := require.New(t) 27 | SetNamingStrategy(LowerCaseWithUnderscores) 28 | output, err := jsoniter.Marshal(struct { 29 | UserName string `json:"UserName"` 30 | FirstLanguage string 31 | }{ 32 | UserName: "taowen", 33 | FirstLanguage: "Chinese", 34 | }) 35 | should.Nil(err) 36 | should.Equal(`{"UserName":"taowen","first_language":"Chinese"}`, string(output)) 37 | } 38 | 39 | func Test_set_naming_strategy_with_omitempty(t *testing.T) { 40 | should := require.New(t) 41 | SetNamingStrategy(LowerCaseWithUnderscores) 42 | output, err := jsoniter.Marshal(struct { 43 | UserName string 44 | FirstLanguage string `json:",omitempty"` 45 | }{ 46 | UserName: "taowen", 47 | }) 48 | should.Nil(err) 49 | should.Equal(`{"user_name":"taowen"}`, string(output)) 50 | } 51 | 52 | func Test_set_naming_strategy_with_private_field(t *testing.T) { 53 | should := require.New(t) 54 | SetNamingStrategy(LowerCaseWithUnderscores) 55 | output, err := jsoniter.Marshal(struct { 56 | UserName string 57 | userId int 58 | _UserAge int 59 | }{ 60 | UserName: "allen", 61 | userId: 100, 62 | _UserAge: 30, 63 | }) 64 | should.Nil(err) 65 | should.Equal(`{"user_name":"allen"}`, string(output)) 66 | } 67 | -------------------------------------------------------------------------------- /extra/privat_fields.go: -------------------------------------------------------------------------------- 1 | package extra 2 | 3 | import ( 4 | "github.com/json-iterator/go" 5 | "strings" 6 | "unicode" 7 | ) 8 | 9 | // SupportPrivateFields include private fields when encoding/decoding 10 | func SupportPrivateFields() { 11 | jsoniter.RegisterExtension(&privateFieldsExtension{}) 12 | } 13 | 14 | type privateFieldsExtension struct { 15 | jsoniter.DummyExtension 16 | } 17 | 18 | func (extension *privateFieldsExtension) UpdateStructDescriptor(structDescriptor *jsoniter.StructDescriptor) { 19 | for _, binding := range structDescriptor.Fields { 20 | isPrivate := unicode.IsLower(rune(binding.Field.Name()[0])) 21 | if isPrivate { 22 | tag, hastag := binding.Field.Tag().Lookup("json") 23 | if !hastag { 24 | binding.FromNames = []string{binding.Field.Name()} 25 | binding.ToNames = []string{binding.Field.Name()} 26 | continue 27 | } 28 | tagParts := strings.Split(tag, ",") 29 | names := calcFieldNames(binding.Field.Name(), tagParts[0], tag) 30 | binding.FromNames = names 31 | binding.ToNames = names 32 | } 33 | } 34 | } 35 | 36 | func calcFieldNames(originalFieldName string, tagProvidedFieldName string, wholeTag string) []string { 37 | // ignore? 38 | if wholeTag == "-" { 39 | return []string{} 40 | } 41 | // rename? 42 | var fieldNames []string 43 | if tagProvidedFieldName == "" { 44 | fieldNames = []string{originalFieldName} 45 | } else { 46 | fieldNames = []string{tagProvidedFieldName} 47 | } 48 | // private? 49 | isNotExported := unicode.IsLower(rune(originalFieldName[0])) 50 | if isNotExported { 51 | fieldNames = []string{} 52 | } 53 | return fieldNames 54 | } 55 | -------------------------------------------------------------------------------- /extra/private_fields_test.go: -------------------------------------------------------------------------------- 1 | package extra 2 | 3 | import ( 4 | "github.com/json-iterator/go" 5 | "github.com/stretchr/testify/require" 6 | "testing" 7 | ) 8 | 9 | func Test_private_fields(t *testing.T) { 10 | type TestObject struct { 11 | field1 string 12 | } 13 | SupportPrivateFields() 14 | should := require.New(t) 15 | obj := TestObject{} 16 | should.Nil(jsoniter.UnmarshalFromString(`{"field1":"Hello"}`, &obj)) 17 | should.Equal("Hello", obj.field1) 18 | } 19 | -------------------------------------------------------------------------------- /extra/time_as_int64_codec.go: -------------------------------------------------------------------------------- 1 | package extra 2 | 3 | import ( 4 | "github.com/json-iterator/go" 5 | "time" 6 | "unsafe" 7 | ) 8 | 9 | // RegisterTimeAsInt64Codec encode/decode time since number of unit since epoch. the precision is the unit. 10 | func RegisterTimeAsInt64Codec(precision time.Duration) { 11 | jsoniter.RegisterTypeEncoder("time.Time", &timeAsInt64Codec{precision}) 12 | jsoniter.RegisterTypeDecoder("time.Time", &timeAsInt64Codec{precision}) 13 | } 14 | 15 | type timeAsInt64Codec struct { 16 | precision time.Duration 17 | } 18 | 19 | func (codec *timeAsInt64Codec) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { 20 | nanoseconds := iter.ReadInt64() * codec.precision.Nanoseconds() 21 | *((*time.Time)(ptr)) = time.Unix(0, nanoseconds) 22 | } 23 | 24 | func (codec *timeAsInt64Codec) IsEmpty(ptr unsafe.Pointer) bool { 25 | ts := *((*time.Time)(ptr)) 26 | return ts.UnixNano() == 0 27 | } 28 | func (codec *timeAsInt64Codec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) { 29 | ts := *((*time.Time)(ptr)) 30 | stream.WriteInt64(ts.UnixNano() / codec.precision.Nanoseconds()) 31 | } 32 | -------------------------------------------------------------------------------- /extra/time_as_int64_codec_test.go: -------------------------------------------------------------------------------- 1 | package extra 2 | 3 | import ( 4 | "github.com/json-iterator/go" 5 | "github.com/stretchr/testify/require" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func Test_time_as_int64(t *testing.T) { 11 | should := require.New(t) 12 | RegisterTimeAsInt64Codec(time.Nanosecond) 13 | output, err := jsoniter.Marshal(time.Unix(1497952257, 1002)) 14 | should.Nil(err) 15 | should.Equal("1497952257000001002", string(output)) 16 | var val time.Time 17 | should.Nil(jsoniter.Unmarshal(output, &val)) 18 | should.Equal(int64(1497952257000001002), val.UnixNano()) 19 | } 20 | 21 | func Test_time_as_int64_keep_microsecond(t *testing.T) { 22 | t.Skip("conflict") 23 | should := require.New(t) 24 | RegisterTimeAsInt64Codec(time.Microsecond) 25 | output, err := jsoniter.Marshal(time.Unix(1, 1002)) 26 | should.Nil(err) 27 | should.Equal("1000001", string(output)) 28 | var val time.Time 29 | should.Nil(jsoniter.Unmarshal(output, &val)) 30 | should.Equal(int64(1000001000), val.UnixNano()) 31 | } 32 | -------------------------------------------------------------------------------- /fuzzy_mode_convert_table.md: -------------------------------------------------------------------------------- 1 | | json type \ dest type | bool | int | uint | float |string| 2 | | --- | --- | --- | --- |--|--| 3 | | number | positive => true
negative => true
zero => false| 23.2 => 23
-32.1 => -32| 12.1 => 12
-12.1 => 0|as normal|same as origin| 4 | | string | empty string => false
string "0" => false
other strings => true | "123.32" => 123
"-123.4" => -123
"123.23xxxw" => 123
"abcde12" => 0
"-32.1" => -32| 13.2 => 13
-1.1 => 0 |12.1 => 12.1
-12.3 => -12.3
12.4xxa => 12.4
+1.1e2 =>110 |same as origin| 5 | | bool | true => true
false => false| true => 1
false => 0 | true => 1
false => 0 |true => 1
false => 0|true => "true"
false => "false"| 6 | | object | true | 0 | 0 |0|originnal json| 7 | | array | empty array => false
nonempty array => true| [] => 0
[1,2] => 1 | [] => 0
[1,2] => 1 |[] => 0
[1,2] => 1|original json| -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/json-iterator/go 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/davecgh/go-spew v1.1.1 7 | github.com/google/gofuzz v1.0.0 8 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 9 | github.com/modern-go/reflect2 v1.0.2 10 | github.com/stretchr/testify v1.8.0 11 | ) 12 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= 5 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 6 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= 7 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 8 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 9 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 10 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 11 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 12 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 13 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 14 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 15 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 16 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 17 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 18 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 19 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 20 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 21 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 22 | -------------------------------------------------------------------------------- /iter_array.go: -------------------------------------------------------------------------------- 1 | package jsoniter 2 | 3 | // ReadArray read array element, tells if the array has more element to read. 4 | func (iter *Iterator) ReadArray() (ret bool) { 5 | c := iter.nextToken() 6 | switch c { 7 | case 'n': 8 | iter.skipThreeBytes('u', 'l', 'l') 9 | return false // null 10 | case '[': 11 | c = iter.nextToken() 12 | if c != ']' { 13 | iter.unreadByte() 14 | return true 15 | } 16 | return false 17 | case ']': 18 | return false 19 | case ',': 20 | return true 21 | default: 22 | iter.ReportError("ReadArray", "expect [ or , or ] or n, but found "+string([]byte{c})) 23 | return 24 | } 25 | } 26 | 27 | // ReadArrayCB read array with callback 28 | func (iter *Iterator) ReadArrayCB(callback func(*Iterator) bool) (ret bool) { 29 | c := iter.nextToken() 30 | if c == '[' { 31 | if !iter.incrementDepth() { 32 | return false 33 | } 34 | c = iter.nextToken() 35 | if c != ']' { 36 | iter.unreadByte() 37 | if !callback(iter) { 38 | iter.decrementDepth() 39 | return false 40 | } 41 | c = iter.nextToken() 42 | for c == ',' { 43 | if !callback(iter) { 44 | iter.decrementDepth() 45 | return false 46 | } 47 | c = iter.nextToken() 48 | } 49 | if c != ']' { 50 | iter.ReportError("ReadArrayCB", "expect ] in the end, but found "+string([]byte{c})) 51 | iter.decrementDepth() 52 | return false 53 | } 54 | return iter.decrementDepth() 55 | } 56 | return iter.decrementDepth() 57 | } 58 | if c == 'n' { 59 | iter.skipThreeBytes('u', 'l', 'l') 60 | return true // null 61 | } 62 | iter.ReportError("ReadArrayCB", "expect [ or n, but found "+string([]byte{c})) 63 | return false 64 | } 65 | -------------------------------------------------------------------------------- /iter_skip.go: -------------------------------------------------------------------------------- 1 | package jsoniter 2 | 3 | import "fmt" 4 | 5 | // ReadNil reads a json object as nil and 6 | // returns whether it's a nil or not 7 | func (iter *Iterator) ReadNil() (ret bool) { 8 | c := iter.nextToken() 9 | if c == 'n' { 10 | iter.skipThreeBytes('u', 'l', 'l') // null 11 | return true 12 | } 13 | iter.unreadByte() 14 | return false 15 | } 16 | 17 | // ReadBool reads a json object as BoolValue 18 | func (iter *Iterator) ReadBool() (ret bool) { 19 | c := iter.nextToken() 20 | if c == 't' { 21 | iter.skipThreeBytes('r', 'u', 'e') 22 | return true 23 | } 24 | if c == 'f' { 25 | iter.skipFourBytes('a', 'l', 's', 'e') 26 | return false 27 | } 28 | iter.ReportError("ReadBool", "expect t or f, but found "+string([]byte{c})) 29 | return 30 | } 31 | 32 | // SkipAndReturnBytes skip next JSON element, and return its content as []byte. 33 | // The []byte can be kept, it is a copy of data. 34 | func (iter *Iterator) SkipAndReturnBytes() []byte { 35 | iter.startCapture(iter.head) 36 | iter.Skip() 37 | return iter.stopCapture() 38 | } 39 | 40 | // SkipAndAppendBytes skips next JSON element and appends its content to 41 | // buffer, returning the result. 42 | func (iter *Iterator) SkipAndAppendBytes(buf []byte) []byte { 43 | iter.startCaptureTo(buf, iter.head) 44 | iter.Skip() 45 | return iter.stopCapture() 46 | } 47 | 48 | func (iter *Iterator) startCaptureTo(buf []byte, captureStartedAt int) { 49 | if iter.captured != nil { 50 | panic("already in capture mode") 51 | } 52 | iter.captureStartedAt = captureStartedAt 53 | iter.captured = buf 54 | } 55 | 56 | func (iter *Iterator) startCapture(captureStartedAt int) { 57 | iter.startCaptureTo(make([]byte, 0, 32), captureStartedAt) 58 | } 59 | 60 | func (iter *Iterator) stopCapture() []byte { 61 | if iter.captured == nil { 62 | panic("not in capture mode") 63 | } 64 | captured := iter.captured 65 | remaining := iter.buf[iter.captureStartedAt:iter.head] 66 | iter.captureStartedAt = -1 67 | iter.captured = nil 68 | return append(captured, remaining...) 69 | } 70 | 71 | // Skip skips a json object and positions to relatively the next json object 72 | func (iter *Iterator) Skip() { 73 | c := iter.nextToken() 74 | switch c { 75 | case '"': 76 | iter.skipString() 77 | case 'n': 78 | iter.skipThreeBytes('u', 'l', 'l') // null 79 | case 't': 80 | iter.skipThreeBytes('r', 'u', 'e') // true 81 | case 'f': 82 | iter.skipFourBytes('a', 'l', 's', 'e') // false 83 | case '0': 84 | iter.unreadByte() 85 | iter.ReadFloat32() 86 | case '-', '1', '2', '3', '4', '5', '6', '7', '8', '9': 87 | iter.skipNumber() 88 | case '[': 89 | iter.skipArray() 90 | case '{': 91 | iter.skipObject() 92 | default: 93 | iter.ReportError("Skip", fmt.Sprintf("do not know how to skip: %v", c)) 94 | return 95 | } 96 | } 97 | 98 | func (iter *Iterator) skipFourBytes(b1, b2, b3, b4 byte) { 99 | if iter.readByte() != b1 { 100 | iter.ReportError("skipFourBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3, b4}))) 101 | return 102 | } 103 | if iter.readByte() != b2 { 104 | iter.ReportError("skipFourBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3, b4}))) 105 | return 106 | } 107 | if iter.readByte() != b3 { 108 | iter.ReportError("skipFourBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3, b4}))) 109 | return 110 | } 111 | if iter.readByte() != b4 { 112 | iter.ReportError("skipFourBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3, b4}))) 113 | return 114 | } 115 | } 116 | 117 | func (iter *Iterator) skipThreeBytes(b1, b2, b3 byte) { 118 | if iter.readByte() != b1 { 119 | iter.ReportError("skipThreeBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3}))) 120 | return 121 | } 122 | if iter.readByte() != b2 { 123 | iter.ReportError("skipThreeBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3}))) 124 | return 125 | } 126 | if iter.readByte() != b3 { 127 | iter.ReportError("skipThreeBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3}))) 128 | return 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /iter_skip_sloppy.go: -------------------------------------------------------------------------------- 1 | //+build jsoniter_sloppy 2 | 3 | package jsoniter 4 | 5 | // sloppy but faster implementation, do not validate the input json 6 | 7 | func (iter *Iterator) skipNumber() { 8 | for { 9 | for i := iter.head; i < iter.tail; i++ { 10 | c := iter.buf[i] 11 | switch c { 12 | case ' ', '\n', '\r', '\t', ',', '}', ']': 13 | iter.head = i 14 | return 15 | } 16 | } 17 | if !iter.loadMore() { 18 | return 19 | } 20 | } 21 | } 22 | 23 | func (iter *Iterator) skipArray() { 24 | level := 1 25 | if !iter.incrementDepth() { 26 | return 27 | } 28 | for { 29 | for i := iter.head; i < iter.tail; i++ { 30 | switch iter.buf[i] { 31 | case '"': // If inside string, skip it 32 | iter.head = i + 1 33 | iter.skipString() 34 | i = iter.head - 1 // it will be i++ soon 35 | case '[': // If open symbol, increase level 36 | level++ 37 | if !iter.incrementDepth() { 38 | return 39 | } 40 | case ']': // If close symbol, increase level 41 | level-- 42 | if !iter.decrementDepth() { 43 | return 44 | } 45 | 46 | // If we have returned to the original level, we're done 47 | if level == 0 { 48 | iter.head = i + 1 49 | return 50 | } 51 | } 52 | } 53 | if !iter.loadMore() { 54 | iter.ReportError("skipObject", "incomplete array") 55 | return 56 | } 57 | } 58 | } 59 | 60 | func (iter *Iterator) skipObject() { 61 | level := 1 62 | if !iter.incrementDepth() { 63 | return 64 | } 65 | 66 | for { 67 | for i := iter.head; i < iter.tail; i++ { 68 | switch iter.buf[i] { 69 | case '"': // If inside string, skip it 70 | iter.head = i + 1 71 | iter.skipString() 72 | i = iter.head - 1 // it will be i++ soon 73 | case '{': // If open symbol, increase level 74 | level++ 75 | if !iter.incrementDepth() { 76 | return 77 | } 78 | case '}': // If close symbol, increase level 79 | level-- 80 | if !iter.decrementDepth() { 81 | return 82 | } 83 | 84 | // If we have returned to the original level, we're done 85 | if level == 0 { 86 | iter.head = i + 1 87 | return 88 | } 89 | } 90 | } 91 | if !iter.loadMore() { 92 | iter.ReportError("skipObject", "incomplete object") 93 | return 94 | } 95 | } 96 | } 97 | 98 | func (iter *Iterator) skipString() { 99 | for { 100 | end, escaped := iter.findStringEnd() 101 | if end == -1 { 102 | if !iter.loadMore() { 103 | iter.ReportError("skipString", "incomplete string") 104 | return 105 | } 106 | if escaped { 107 | iter.head = 1 // skip the first char as last char read is \ 108 | } 109 | } else { 110 | iter.head = end 111 | return 112 | } 113 | } 114 | } 115 | 116 | // adapted from: https://github.com/buger/jsonparser/blob/master/parser.go 117 | // Tries to find the end of string 118 | // Support if string contains escaped quote symbols. 119 | func (iter *Iterator) findStringEnd() (int, bool) { 120 | escaped := false 121 | for i := iter.head; i < iter.tail; i++ { 122 | c := iter.buf[i] 123 | if c == '"' { 124 | if !escaped { 125 | return i + 1, false 126 | } 127 | j := i - 1 128 | for { 129 | if j < iter.head || iter.buf[j] != '\\' { 130 | // even number of backslashes 131 | // either end of buffer, or " found 132 | return i + 1, true 133 | } 134 | j-- 135 | if j < iter.head || iter.buf[j] != '\\' { 136 | // odd number of backslashes 137 | // it is \" or \\\" 138 | break 139 | } 140 | j-- 141 | } 142 | } else if c == '\\' { 143 | escaped = true 144 | } 145 | } 146 | j := iter.tail - 1 147 | for { 148 | if j < iter.head || iter.buf[j] != '\\' { 149 | // even number of backslashes 150 | // either end of buffer, or " found 151 | return -1, false // do not end with \ 152 | } 153 | j-- 154 | if j < iter.head || iter.buf[j] != '\\' { 155 | // odd number of backslashes 156 | // it is \" or \\\" 157 | break 158 | } 159 | j-- 160 | 161 | } 162 | return -1, true // end with \ 163 | } 164 | -------------------------------------------------------------------------------- /iter_skip_sloppy_test.go: -------------------------------------------------------------------------------- 1 | //+build jsoniter_sloppy 2 | 3 | package jsoniter 4 | 5 | import ( 6 | "github.com/stretchr/testify/require" 7 | "io" 8 | "testing" 9 | ) 10 | 11 | func Test_string_end(t *testing.T) { 12 | end, escaped := ParseString(ConfigDefault, `abc"`).findStringEnd() 13 | if end != 4 { 14 | t.Fatal(end) 15 | } 16 | if escaped != false { 17 | t.Fatal(escaped) 18 | } 19 | end, escaped = ParseString(ConfigDefault, `abc\\"`).findStringEnd() 20 | if end != 6 { 21 | t.Fatal(end) 22 | } 23 | if escaped != true { 24 | t.Fatal(escaped) 25 | } 26 | end, escaped = ParseString(ConfigDefault, `abc\\\\"`).findStringEnd() 27 | if end != 8 { 28 | t.Fatal(end) 29 | } 30 | if escaped != true { 31 | t.Fatal(escaped) 32 | } 33 | end, escaped = ParseString(ConfigDefault, `abc\"`).findStringEnd() 34 | if end != -1 { 35 | t.Fatal(end) 36 | } 37 | if escaped != false { 38 | t.Fatal(escaped) 39 | } 40 | end, escaped = ParseString(ConfigDefault, `abc\`).findStringEnd() 41 | if end != -1 { 42 | t.Fatal(end) 43 | } 44 | if escaped != true { 45 | t.Fatal(escaped) 46 | } 47 | end, escaped = ParseString(ConfigDefault, `abc\\`).findStringEnd() 48 | if end != -1 { 49 | t.Fatal(end) 50 | } 51 | if escaped != false { 52 | t.Fatal(escaped) 53 | } 54 | end, escaped = ParseString(ConfigDefault, `\\`).findStringEnd() 55 | if end != -1 { 56 | t.Fatal(end) 57 | } 58 | if escaped != false { 59 | t.Fatal(escaped) 60 | } 61 | end, escaped = ParseString(ConfigDefault, `\`).findStringEnd() 62 | if end != -1 { 63 | t.Fatal(end) 64 | } 65 | if escaped != true { 66 | t.Fatal(escaped) 67 | } 68 | } 69 | 70 | type StagedReader struct { 71 | r1 string 72 | r2 string 73 | r3 string 74 | r int 75 | } 76 | 77 | func (reader *StagedReader) Read(p []byte) (n int, err error) { 78 | reader.r++ 79 | switch reader.r { 80 | case 1: 81 | copy(p, []byte(reader.r1)) 82 | return len(reader.r1), nil 83 | case 2: 84 | copy(p, []byte(reader.r2)) 85 | return len(reader.r2), nil 86 | case 3: 87 | copy(p, []byte(reader.r3)) 88 | return len(reader.r3), nil 89 | default: 90 | return 0, io.EOF 91 | } 92 | } 93 | 94 | func Test_skip_string(t *testing.T) { 95 | should := require.New(t) 96 | iter := ParseString(ConfigDefault, `"abc`) 97 | iter.skipString() 98 | should.Equal(1, iter.head) 99 | iter = ParseString(ConfigDefault, `\""abc`) 100 | iter.skipString() 101 | should.Equal(3, iter.head) 102 | reader := &StagedReader{ 103 | r1: `abc`, 104 | r2: `"`, 105 | } 106 | iter = Parse(ConfigDefault, reader, 4096) 107 | iter.skipString() 108 | should.Equal(1, iter.head) 109 | reader = &StagedReader{ 110 | r1: `abc`, 111 | r2: `1"`, 112 | } 113 | iter = Parse(ConfigDefault, reader, 4096) 114 | iter.skipString() 115 | should.Equal(2, iter.head) 116 | reader = &StagedReader{ 117 | r1: `abc\`, 118 | r2: `"`, 119 | } 120 | iter = Parse(ConfigDefault, reader, 4096) 121 | iter.skipString() 122 | should.NotNil(iter.Error) 123 | reader = &StagedReader{ 124 | r1: `abc\`, 125 | r2: `""`, 126 | } 127 | iter = Parse(ConfigDefault, reader, 4096) 128 | iter.skipString() 129 | should.Equal(2, iter.head) 130 | } 131 | 132 | func Test_skip_object(t *testing.T) { 133 | iter := ParseString(ConfigDefault, `}`) 134 | iter.skipObject() 135 | if iter.head != 1 { 136 | t.Fatal(iter.head) 137 | } 138 | iter = ParseString(ConfigDefault, `a}`) 139 | iter.skipObject() 140 | if iter.head != 2 { 141 | t.Fatal(iter.head) 142 | } 143 | iter = ParseString(ConfigDefault, `{}}a`) 144 | iter.skipObject() 145 | if iter.head != 3 { 146 | t.Fatal(iter.head) 147 | } 148 | reader := &StagedReader{ 149 | r1: `{`, 150 | r2: `}}a`, 151 | } 152 | iter = Parse(ConfigDefault, reader, 4096) 153 | iter.skipObject() 154 | if iter.head != 2 { 155 | t.Fatal(iter.head) 156 | } 157 | iter = ParseString(ConfigDefault, `"}"}a`) 158 | iter.skipObject() 159 | if iter.head != 4 { 160 | t.Fatal(iter.head) 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /iter_skip_strict.go: -------------------------------------------------------------------------------- 1 | //+build !jsoniter_sloppy 2 | 3 | package jsoniter 4 | 5 | import ( 6 | "fmt" 7 | "io" 8 | ) 9 | 10 | func (iter *Iterator) skipNumber() { 11 | if !iter.trySkipNumber() { 12 | iter.unreadByte() 13 | if iter.Error != nil && iter.Error != io.EOF { 14 | return 15 | } 16 | iter.ReadFloat64() 17 | if iter.Error != nil && iter.Error != io.EOF { 18 | iter.Error = nil 19 | iter.ReadBigFloat() 20 | } 21 | } 22 | } 23 | 24 | func (iter *Iterator) trySkipNumber() bool { 25 | dotFound := false 26 | for i := iter.head; i < iter.tail; i++ { 27 | c := iter.buf[i] 28 | switch c { 29 | case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 30 | case '.': 31 | if dotFound { 32 | iter.ReportError("validateNumber", `more than one dot found in number`) 33 | return true // already failed 34 | } 35 | if i+1 == iter.tail { 36 | return false 37 | } 38 | c = iter.buf[i+1] 39 | switch c { 40 | case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 41 | default: 42 | iter.ReportError("validateNumber", `missing digit after dot`) 43 | return true // already failed 44 | } 45 | dotFound = true 46 | default: 47 | switch c { 48 | case ',', ']', '}', ' ', '\t', '\n', '\r': 49 | if iter.head == i { 50 | return false // if - without following digits 51 | } 52 | iter.head = i 53 | return true // must be valid 54 | } 55 | return false // may be invalid 56 | } 57 | } 58 | return false 59 | } 60 | 61 | func (iter *Iterator) skipString() { 62 | if !iter.trySkipString() { 63 | iter.unreadByte() 64 | iter.ReadString() 65 | } 66 | } 67 | 68 | func (iter *Iterator) trySkipString() bool { 69 | for i := iter.head; i < iter.tail; i++ { 70 | c := iter.buf[i] 71 | if c == '"' { 72 | iter.head = i + 1 73 | return true // valid 74 | } else if c == '\\' { 75 | return false 76 | } else if c < ' ' { 77 | iter.ReportError("trySkipString", 78 | fmt.Sprintf(`invalid control character found: %d`, c)) 79 | return true // already failed 80 | } 81 | } 82 | return false 83 | } 84 | 85 | func (iter *Iterator) skipObject() { 86 | iter.unreadByte() 87 | iter.ReadObjectCB(func(iter *Iterator, field string) bool { 88 | iter.Skip() 89 | return true 90 | }) 91 | } 92 | 93 | func (iter *Iterator) skipArray() { 94 | iter.unreadByte() 95 | iter.ReadArrayCB(func(iter *Iterator) bool { 96 | iter.Skip() 97 | return true 98 | }) 99 | } 100 | -------------------------------------------------------------------------------- /jsoniter.go: -------------------------------------------------------------------------------- 1 | // Package jsoniter implements encoding and decoding of JSON as defined in 2 | // RFC 4627 and provides interfaces with identical syntax of standard lib encoding/json. 3 | // Converting from encoding/json to jsoniter is no more than replacing the package with jsoniter 4 | // and variable type declarations (if any). 5 | // jsoniter interfaces gives 100% compatibility with code using standard lib. 6 | // 7 | // "JSON and Go" 8 | // (https://golang.org/doc/articles/json_and_go.html) 9 | // gives a description of how Marshal/Unmarshal operate 10 | // between arbitrary or predefined json objects and bytes, 11 | // and it applies to jsoniter.Marshal/Unmarshal as well. 12 | // 13 | // Besides, jsoniter.Iterator provides a different set of interfaces 14 | // iterating given bytes/string/reader 15 | // and yielding parsed elements one by one. 16 | // This set of interfaces reads input as required and gives 17 | // better performance. 18 | package jsoniter 19 | -------------------------------------------------------------------------------- /misc_tests/jsoniter_bool_test.go: -------------------------------------------------------------------------------- 1 | package misc_tests 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/json-iterator/go" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func Test_true(t *testing.T) { 12 | should := require.New(t) 13 | iter := jsoniter.ParseString(jsoniter.ConfigDefault, `true`) 14 | should.True(iter.ReadBool()) 15 | iter = jsoniter.ParseString(jsoniter.ConfigDefault, `true`) 16 | should.Equal(true, iter.Read()) 17 | } 18 | 19 | func Test_false(t *testing.T) { 20 | should := require.New(t) 21 | iter := jsoniter.ParseString(jsoniter.ConfigDefault, `false`) 22 | should.False(iter.ReadBool()) 23 | } 24 | 25 | func Test_write_true_false(t *testing.T) { 26 | should := require.New(t) 27 | buf := &bytes.Buffer{} 28 | stream := jsoniter.NewStream(jsoniter.ConfigDefault, buf, 4096) 29 | stream.WriteTrue() 30 | stream.WriteFalse() 31 | stream.WriteBool(false) 32 | stream.Flush() 33 | should.Nil(stream.Error) 34 | should.Equal("truefalsefalse", buf.String()) 35 | } 36 | 37 | func Test_write_val_bool(t *testing.T) { 38 | should := require.New(t) 39 | buf := &bytes.Buffer{} 40 | stream := jsoniter.NewStream(jsoniter.ConfigDefault, buf, 4096) 41 | stream.WriteVal(true) 42 | should.Equal(stream.Buffered(), 4) 43 | stream.Flush() 44 | should.Equal(stream.Buffered(), 0) 45 | should.Nil(stream.Error) 46 | should.Equal("true", buf.String()) 47 | } 48 | -------------------------------------------------------------------------------- /misc_tests/jsoniter_float_test.go: -------------------------------------------------------------------------------- 1 | package misc_tests 2 | 3 | import ( 4 | "encoding/json" 5 | "math" 6 | "testing" 7 | 8 | "github.com/json-iterator/go" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func Test_read_big_float(t *testing.T) { 13 | should := require.New(t) 14 | iter := jsoniter.ParseString(jsoniter.ConfigDefault, `12.3`) 15 | val := iter.ReadBigFloat() 16 | val64, _ := val.Float64() 17 | should.Equal(12.3, val64) 18 | } 19 | 20 | func Test_read_big_int(t *testing.T) { 21 | should := require.New(t) 22 | iter := jsoniter.ParseString(jsoniter.ConfigDefault, `92233720368547758079223372036854775807`) 23 | val := iter.ReadBigInt() 24 | should.NotNil(val) 25 | should.Equal(`92233720368547758079223372036854775807`, val.String()) 26 | } 27 | 28 | func Test_read_float_as_interface(t *testing.T) { 29 | should := require.New(t) 30 | iter := jsoniter.ParseString(jsoniter.ConfigDefault, `12.3`) 31 | should.Equal(float64(12.3), iter.Read()) 32 | } 33 | 34 | func Test_wrap_float(t *testing.T) { 35 | should := require.New(t) 36 | str, err := jsoniter.MarshalToString(jsoniter.WrapFloat64(12.3)) 37 | should.Nil(err) 38 | should.Equal("12.3", str) 39 | } 40 | 41 | func Test_read_float64_cursor(t *testing.T) { 42 | should := require.New(t) 43 | iter := jsoniter.ParseString(jsoniter.ConfigDefault, "[1.23456789\n,2,3]") 44 | should.True(iter.ReadArray()) 45 | should.Equal(1.23456789, iter.Read()) 46 | should.True(iter.ReadArray()) 47 | should.Equal(float64(2), iter.Read()) 48 | } 49 | 50 | func Test_read_float_scientific(t *testing.T) { 51 | should := require.New(t) 52 | var obj interface{} 53 | should.NoError(jsoniter.UnmarshalFromString(`1e1`, &obj)) 54 | should.Equal(float64(10), obj) 55 | should.NoError(json.Unmarshal([]byte(`1e1`), &obj)) 56 | should.Equal(float64(10), obj) 57 | should.NoError(jsoniter.UnmarshalFromString(`1.0e1`, &obj)) 58 | should.Equal(float64(10), obj) 59 | should.NoError(json.Unmarshal([]byte(`1.0e1`), &obj)) 60 | should.Equal(float64(10), obj) 61 | } 62 | 63 | func Test_lossy_float_marshal(t *testing.T) { 64 | should := require.New(t) 65 | api := jsoniter.Config{MarshalFloatWith6Digits: true}.Froze() 66 | output, err := api.MarshalToString(float64(0.1234567)) 67 | should.Nil(err) 68 | should.Equal("0.123457", output) 69 | output, err = api.MarshalToString(float32(0.1234567)) 70 | should.Nil(err) 71 | should.Equal("0.123457", output) 72 | } 73 | 74 | func Test_read_number(t *testing.T) { 75 | should := require.New(t) 76 | iter := jsoniter.ParseString(jsoniter.ConfigDefault, `92233720368547758079223372036854775807`) 77 | val := iter.ReadNumber() 78 | should.Equal(`92233720368547758079223372036854775807`, string(val)) 79 | } 80 | 81 | func Test_encode_inf(t *testing.T) { 82 | should := require.New(t) 83 | _, err := json.Marshal(math.Inf(1)) 84 | should.Error(err) 85 | _, err = jsoniter.Marshal(float32(math.Inf(1))) 86 | should.Error(err) 87 | _, err = jsoniter.Marshal(math.Inf(-1)) 88 | should.Error(err) 89 | } 90 | 91 | func Test_encode_nan(t *testing.T) { 92 | should := require.New(t) 93 | _, err := json.Marshal(math.NaN()) 94 | should.Error(err) 95 | _, err = jsoniter.Marshal(float32(math.NaN())) 96 | should.Error(err) 97 | _, err = jsoniter.Marshal(math.NaN()) 98 | should.Error(err) 99 | } 100 | 101 | func Benchmark_jsoniter_float(b *testing.B) { 102 | b.ReportAllocs() 103 | input := []byte(`1.1123,`) 104 | iter := jsoniter.NewIterator(jsoniter.ConfigDefault) 105 | for n := 0; n < b.N; n++ { 106 | iter.ResetBytes(input) 107 | iter.ReadFloat64() 108 | } 109 | } 110 | 111 | func Benchmark_json_float(b *testing.B) { 112 | for n := 0; n < b.N; n++ { 113 | result := float64(0) 114 | json.Unmarshal([]byte(`1.1`), &result) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /misc_tests/jsoniter_int_test.go: -------------------------------------------------------------------------------- 1 | // +build go1.8 2 | 3 | package misc_tests 4 | 5 | import ( 6 | "bytes" 7 | "encoding/json" 8 | "io" 9 | "io/ioutil" 10 | "math/rand" 11 | "strconv" 12 | "testing" 13 | 14 | "github.com/json-iterator/go" 15 | "github.com/stretchr/testify/require" 16 | ) 17 | 18 | func Test_read_uint64_invalid(t *testing.T) { 19 | should := require.New(t) 20 | iter := jsoniter.ParseString(jsoniter.ConfigDefault, ",") 21 | iter.ReadUint64() 22 | should.NotNil(iter.Error) 23 | } 24 | 25 | func Test_read_int32_array(t *testing.T) { 26 | should := require.New(t) 27 | input := `[123,456,789]` 28 | val := make([]int32, 0) 29 | jsoniter.UnmarshalFromString(input, &val) 30 | should.Equal(3, len(val)) 31 | } 32 | 33 | func Test_read_int64_array(t *testing.T) { 34 | should := require.New(t) 35 | input := `[123,456,789]` 36 | val := make([]int64, 0) 37 | jsoniter.UnmarshalFromString(input, &val) 38 | should.Equal(3, len(val)) 39 | } 40 | 41 | func Test_wrap_int(t *testing.T) { 42 | should := require.New(t) 43 | str, err := jsoniter.MarshalToString(jsoniter.WrapInt64(100)) 44 | should.Nil(err) 45 | should.Equal("100", str) 46 | } 47 | 48 | func Test_write_val_int(t *testing.T) { 49 | should := require.New(t) 50 | buf := &bytes.Buffer{} 51 | stream := jsoniter.NewStream(jsoniter.ConfigDefault, buf, 4096) 52 | stream.WriteVal(1001) 53 | stream.Flush() 54 | should.Nil(stream.Error) 55 | should.Equal("1001", buf.String()) 56 | } 57 | 58 | func Test_write_val_int_ptr(t *testing.T) { 59 | should := require.New(t) 60 | buf := &bytes.Buffer{} 61 | stream := jsoniter.NewStream(jsoniter.ConfigDefault, buf, 4096) 62 | val := 1001 63 | stream.WriteVal(&val) 64 | stream.Flush() 65 | should.Nil(stream.Error) 66 | should.Equal("1001", buf.String()) 67 | } 68 | 69 | func Test_float_as_int(t *testing.T) { 70 | should := require.New(t) 71 | var i int 72 | should.NotNil(jsoniter.Unmarshal([]byte(`1.1`), &i)) 73 | } 74 | 75 | // chunkedData is io.Reader which returns random amount of data in range [1, chunkedData.chunkSize]. 76 | // It simulates chunked data on from HTTP server, which is commonly used by net/http package. 77 | type chunkedData struct { 78 | chunkSize int 79 | data []byte 80 | head int 81 | } 82 | 83 | // Read is implementation of the io.Reader which returns random amount of data in range [1, chunkedData.chunkSize]. 84 | func (c *chunkedData) Read(p []byte) (n int, err error) { 85 | to := c.head + int(rand.Int31n(int32(c.chunkSize))+1) 86 | 87 | // copy does not copy more data then p can consume 88 | n = copy(p, c.data[c.head:to]) 89 | c.head = c.head + n 90 | if c.head >= len(c.data) { 91 | err = io.EOF 92 | } 93 | return n, err 94 | } 95 | 96 | // TestIterator_ReadInt_chunkedInput validates the behaviour of Iterator.ReadInt() method in where: 97 | // - it reads data from io.Reader, 98 | // - expected value is 0 (zero) 99 | // - Iterator.tail == Iterator.head 100 | // - Iterator.tail < len(Iterator.buf) 101 | // - value in buffer after Iterator.tail is presented from previous read and has '.' character. 102 | func TestIterator_ReadInt_chunkedInput(t *testing.T) { 103 | should := require.New(t) 104 | 105 | data := &chunkedData{ 106 | data: jsonFloatIntArray(t, 10), 107 | } 108 | 109 | // because this test is rely on randomness of chunkedData, we are doing multiple iterations to 110 | // be sure, that we can hit a required case. 111 | for data.chunkSize = 3; data.chunkSize <= len(data.data); data.chunkSize++ { 112 | data.head = 0 113 | 114 | iter := jsoniter.Parse(jsoniter.ConfigDefault, data, data.chunkSize) 115 | i := 0 116 | for iter.ReadArray() { 117 | // every even item is float, let's just skip it. 118 | if i%2 == 0 { 119 | iter.Skip() 120 | i++ 121 | continue 122 | } 123 | 124 | should.Zero(iter.ReadInt()) 125 | should.NoError(iter.Error) 126 | 127 | i++ 128 | } 129 | } 130 | } 131 | 132 | // jsonFloatIntArray generates JSON array where every 133 | // - even item is float 0.1 134 | // - odd item is integer 0 135 | // 136 | // [0.1, 0, 0.1, 0] 137 | func jsonFloatIntArray(t *testing.T, numberOfItems int) []byte { 138 | t.Helper() 139 | numbers := make([]jsoniter.Any, numberOfItems) 140 | for i := range numbers { 141 | switch i % 2 { 142 | case 0: 143 | numbers[i] = jsoniter.WrapFloat64(0.1) 144 | default: 145 | numbers[i] = jsoniter.WrapInt64(0) 146 | } 147 | } 148 | 149 | fixture, err := jsoniter.ConfigFastest.Marshal(numbers) 150 | if err != nil { 151 | panic(err) 152 | } 153 | 154 | b := &bytes.Buffer{} 155 | 156 | require.NoError( 157 | t, 158 | json.Compact(b, fixture), 159 | "json should be compactable", 160 | ) 161 | return b.Bytes() 162 | } 163 | 164 | func Benchmark_jsoniter_encode_int(b *testing.B) { 165 | stream := jsoniter.NewStream(jsoniter.ConfigDefault, ioutil.Discard, 64) 166 | for n := 0; n < b.N; n++ { 167 | stream.Reset(nil) 168 | stream.WriteUint64(0xffffffff) 169 | } 170 | } 171 | 172 | func Benchmark_itoa(b *testing.B) { 173 | for n := 0; n < b.N; n++ { 174 | strconv.FormatInt(0xffffffff, 10) 175 | } 176 | } 177 | 178 | func Benchmark_jsoniter_int(b *testing.B) { 179 | iter := jsoniter.NewIterator(jsoniter.ConfigDefault) 180 | input := []byte(`100`) 181 | for n := 0; n < b.N; n++ { 182 | iter.ResetBytes(input) 183 | iter.ReadInt64() 184 | } 185 | } 186 | 187 | func Benchmark_json_int(b *testing.B) { 188 | for n := 0; n < b.N; n++ { 189 | result := int64(0) 190 | json.Unmarshal([]byte(`-100`), &result) 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /misc_tests/jsoniter_interface_test.go: -------------------------------------------------------------------------------- 1 | package misc_tests 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/json-iterator/go" 6 | "github.com/stretchr/testify/require" 7 | "io" 8 | "testing" 9 | ) 10 | 11 | func Test_nil_non_empty_interface(t *testing.T) { 12 | type TestObject struct { 13 | Field []io.Closer 14 | } 15 | should := require.New(t) 16 | obj := TestObject{} 17 | b := []byte(`{"Field":["AAA"]}`) 18 | should.NotNil(json.Unmarshal(b, &obj)) 19 | should.NotNil(jsoniter.Unmarshal(b, &obj)) 20 | } 21 | 22 | func Test_nil_out_null_interface(t *testing.T) { 23 | type TestData struct { 24 | Field interface{} `json:"field"` 25 | } 26 | should := require.New(t) 27 | 28 | var boolVar bool 29 | obj := TestData{ 30 | Field: &boolVar, 31 | } 32 | 33 | data1 := []byte(`{"field": true}`) 34 | 35 | err := jsoniter.Unmarshal(data1, &obj) 36 | should.NoError(err) 37 | should.Equal(true, *(obj.Field.(*bool))) 38 | 39 | data2 := []byte(`{"field": null}`) 40 | 41 | err = jsoniter.Unmarshal(data2, &obj) 42 | should.NoError(err) 43 | should.Nil(obj.Field) 44 | 45 | // Checking stdlib behavior matches. 46 | obj2 := TestData{ 47 | Field: &boolVar, 48 | } 49 | 50 | err = json.Unmarshal(data1, &obj2) 51 | should.NoError(err) 52 | should.Equal(true, *(obj2.Field.(*bool))) 53 | 54 | err = json.Unmarshal(data2, &obj2) 55 | should.NoError(err) 56 | should.Equal(nil, obj2.Field) 57 | } 58 | 59 | func Test_overwrite_interface_ptr_value_with_nil(t *testing.T) { 60 | type Wrapper struct { 61 | Payload interface{} `json:"payload,omitempty"` 62 | } 63 | type Payload struct { 64 | Value int `json:"val,omitempty"` 65 | } 66 | 67 | should := require.New(t) 68 | 69 | payload := &Payload{} 70 | wrapper := &Wrapper{ 71 | Payload: &payload, 72 | } 73 | 74 | err := json.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper) 75 | should.NoError(err) 76 | should.Equal(&payload, wrapper.Payload) 77 | should.Equal(42, (*(wrapper.Payload.(**Payload))).Value) 78 | 79 | err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper) 80 | should.NoError(err) 81 | should.Equal(&payload, wrapper.Payload) 82 | should.Equal((*Payload)(nil), payload) 83 | 84 | payload = &Payload{} 85 | wrapper = &Wrapper{ 86 | Payload: &payload, 87 | } 88 | 89 | err = jsoniter.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper) 90 | should.Equal(nil, err) 91 | should.Equal(&payload, wrapper.Payload) 92 | should.Equal(42, (*(wrapper.Payload.(**Payload))).Value) 93 | 94 | err = jsoniter.Unmarshal([]byte(`{"payload": null}`), &wrapper) 95 | should.NoError(err) 96 | should.Equal(&payload, wrapper.Payload) 97 | should.Equal((*Payload)(nil), payload) 98 | } 99 | 100 | func Test_overwrite_interface_value_with_nil(t *testing.T) { 101 | type Wrapper struct { 102 | Payload interface{} `json:"payload,omitempty"` 103 | } 104 | type Payload struct { 105 | Value int `json:"val,omitempty"` 106 | } 107 | 108 | should := require.New(t) 109 | 110 | payload := &Payload{} 111 | wrapper := &Wrapper{ 112 | Payload: payload, 113 | } 114 | 115 | err := json.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper) 116 | should.NoError(err) 117 | should.Equal(42, wrapper.Payload.(*Payload).Value) 118 | 119 | err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper) 120 | should.NoError(err) 121 | should.Equal(nil, wrapper.Payload) 122 | should.Equal(42, payload.Value) 123 | 124 | payload = &Payload{} 125 | wrapper = &Wrapper{ 126 | Payload: payload, 127 | } 128 | 129 | err = jsoniter.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper) 130 | should.Equal(nil, err) 131 | should.Equal(42, wrapper.Payload.(*Payload).Value) 132 | 133 | err = jsoniter.Unmarshal([]byte(`{"payload": null}`), &wrapper) 134 | should.Equal(nil, err) 135 | should.Equal(nil, wrapper.Payload) 136 | should.Equal(42, payload.Value) 137 | } 138 | 139 | func Test_unmarshal_into_nil(t *testing.T) { 140 | type Payload struct { 141 | Value int `json:"val,omitempty"` 142 | } 143 | type Wrapper struct { 144 | Payload interface{} `json:"payload,omitempty"` 145 | } 146 | 147 | should := require.New(t) 148 | 149 | var payload *Payload 150 | wrapper := &Wrapper{ 151 | Payload: payload, 152 | } 153 | 154 | err := json.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper) 155 | should.NoError(err) 156 | should.NotNil(wrapper.Payload) 157 | should.Nil(payload) 158 | 159 | err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper) 160 | should.NoError(err) 161 | should.Nil(wrapper.Payload) 162 | should.Nil(payload) 163 | 164 | payload = nil 165 | wrapper = &Wrapper{ 166 | Payload: payload, 167 | } 168 | 169 | err = jsoniter.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper) 170 | should.NoError(err) 171 | should.NotNil(wrapper.Payload) 172 | should.Nil(payload) 173 | 174 | err = jsoniter.Unmarshal([]byte(`{"payload": null}`), &wrapper) 175 | should.NoError(err) 176 | should.Nil(wrapper.Payload) 177 | should.Nil(payload) 178 | } 179 | -------------------------------------------------------------------------------- /misc_tests/jsoniter_map_test.go: -------------------------------------------------------------------------------- 1 | package misc_tests 2 | 3 | import ( 4 | "encoding/json" 5 | "math/big" 6 | "testing" 7 | 8 | "github.com/json-iterator/go" 9 | "github.com/stretchr/testify/require" 10 | "strings" 11 | ) 12 | 13 | func Test_decode_TextMarshaler_key_map(t *testing.T) { 14 | should := require.New(t) 15 | var val map[*big.Float]string 16 | should.Nil(jsoniter.UnmarshalFromString(`{"1":"2"}`, &val)) 17 | str, err := jsoniter.MarshalToString(val) 18 | should.Nil(err) 19 | should.Equal(`{"1":"2"}`, str) 20 | } 21 | 22 | func Test_read_map_with_reader(t *testing.T) { 23 | should := require.New(t) 24 | input := `{"branch":"beta","change_log":"add the rows{10}","channel":"fros","create_time":"2017-06-13 16:39:08","firmware_list":"","md5":"80dee2bf7305bcf179582088e29fd7b9","note":{"CoreServices":{"md5":"d26975c0a8c7369f70ed699f2855cc2e","package_name":"CoreServices","version_code":"76","version_name":"1.0.76"},"FrDaemon":{"md5":"6b1f0626673200bc2157422cd2103f5d","package_name":"FrDaemon","version_code":"390","version_name":"1.0.390"},"FrGallery":{"md5":"90d767f0f31bcd3c1d27281ec979ba65","package_name":"FrGallery","version_code":"349","version_name":"1.0.349"},"FrLocal":{"md5":"f15a215b2c070a80a01f07bde4f219eb","package_name":"FrLocal","version_code":"791","version_name":"1.0.791"}},"pack_region_urls":{"CN":"https://s3.cn-north-1.amazonaws.com.cn/xxx-os/ttt_xxx_android_1.5.3.344.393.zip","default":"http://192.168.8.78/ttt_xxx_android_1.5.3.344.393.zip","local":"http://192.168.8.78/ttt_xxx_android_1.5.3.344.393.zip"},"pack_version":"1.5.3.344.393","pack_version_code":393,"region":"all","release_flag":0,"revision":62,"size":38966875,"status":3}` 25 | reader := strings.NewReader(input) 26 | decoder := jsoniter.ConfigCompatibleWithStandardLibrary.NewDecoder(reader) 27 | m1 := map[string]interface{}{} 28 | should.Nil(decoder.Decode(&m1)) 29 | m2 := map[string]interface{}{} 30 | should.Nil(json.Unmarshal([]byte(input), &m2)) 31 | should.Equal(m2, m1) 32 | should.Equal("1.0.76", m1["note"].(map[string]interface{})["CoreServices"].(map[string]interface{})["version_name"]) 33 | } 34 | 35 | func Test_map_eface_of_eface(t *testing.T) { 36 | should := require.New(t) 37 | json := jsoniter.ConfigCompatibleWithStandardLibrary 38 | output, err := json.MarshalToString(map[interface{}]interface{}{ 39 | "1": 2, 40 | 3: "4", 41 | }) 42 | should.NoError(err) 43 | should.Equal(`{"1":2,"3":"4"}`, output) 44 | } 45 | 46 | func Test_encode_nil_map(t *testing.T) { 47 | should := require.New(t) 48 | var nilMap map[string]string 49 | output, err := jsoniter.MarshalToString(nilMap) 50 | should.NoError(err) 51 | should.Equal(`null`, output) 52 | } 53 | -------------------------------------------------------------------------------- /misc_tests/jsoniter_null_test.go: -------------------------------------------------------------------------------- 1 | package misc_tests 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "testing" 7 | 8 | "github.com/json-iterator/go" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func Test_read_null(t *testing.T) { 13 | should := require.New(t) 14 | iter := jsoniter.ParseString(jsoniter.ConfigDefault, `null`) 15 | should.True(iter.ReadNil()) 16 | iter = jsoniter.ParseString(jsoniter.ConfigDefault, `null`) 17 | should.Nil(iter.Read()) 18 | iter = jsoniter.ParseString(jsoniter.ConfigDefault, `navy`) 19 | iter.Read() 20 | should.True(iter.Error != nil && iter.Error != io.EOF) 21 | iter = jsoniter.ParseString(jsoniter.ConfigDefault, `navy`) 22 | iter.ReadNil() 23 | should.True(iter.Error != nil && iter.Error != io.EOF) 24 | } 25 | 26 | func Test_write_null(t *testing.T) { 27 | should := require.New(t) 28 | buf := &bytes.Buffer{} 29 | stream := jsoniter.NewStream(jsoniter.ConfigDefault, buf, 4096) 30 | stream.WriteNil() 31 | stream.Flush() 32 | should.Nil(stream.Error) 33 | should.Equal("null", buf.String()) 34 | } 35 | 36 | func Test_decode_null_object_field(t *testing.T) { 37 | should := require.New(t) 38 | iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[null,"a"]`) 39 | iter.ReadArray() 40 | if iter.ReadObject() != "" { 41 | t.FailNow() 42 | } 43 | iter.ReadArray() 44 | if iter.ReadString() != "a" { 45 | t.FailNow() 46 | } 47 | type TestObject struct { 48 | Field string 49 | } 50 | objs := []TestObject{} 51 | should.Nil(jsoniter.UnmarshalFromString("[null]", &objs)) 52 | should.Len(objs, 1) 53 | } 54 | 55 | func Test_decode_null_array_element(t *testing.T) { 56 | should := require.New(t) 57 | iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[null,"a"]`) 58 | should.True(iter.ReadArray()) 59 | should.True(iter.ReadNil()) 60 | should.True(iter.ReadArray()) 61 | should.Equal("a", iter.ReadString()) 62 | } 63 | 64 | func Test_decode_null_string(t *testing.T) { 65 | should := require.New(t) 66 | iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[null,"a"]`) 67 | should.True(iter.ReadArray()) 68 | should.Equal("", iter.ReadString()) 69 | should.True(iter.ReadArray()) 70 | should.Equal("a", iter.ReadString()) 71 | } 72 | 73 | func Test_decode_null_skip(t *testing.T) { 74 | iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[null,"a"]`) 75 | iter.ReadArray() 76 | iter.Skip() 77 | iter.ReadArray() 78 | if iter.ReadString() != "a" { 79 | t.FailNow() 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /misc_tests/jsoniter_raw_message_test.go: -------------------------------------------------------------------------------- 1 | package misc_tests 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/json-iterator/go" 6 | "github.com/stretchr/testify/require" 7 | "strings" 8 | "testing" 9 | ) 10 | 11 | func Test_jsoniter_RawMessage(t *testing.T) { 12 | should := require.New(t) 13 | var data jsoniter.RawMessage 14 | should.Nil(jsoniter.Unmarshal([]byte(`[1,2,3]`), &data)) 15 | should.Equal(`[1,2,3]`, string(data)) 16 | str, err := jsoniter.MarshalToString(data) 17 | should.Nil(err) 18 | should.Equal(`[1,2,3]`, str) 19 | } 20 | 21 | func Test_encode_map_of_jsoniter_raw_message(t *testing.T) { 22 | should := require.New(t) 23 | type RawMap map[string]*jsoniter.RawMessage 24 | value := jsoniter.RawMessage("[]") 25 | rawMap := RawMap{"hello": &value} 26 | output, err := jsoniter.MarshalToString(rawMap) 27 | should.Nil(err) 28 | should.Equal(`{"hello":[]}`, output) 29 | } 30 | 31 | func Test_marshal_invalid_json_raw_message(t *testing.T) { 32 | type A struct { 33 | Raw json.RawMessage `json:"raw"` 34 | } 35 | message := []byte(`{}`) 36 | 37 | a := A{} 38 | should := require.New(t) 39 | should.Nil(jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(message, &a)) 40 | aout, aouterr := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(&a) 41 | should.Equal(`{"raw":null}`, string(aout)) 42 | should.Nil(aouterr) 43 | } 44 | 45 | func Test_marshal_nil_json_raw_message(t *testing.T) { 46 | type A struct { 47 | Nil1 jsoniter.RawMessage `json:"raw1"` 48 | Nil2 json.RawMessage `json:"raw2"` 49 | } 50 | 51 | a := A{} 52 | should := require.New(t) 53 | aout, aouterr := jsoniter.Marshal(&a) 54 | should.Equal(`{"raw1":null,"raw2":null}`, string(aout)) 55 | should.Nil(aouterr) 56 | 57 | a.Nil1 = []byte(`Any`) 58 | a.Nil2 = []byte(`Any`) 59 | should.Nil(jsoniter.Unmarshal(aout, &a)) 60 | should.Nil(a.Nil1) 61 | should.Nil(a.Nil2) 62 | } 63 | 64 | func Test_raw_message_memory_not_copied_issue(t *testing.T) { 65 | jsonStream := `{"name":"xxxxx","bundle_id":"com.zonst.majiang","app_platform":"ios","app_category":"100103", "budget_day":1000,"bidding_min":1,"bidding_max":2,"bidding_type":"CPM", "freq":{"open":true,"type":"day","num":100},"speed":1, "targeting":{"vendor":{"open":true,"list":["zonst"]}, "geo_code":{"open":true,"list":["156110100"]},"app_category":{"open":true,"list":["100101"]}, "day_parting":{"open":true,"list":["100409","100410"]},"device_type":{"open":true,"list":["ipad"]}, "os_version":{"open":true,"list":[10]},"carrier":{"open":true,"list":["mobile"]}, "network":{"open":true,"list":["4G"]}},"url":{"tracking_imp_url":"http://www.baidu.com", "tracking_clk_url":"http://www.baidu.com","jump_url":"http://www.baidu.com","deep_link_url":"http://www.baidu.com"}}` 66 | type IteratorObject struct { 67 | Name *string `json:"name"` 68 | BundleId *string `json:"bundle_id"` 69 | AppCategory *string `json:"app_category"` 70 | AppPlatform *string `json:"app_platform"` 71 | BudgetDay *float32 `json:"budget_day"` 72 | BiddingMax *float32 `json:"bidding_max"` 73 | BiddingMin *float32 `json:"bidding_min"` 74 | BiddingType *string `json:"bidding_type"` 75 | Freq *jsoniter.RawMessage `json:"freq"` 76 | Targeting *jsoniter.RawMessage `json:"targeting"` 77 | Url *jsoniter.RawMessage `json:"url"` 78 | Speed *int `json:"speed" db:"speed"` 79 | } 80 | 81 | obj := &IteratorObject{} 82 | decoder := jsoniter.NewDecoder(strings.NewReader(jsonStream)) 83 | err := decoder.Decode(obj) 84 | should := require.New(t) 85 | should.Nil(err) 86 | should.Equal(`{"open":true,"type":"day","num":100}`, string(*obj.Freq)) 87 | } 88 | -------------------------------------------------------------------------------- /pool.go: -------------------------------------------------------------------------------- 1 | package jsoniter 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | // IteratorPool a thread safe pool of iterators with same configuration 8 | type IteratorPool interface { 9 | BorrowIterator(data []byte) *Iterator 10 | ReturnIterator(iter *Iterator) 11 | } 12 | 13 | // StreamPool a thread safe pool of streams with same configuration 14 | type StreamPool interface { 15 | BorrowStream(writer io.Writer) *Stream 16 | ReturnStream(stream *Stream) 17 | } 18 | 19 | func (cfg *frozenConfig) BorrowStream(writer io.Writer) *Stream { 20 | stream := cfg.streamPool.Get().(*Stream) 21 | stream.Reset(writer) 22 | return stream 23 | } 24 | 25 | func (cfg *frozenConfig) ReturnStream(stream *Stream) { 26 | stream.out = nil 27 | stream.Error = nil 28 | stream.Attachment = nil 29 | cfg.streamPool.Put(stream) 30 | } 31 | 32 | func (cfg *frozenConfig) BorrowIterator(data []byte) *Iterator { 33 | iter := cfg.iteratorPool.Get().(*Iterator) 34 | iter.ResetBytes(data) 35 | return iter 36 | } 37 | 38 | func (cfg *frozenConfig) ReturnIterator(iter *Iterator) { 39 | iter.Error = nil 40 | iter.Attachment = nil 41 | cfg.iteratorPool.Put(iter) 42 | } 43 | -------------------------------------------------------------------------------- /reflect_array.go: -------------------------------------------------------------------------------- 1 | package jsoniter 2 | 3 | import ( 4 | "fmt" 5 | "github.com/modern-go/reflect2" 6 | "io" 7 | "unsafe" 8 | ) 9 | 10 | func decoderOfArray(ctx *ctx, typ reflect2.Type) ValDecoder { 11 | arrayType := typ.(*reflect2.UnsafeArrayType) 12 | decoder := decoderOfType(ctx.append("[arrayElem]"), arrayType.Elem()) 13 | return &arrayDecoder{arrayType, decoder} 14 | } 15 | 16 | func encoderOfArray(ctx *ctx, typ reflect2.Type) ValEncoder { 17 | arrayType := typ.(*reflect2.UnsafeArrayType) 18 | if arrayType.Len() == 0 { 19 | return emptyArrayEncoder{} 20 | } 21 | encoder := encoderOfType(ctx.append("[arrayElem]"), arrayType.Elem()) 22 | return &arrayEncoder{arrayType, encoder} 23 | } 24 | 25 | type emptyArrayEncoder struct{} 26 | 27 | func (encoder emptyArrayEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { 28 | stream.WriteEmptyArray() 29 | } 30 | 31 | func (encoder emptyArrayEncoder) IsEmpty(ptr unsafe.Pointer) bool { 32 | return true 33 | } 34 | 35 | type arrayEncoder struct { 36 | arrayType *reflect2.UnsafeArrayType 37 | elemEncoder ValEncoder 38 | } 39 | 40 | func (encoder *arrayEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { 41 | stream.WriteArrayStart() 42 | elemPtr := unsafe.Pointer(ptr) 43 | encoder.elemEncoder.Encode(elemPtr, stream) 44 | for i := 1; i < encoder.arrayType.Len(); i++ { 45 | stream.WriteMore() 46 | elemPtr = encoder.arrayType.UnsafeGetIndex(ptr, i) 47 | encoder.elemEncoder.Encode(elemPtr, stream) 48 | } 49 | stream.WriteArrayEnd() 50 | if stream.Error != nil && stream.Error != io.EOF { 51 | stream.Error = fmt.Errorf("%v: %s", encoder.arrayType, stream.Error.Error()) 52 | } 53 | } 54 | 55 | func (encoder *arrayEncoder) IsEmpty(ptr unsafe.Pointer) bool { 56 | return false 57 | } 58 | 59 | type arrayDecoder struct { 60 | arrayType *reflect2.UnsafeArrayType 61 | elemDecoder ValDecoder 62 | } 63 | 64 | func (decoder *arrayDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { 65 | decoder.doDecode(ptr, iter) 66 | if iter.Error != nil && iter.Error != io.EOF { 67 | iter.Error = fmt.Errorf("%v: %s", decoder.arrayType, iter.Error.Error()) 68 | } 69 | } 70 | 71 | func (decoder *arrayDecoder) doDecode(ptr unsafe.Pointer, iter *Iterator) { 72 | c := iter.nextToken() 73 | arrayType := decoder.arrayType 74 | if c == 'n' { 75 | iter.skipThreeBytes('u', 'l', 'l') 76 | return 77 | } 78 | if c != '[' { 79 | iter.ReportError("decode array", "expect [ or n, but found "+string([]byte{c})) 80 | return 81 | } 82 | c = iter.nextToken() 83 | if c == ']' { 84 | return 85 | } 86 | iter.unreadByte() 87 | elemPtr := arrayType.UnsafeGetIndex(ptr, 0) 88 | decoder.elemDecoder.Decode(elemPtr, iter) 89 | length := 1 90 | for c = iter.nextToken(); c == ','; c = iter.nextToken() { 91 | if length >= arrayType.Len() { 92 | iter.Skip() 93 | continue 94 | } 95 | idx := length 96 | length += 1 97 | elemPtr = arrayType.UnsafeGetIndex(ptr, idx) 98 | decoder.elemDecoder.Decode(elemPtr, iter) 99 | } 100 | if c != ']' { 101 | iter.ReportError("decode array", "expect ], but found "+string([]byte{c})) 102 | return 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /reflect_dynamic.go: -------------------------------------------------------------------------------- 1 | package jsoniter 2 | 3 | import ( 4 | "github.com/modern-go/reflect2" 5 | "reflect" 6 | "unsafe" 7 | ) 8 | 9 | type dynamicEncoder struct { 10 | valType reflect2.Type 11 | } 12 | 13 | func (encoder *dynamicEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { 14 | obj := encoder.valType.UnsafeIndirect(ptr) 15 | stream.WriteVal(obj) 16 | } 17 | 18 | func (encoder *dynamicEncoder) IsEmpty(ptr unsafe.Pointer) bool { 19 | return encoder.valType.UnsafeIndirect(ptr) == nil 20 | } 21 | 22 | type efaceDecoder struct { 23 | } 24 | 25 | func (decoder *efaceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { 26 | pObj := (*interface{})(ptr) 27 | obj := *pObj 28 | if obj == nil { 29 | *pObj = iter.Read() 30 | return 31 | } 32 | typ := reflect2.TypeOf(obj) 33 | if typ.Kind() != reflect.Ptr { 34 | *pObj = iter.Read() 35 | return 36 | } 37 | ptrType := typ.(*reflect2.UnsafePtrType) 38 | ptrElemType := ptrType.Elem() 39 | if iter.WhatIsNext() == NilValue { 40 | if ptrElemType.Kind() != reflect.Ptr { 41 | iter.skipFourBytes('n', 'u', 'l', 'l') 42 | *pObj = nil 43 | return 44 | } 45 | } 46 | if reflect2.IsNil(obj) { 47 | obj := ptrElemType.New() 48 | iter.ReadVal(obj) 49 | *pObj = obj 50 | return 51 | } 52 | iter.ReadVal(obj) 53 | } 54 | 55 | type ifaceDecoder struct { 56 | valType *reflect2.UnsafeIFaceType 57 | } 58 | 59 | func (decoder *ifaceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { 60 | if iter.ReadNil() { 61 | decoder.valType.UnsafeSet(ptr, decoder.valType.UnsafeNew()) 62 | return 63 | } 64 | obj := decoder.valType.UnsafeIndirect(ptr) 65 | if reflect2.IsNil(obj) { 66 | iter.ReportError("decode non empty interface", "can not unmarshal into nil") 67 | return 68 | } 69 | iter.ReadVal(obj) 70 | } 71 | -------------------------------------------------------------------------------- /reflect_json_number.go: -------------------------------------------------------------------------------- 1 | package jsoniter 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/modern-go/reflect2" 6 | "strconv" 7 | "unsafe" 8 | ) 9 | 10 | type Number string 11 | 12 | // String returns the literal text of the number. 13 | func (n Number) String() string { return string(n) } 14 | 15 | // Float64 returns the number as a float64. 16 | func (n Number) Float64() (float64, error) { 17 | return strconv.ParseFloat(string(n), 64) 18 | } 19 | 20 | // Int64 returns the number as an int64. 21 | func (n Number) Int64() (int64, error) { 22 | return strconv.ParseInt(string(n), 10, 64) 23 | } 24 | 25 | func CastJsonNumber(val interface{}) (string, bool) { 26 | switch typedVal := val.(type) { 27 | case json.Number: 28 | return string(typedVal), true 29 | case Number: 30 | return string(typedVal), true 31 | } 32 | return "", false 33 | } 34 | 35 | var jsonNumberType = reflect2.TypeOfPtr((*json.Number)(nil)).Elem() 36 | var jsoniterNumberType = reflect2.TypeOfPtr((*Number)(nil)).Elem() 37 | 38 | func createDecoderOfJsonNumber(ctx *ctx, typ reflect2.Type) ValDecoder { 39 | if typ.AssignableTo(jsonNumberType) { 40 | return &jsonNumberCodec{} 41 | } 42 | if typ.AssignableTo(jsoniterNumberType) { 43 | return &jsoniterNumberCodec{} 44 | } 45 | return nil 46 | } 47 | 48 | func createEncoderOfJsonNumber(ctx *ctx, typ reflect2.Type) ValEncoder { 49 | if typ.AssignableTo(jsonNumberType) { 50 | return &jsonNumberCodec{} 51 | } 52 | if typ.AssignableTo(jsoniterNumberType) { 53 | return &jsoniterNumberCodec{} 54 | } 55 | return nil 56 | } 57 | 58 | type jsonNumberCodec struct { 59 | } 60 | 61 | func (codec *jsonNumberCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { 62 | switch iter.WhatIsNext() { 63 | case StringValue: 64 | *((*json.Number)(ptr)) = json.Number(iter.ReadString()) 65 | case NilValue: 66 | iter.skipFourBytes('n', 'u', 'l', 'l') 67 | *((*json.Number)(ptr)) = "" 68 | default: 69 | *((*json.Number)(ptr)) = json.Number([]byte(iter.readNumberAsString())) 70 | } 71 | } 72 | 73 | func (codec *jsonNumberCodec) Encode(ptr unsafe.Pointer, stream *Stream) { 74 | number := *((*json.Number)(ptr)) 75 | if len(number) == 0 { 76 | stream.writeByte('0') 77 | } else { 78 | stream.WriteRaw(string(number)) 79 | } 80 | } 81 | 82 | func (codec *jsonNumberCodec) IsEmpty(ptr unsafe.Pointer) bool { 83 | return len(*((*json.Number)(ptr))) == 0 84 | } 85 | 86 | type jsoniterNumberCodec struct { 87 | } 88 | 89 | func (codec *jsoniterNumberCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { 90 | switch iter.WhatIsNext() { 91 | case StringValue: 92 | *((*Number)(ptr)) = Number(iter.ReadString()) 93 | case NilValue: 94 | iter.skipFourBytes('n', 'u', 'l', 'l') 95 | *((*Number)(ptr)) = "" 96 | default: 97 | *((*Number)(ptr)) = Number([]byte(iter.readNumberAsString())) 98 | } 99 | } 100 | 101 | func (codec *jsoniterNumberCodec) Encode(ptr unsafe.Pointer, stream *Stream) { 102 | number := *((*Number)(ptr)) 103 | if len(number) == 0 { 104 | stream.writeByte('0') 105 | } else { 106 | stream.WriteRaw(string(number)) 107 | } 108 | } 109 | 110 | func (codec *jsoniterNumberCodec) IsEmpty(ptr unsafe.Pointer) bool { 111 | return len(*((*Number)(ptr))) == 0 112 | } 113 | -------------------------------------------------------------------------------- /reflect_json_raw_message.go: -------------------------------------------------------------------------------- 1 | package jsoniter 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/modern-go/reflect2" 6 | "unsafe" 7 | ) 8 | 9 | var jsonRawMessageType = reflect2.TypeOfPtr((*json.RawMessage)(nil)).Elem() 10 | var jsoniterRawMessageType = reflect2.TypeOfPtr((*RawMessage)(nil)).Elem() 11 | 12 | func createEncoderOfJsonRawMessage(ctx *ctx, typ reflect2.Type) ValEncoder { 13 | if typ == jsonRawMessageType { 14 | return &jsonRawMessageCodec{} 15 | } 16 | if typ == jsoniterRawMessageType { 17 | return &jsoniterRawMessageCodec{} 18 | } 19 | return nil 20 | } 21 | 22 | func createDecoderOfJsonRawMessage(ctx *ctx, typ reflect2.Type) ValDecoder { 23 | if typ == jsonRawMessageType { 24 | return &jsonRawMessageCodec{} 25 | } 26 | if typ == jsoniterRawMessageType { 27 | return &jsoniterRawMessageCodec{} 28 | } 29 | return nil 30 | } 31 | 32 | type jsonRawMessageCodec struct { 33 | } 34 | 35 | func (codec *jsonRawMessageCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { 36 | if iter.ReadNil() { 37 | *((*json.RawMessage)(ptr)) = nil 38 | } else { 39 | *((*json.RawMessage)(ptr)) = iter.SkipAndReturnBytes() 40 | } 41 | } 42 | 43 | func (codec *jsonRawMessageCodec) Encode(ptr unsafe.Pointer, stream *Stream) { 44 | if *((*json.RawMessage)(ptr)) == nil { 45 | stream.WriteNil() 46 | } else { 47 | stream.WriteRaw(string(*((*json.RawMessage)(ptr)))) 48 | } 49 | } 50 | 51 | func (codec *jsonRawMessageCodec) IsEmpty(ptr unsafe.Pointer) bool { 52 | return len(*((*json.RawMessage)(ptr))) == 0 53 | } 54 | 55 | type jsoniterRawMessageCodec struct { 56 | } 57 | 58 | func (codec *jsoniterRawMessageCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { 59 | if iter.ReadNil() { 60 | *((*RawMessage)(ptr)) = nil 61 | } else { 62 | *((*RawMessage)(ptr)) = iter.SkipAndReturnBytes() 63 | } 64 | } 65 | 66 | func (codec *jsoniterRawMessageCodec) Encode(ptr unsafe.Pointer, stream *Stream) { 67 | if *((*RawMessage)(ptr)) == nil { 68 | stream.WriteNil() 69 | } else { 70 | stream.WriteRaw(string(*((*RawMessage)(ptr)))) 71 | } 72 | } 73 | 74 | func (codec *jsoniterRawMessageCodec) IsEmpty(ptr unsafe.Pointer) bool { 75 | return len(*((*RawMessage)(ptr))) == 0 76 | } 77 | -------------------------------------------------------------------------------- /reflect_optional.go: -------------------------------------------------------------------------------- 1 | package jsoniter 2 | 3 | import ( 4 | "github.com/modern-go/reflect2" 5 | "unsafe" 6 | ) 7 | 8 | func decoderOfOptional(ctx *ctx, typ reflect2.Type) ValDecoder { 9 | ptrType := typ.(*reflect2.UnsafePtrType) 10 | elemType := ptrType.Elem() 11 | decoder := decoderOfType(ctx, elemType) 12 | return &OptionalDecoder{elemType, decoder} 13 | } 14 | 15 | func encoderOfOptional(ctx *ctx, typ reflect2.Type) ValEncoder { 16 | ptrType := typ.(*reflect2.UnsafePtrType) 17 | elemType := ptrType.Elem() 18 | elemEncoder := encoderOfType(ctx, elemType) 19 | encoder := &OptionalEncoder{elemEncoder} 20 | return encoder 21 | } 22 | 23 | type OptionalDecoder struct { 24 | ValueType reflect2.Type 25 | ValueDecoder ValDecoder 26 | } 27 | 28 | func (decoder *OptionalDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { 29 | if iter.ReadNil() { 30 | *((*unsafe.Pointer)(ptr)) = nil 31 | } else { 32 | if *((*unsafe.Pointer)(ptr)) == nil { 33 | //pointer to null, we have to allocate memory to hold the value 34 | newPtr := decoder.ValueType.UnsafeNew() 35 | decoder.ValueDecoder.Decode(newPtr, iter) 36 | *((*unsafe.Pointer)(ptr)) = newPtr 37 | } else { 38 | //reuse existing instance 39 | decoder.ValueDecoder.Decode(*((*unsafe.Pointer)(ptr)), iter) 40 | } 41 | } 42 | } 43 | 44 | type dereferenceDecoder struct { 45 | // only to deference a pointer 46 | valueType reflect2.Type 47 | valueDecoder ValDecoder 48 | } 49 | 50 | func (decoder *dereferenceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { 51 | if *((*unsafe.Pointer)(ptr)) == nil { 52 | //pointer to null, we have to allocate memory to hold the value 53 | newPtr := decoder.valueType.UnsafeNew() 54 | decoder.valueDecoder.Decode(newPtr, iter) 55 | *((*unsafe.Pointer)(ptr)) = newPtr 56 | } else { 57 | //reuse existing instance 58 | decoder.valueDecoder.Decode(*((*unsafe.Pointer)(ptr)), iter) 59 | } 60 | } 61 | 62 | type OptionalEncoder struct { 63 | ValueEncoder ValEncoder 64 | } 65 | 66 | func (encoder *OptionalEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { 67 | if *((*unsafe.Pointer)(ptr)) == nil { 68 | stream.WriteNil() 69 | } else { 70 | encoder.ValueEncoder.Encode(*((*unsafe.Pointer)(ptr)), stream) 71 | } 72 | } 73 | 74 | func (encoder *OptionalEncoder) IsEmpty(ptr unsafe.Pointer) bool { 75 | return *((*unsafe.Pointer)(ptr)) == nil 76 | } 77 | 78 | type dereferenceEncoder struct { 79 | ValueEncoder ValEncoder 80 | } 81 | 82 | func (encoder *dereferenceEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { 83 | if *((*unsafe.Pointer)(ptr)) == nil { 84 | stream.WriteNil() 85 | } else { 86 | encoder.ValueEncoder.Encode(*((*unsafe.Pointer)(ptr)), stream) 87 | } 88 | } 89 | 90 | func (encoder *dereferenceEncoder) IsEmpty(ptr unsafe.Pointer) bool { 91 | dePtr := *((*unsafe.Pointer)(ptr)) 92 | if dePtr == nil { 93 | return true 94 | } 95 | return encoder.ValueEncoder.IsEmpty(dePtr) 96 | } 97 | 98 | func (encoder *dereferenceEncoder) IsEmbeddedPtrNil(ptr unsafe.Pointer) bool { 99 | deReferenced := *((*unsafe.Pointer)(ptr)) 100 | if deReferenced == nil { 101 | return true 102 | } 103 | isEmbeddedPtrNil, converted := encoder.ValueEncoder.(IsEmbeddedPtrNil) 104 | if !converted { 105 | return false 106 | } 107 | fieldPtr := unsafe.Pointer(deReferenced) 108 | return isEmbeddedPtrNil.IsEmbeddedPtrNil(fieldPtr) 109 | } 110 | 111 | type referenceEncoder struct { 112 | encoder ValEncoder 113 | } 114 | 115 | func (encoder *referenceEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { 116 | encoder.encoder.Encode(unsafe.Pointer(&ptr), stream) 117 | } 118 | 119 | func (encoder *referenceEncoder) IsEmpty(ptr unsafe.Pointer) bool { 120 | return encoder.encoder.IsEmpty(unsafe.Pointer(&ptr)) 121 | } 122 | 123 | type referenceDecoder struct { 124 | decoder ValDecoder 125 | } 126 | 127 | func (decoder *referenceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { 128 | decoder.decoder.Decode(unsafe.Pointer(&ptr), iter) 129 | } 130 | -------------------------------------------------------------------------------- /reflect_slice.go: -------------------------------------------------------------------------------- 1 | package jsoniter 2 | 3 | import ( 4 | "fmt" 5 | "github.com/modern-go/reflect2" 6 | "io" 7 | "unsafe" 8 | ) 9 | 10 | func decoderOfSlice(ctx *ctx, typ reflect2.Type) ValDecoder { 11 | sliceType := typ.(*reflect2.UnsafeSliceType) 12 | decoder := decoderOfType(ctx.append("[sliceElem]"), sliceType.Elem()) 13 | return &sliceDecoder{sliceType, decoder} 14 | } 15 | 16 | func encoderOfSlice(ctx *ctx, typ reflect2.Type) ValEncoder { 17 | sliceType := typ.(*reflect2.UnsafeSliceType) 18 | encoder := encoderOfType(ctx.append("[sliceElem]"), sliceType.Elem()) 19 | return &sliceEncoder{sliceType, encoder} 20 | } 21 | 22 | type sliceEncoder struct { 23 | sliceType *reflect2.UnsafeSliceType 24 | elemEncoder ValEncoder 25 | } 26 | 27 | func (encoder *sliceEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { 28 | if encoder.sliceType.UnsafeIsNil(ptr) { 29 | stream.WriteNil() 30 | return 31 | } 32 | length := encoder.sliceType.UnsafeLengthOf(ptr) 33 | if length == 0 { 34 | stream.WriteEmptyArray() 35 | return 36 | } 37 | stream.WriteArrayStart() 38 | encoder.elemEncoder.Encode(encoder.sliceType.UnsafeGetIndex(ptr, 0), stream) 39 | for i := 1; i < length; i++ { 40 | stream.WriteMore() 41 | elemPtr := encoder.sliceType.UnsafeGetIndex(ptr, i) 42 | encoder.elemEncoder.Encode(elemPtr, stream) 43 | } 44 | stream.WriteArrayEnd() 45 | if stream.Error != nil && stream.Error != io.EOF { 46 | stream.Error = fmt.Errorf("%v: %s", encoder.sliceType, stream.Error.Error()) 47 | } 48 | } 49 | 50 | func (encoder *sliceEncoder) IsEmpty(ptr unsafe.Pointer) bool { 51 | return encoder.sliceType.UnsafeLengthOf(ptr) == 0 52 | } 53 | 54 | type sliceDecoder struct { 55 | sliceType *reflect2.UnsafeSliceType 56 | elemDecoder ValDecoder 57 | } 58 | 59 | func (decoder *sliceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { 60 | decoder.doDecode(ptr, iter) 61 | if iter.Error != nil && iter.Error != io.EOF { 62 | iter.Error = fmt.Errorf("%v: %s", decoder.sliceType, iter.Error.Error()) 63 | } 64 | } 65 | 66 | func (decoder *sliceDecoder) doDecode(ptr unsafe.Pointer, iter *Iterator) { 67 | c := iter.nextToken() 68 | sliceType := decoder.sliceType 69 | if c == 'n' { 70 | iter.skipThreeBytes('u', 'l', 'l') 71 | sliceType.UnsafeSetNil(ptr) 72 | return 73 | } 74 | if c != '[' { 75 | iter.ReportError("decode slice", "expect [ or n, but found "+string([]byte{c})) 76 | return 77 | } 78 | c = iter.nextToken() 79 | if c == ']' { 80 | sliceType.UnsafeSet(ptr, sliceType.UnsafeMakeSlice(0, 0)) 81 | return 82 | } 83 | iter.unreadByte() 84 | sliceType.UnsafeGrow(ptr, 1) 85 | elemPtr := sliceType.UnsafeGetIndex(ptr, 0) 86 | decoder.elemDecoder.Decode(elemPtr, iter) 87 | length := 1 88 | for c = iter.nextToken(); c == ','; c = iter.nextToken() { 89 | idx := length 90 | length += 1 91 | sliceType.UnsafeGrow(ptr, length) 92 | elemPtr = sliceType.UnsafeGetIndex(ptr, idx) 93 | decoder.elemDecoder.Decode(elemPtr, iter) 94 | } 95 | if c != ']' { 96 | iter.ReportError("decode slice", "expect ], but found "+string([]byte{c})) 97 | return 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /skip_tests/array_test.go: -------------------------------------------------------------------------------- 1 | package skip_tests 2 | 3 | func init() { 4 | testCases = append(testCases, testCase{ 5 | ptr: (*[]interface{})(nil), 6 | inputs: []string{ 7 | `[]`, // valid 8 | `[1]`, // valid 9 | `[ 1, "hello"]`, // valid 10 | `[abc]`, // invalid 11 | `[`, // invalid 12 | `[[]`, // invalid 13 | }, 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /skip_tests/float64_test.go: -------------------------------------------------------------------------------- 1 | package skip_tests 2 | 3 | func init() { 4 | testCases = append(testCases, testCase{ 5 | ptr: (*float64)(nil), 6 | inputs: []string{ 7 | "+1", // invalid 8 | "-a", // invalid 9 | "-\x00", // invalid, zero byte 10 | "0.1", // valid 11 | "0..1", // invalid, more dot 12 | "1e+1", // valid 13 | "1+1", // invalid 14 | "1E1", // valid, e or E 15 | "1ee1", // invalid 16 | "100a", // invalid 17 | "10.", // invalid 18 | }, 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /skip_tests/jsoniter_skip_test.go: -------------------------------------------------------------------------------- 1 | package skip_tests 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "testing" 7 | 8 | "github.com/json-iterator/go" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func Test_skip_number_in_array(t *testing.T) { 13 | should := require.New(t) 14 | iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[-0.12, "stream"]`) 15 | iter.ReadArray() 16 | iter.Skip() 17 | iter.ReadArray() 18 | should.Nil(iter.Error) 19 | should.Equal("stream", iter.ReadString()) 20 | } 21 | 22 | func Test_skip_string_in_array(t *testing.T) { 23 | should := require.New(t) 24 | iter := jsoniter.ParseString(jsoniter.ConfigDefault, `["hello", "stream"]`) 25 | iter.ReadArray() 26 | iter.Skip() 27 | iter.ReadArray() 28 | should.Nil(iter.Error) 29 | should.Equal("stream", iter.ReadString()) 30 | } 31 | 32 | func Test_skip_null(t *testing.T) { 33 | iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[null , "stream"]`) 34 | iter.ReadArray() 35 | iter.Skip() 36 | iter.ReadArray() 37 | if iter.ReadString() != "stream" { 38 | t.FailNow() 39 | } 40 | } 41 | 42 | func Test_skip_true(t *testing.T) { 43 | iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[true , "stream"]`) 44 | iter.ReadArray() 45 | iter.Skip() 46 | iter.ReadArray() 47 | if iter.ReadString() != "stream" { 48 | t.FailNow() 49 | } 50 | } 51 | 52 | func Test_skip_false(t *testing.T) { 53 | iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[false , "stream"]`) 54 | iter.ReadArray() 55 | iter.Skip() 56 | iter.ReadArray() 57 | if iter.ReadString() != "stream" { 58 | t.FailNow() 59 | } 60 | } 61 | 62 | func Test_skip_array(t *testing.T) { 63 | iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[[1, [2, [3], 4]], "stream"]`) 64 | iter.ReadArray() 65 | iter.Skip() 66 | iter.ReadArray() 67 | if iter.ReadString() != "stream" { 68 | t.FailNow() 69 | } 70 | } 71 | 72 | func Test_skip_empty_array(t *testing.T) { 73 | iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[ [ ], "stream"]`) 74 | iter.ReadArray() 75 | iter.Skip() 76 | iter.ReadArray() 77 | if iter.ReadString() != "stream" { 78 | t.FailNow() 79 | } 80 | } 81 | 82 | func Test_skip_nested(t *testing.T) { 83 | iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[ {"a" : [{"stream": "c"}], "d": 102 }, "stream"]`) 84 | iter.ReadArray() 85 | iter.Skip() 86 | iter.ReadArray() 87 | if iter.ReadString() != "stream" { 88 | t.FailNow() 89 | } 90 | } 91 | 92 | func Test_skip_and_return_bytes(t *testing.T) { 93 | should := require.New(t) 94 | iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[ {"a" : [{"stream": "c"}], "d": 102 }, "stream"]`) 95 | iter.ReadArray() 96 | skipped := iter.SkipAndReturnBytes() 97 | should.Equal(`{"a" : [{"stream": "c"}], "d": 102 }`, string(skipped)) 98 | } 99 | 100 | func Test_skip_and_return_bytes_with_reader(t *testing.T) { 101 | should := require.New(t) 102 | iter := jsoniter.Parse(jsoniter.ConfigDefault, bytes.NewBufferString(`[ {"a" : [{"stream": "c"}], "d": 102 }, "stream"]`), 4) 103 | iter.ReadArray() 104 | skipped := iter.SkipAndReturnBytes() 105 | should.Equal(`{"a" : [{"stream": "c"}], "d": 102 }`, string(skipped)) 106 | } 107 | 108 | func Test_append_skip_and_return_bytes_with_reader(t *testing.T) { 109 | should := require.New(t) 110 | iter := jsoniter.Parse(jsoniter.ConfigDefault, bytes.NewBufferString(`[ {"a" : [{"stream": "c"}], "d": 102 }, "stream"]`), 4) 111 | iter.ReadArray() 112 | buf := make([]byte, 0, 1024) 113 | buf = iter.SkipAndAppendBytes(buf) 114 | should.Equal(`{"a" : [{"stream": "c"}], "d": 102 }`, string(buf)) 115 | } 116 | 117 | func Test_skip_empty(t *testing.T) { 118 | should := require.New(t) 119 | should.NotNil(jsoniter.Get([]byte("")).LastError()) 120 | } 121 | 122 | type TestResp struct { 123 | Code uint64 124 | } 125 | 126 | func Benchmark_jsoniter_skip(b *testing.B) { 127 | input := []byte(` 128 | { 129 | "_shards":{ 130 | "total" : 5, 131 | "successful" : 5, 132 | "failed" : 0 133 | }, 134 | "hits":{ 135 | "total" : 1, 136 | "hits" : [ 137 | { 138 | "_index" : "twitter", 139 | "_type" : "tweet", 140 | "_id" : "1", 141 | "_source" : { 142 | "user" : "kimchy", 143 | "postDate" : "2009-11-15T14:12:12", 144 | "message" : "trying out Elasticsearch" 145 | } 146 | } 147 | ] 148 | }, 149 | "code": 200 150 | }`) 151 | for n := 0; n < b.N; n++ { 152 | result := TestResp{} 153 | iter := jsoniter.ParseBytes(jsoniter.ConfigDefault, input) 154 | for field := iter.ReadObject(); field != ""; field = iter.ReadObject() { 155 | switch field { 156 | case "code": 157 | result.Code = iter.ReadUint64() 158 | default: 159 | iter.Skip() 160 | } 161 | } 162 | } 163 | } 164 | 165 | func Benchmark_json_skip(b *testing.B) { 166 | input := []byte(` 167 | { 168 | "_shards":{ 169 | "total" : 5, 170 | "successful" : 5, 171 | "failed" : 0 172 | }, 173 | "hits":{ 174 | "total" : 1, 175 | "hits" : [ 176 | { 177 | "_index" : "twitter", 178 | "_type" : "tweet", 179 | "_id" : "1", 180 | "_source" : { 181 | "user" : "kimchy", 182 | "postDate" : "2009-11-15T14:12:12", 183 | "message" : "trying out Elasticsearch" 184 | } 185 | } 186 | ] 187 | }, 188 | "code": 200 189 | }`) 190 | for n := 0; n < b.N; n++ { 191 | result := TestResp{} 192 | json.Unmarshal(input, &result) 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /skip_tests/skip_test.go: -------------------------------------------------------------------------------- 1 | package skip_tests 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "github.com/json-iterator/go" 7 | "github.com/stretchr/testify/require" 8 | "io" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | type testCase struct { 14 | ptr interface{} 15 | inputs []string 16 | } 17 | 18 | var testCases []testCase 19 | 20 | func Test_skip(t *testing.T) { 21 | for _, testCase := range testCases { 22 | valType := reflect.TypeOf(testCase.ptr).Elem() 23 | for _, input := range testCase.inputs { 24 | t.Run(input, func(t *testing.T) { 25 | should := require.New(t) 26 | ptrVal := reflect.New(valType) 27 | stdErr := json.Unmarshal([]byte(input), ptrVal.Interface()) 28 | iter := jsoniter.ParseString(jsoniter.ConfigDefault, input) 29 | iter.Skip() 30 | iter.ReadNil() // trigger looking forward 31 | err := iter.Error 32 | if err == io.EOF { 33 | err = nil 34 | } else { 35 | err = errors.New("remaining bytes") 36 | } 37 | if stdErr == nil { 38 | should.Nil(err) 39 | } else { 40 | should.NotNil(err) 41 | } 42 | }) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /skip_tests/string_test.go: -------------------------------------------------------------------------------- 1 | package skip_tests 2 | 3 | func init() { 4 | testCases = append(testCases, testCase{ 5 | ptr: (*string)(nil), 6 | inputs: []string{ 7 | `""`, // valid 8 | `"hello"`, // valid 9 | `"`, // invalid 10 | `"\"`, // invalid 11 | `"\x00"`, // invalid 12 | "\"\x00\"", // invalid 13 | "\"\t\"", // invalid 14 | `"\t"`, // valid 15 | }, 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /skip_tests/struct_test.go: -------------------------------------------------------------------------------- 1 | package skip_tests 2 | 3 | func init() { 4 | testCases = append(testCases, testCase{ 5 | ptr: (*struct{})(nil), 6 | inputs: []string{ 7 | `{}`, // valid 8 | `{"hello":"world"}`, // valid 9 | `{hello:"world"}`, // invalid 10 | `{"hello:"world"}`, // invalid 11 | `{"hello","world"}`, // invalid 12 | `{"hello":{}`, // invalid 13 | `{"hello":{}}`, // valid 14 | `{"hello":{}}}`, // invalid 15 | `{"hello": { "hello": 1}}`, // valid 16 | `{abc}`, // invalid 17 | }, 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /stream_float.go: -------------------------------------------------------------------------------- 1 | package jsoniter 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "strconv" 7 | ) 8 | 9 | var pow10 []uint64 10 | 11 | func init() { 12 | pow10 = []uint64{1, 10, 100, 1000, 10000, 100000, 1000000} 13 | } 14 | 15 | // WriteFloat32 write float32 to stream 16 | func (stream *Stream) WriteFloat32(val float32) { 17 | if math.IsInf(float64(val), 0) || math.IsNaN(float64(val)) { 18 | stream.Error = fmt.Errorf("unsupported value: %f", val) 19 | return 20 | } 21 | abs := math.Abs(float64(val)) 22 | fmt := byte('f') 23 | // Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right. 24 | if abs != 0 { 25 | if float32(abs) < 1e-6 || float32(abs) >= 1e21 { 26 | fmt = 'e' 27 | } 28 | } 29 | stream.buf = strconv.AppendFloat(stream.buf, float64(val), fmt, -1, 32) 30 | if fmt == 'e' { 31 | // clean up e-09 to e-9 32 | n := len(stream.buf) 33 | if n >= 4 && stream.buf[n-4] == 'e' && stream.buf[n-3] == '-' && stream.buf[n-2] == '0' { 34 | stream.buf[n-2] = stream.buf[n-1] 35 | stream.buf = stream.buf[:n-1] 36 | } 37 | } 38 | } 39 | 40 | // WriteFloat32Lossy write float32 to stream with ONLY 6 digits precision although much much faster 41 | func (stream *Stream) WriteFloat32Lossy(val float32) { 42 | if math.IsInf(float64(val), 0) || math.IsNaN(float64(val)) { 43 | stream.Error = fmt.Errorf("unsupported value: %f", val) 44 | return 45 | } 46 | if val < 0 { 47 | stream.writeByte('-') 48 | val = -val 49 | } 50 | if val > 0x4ffffff { 51 | stream.WriteFloat32(val) 52 | return 53 | } 54 | precision := 6 55 | exp := uint64(1000000) // 6 56 | lval := uint64(float64(val)*float64(exp) + 0.5) 57 | stream.WriteUint64(lval / exp) 58 | fval := lval % exp 59 | if fval == 0 { 60 | return 61 | } 62 | stream.writeByte('.') 63 | for p := precision - 1; p > 0 && fval < pow10[p]; p-- { 64 | stream.writeByte('0') 65 | } 66 | stream.WriteUint64(fval) 67 | for stream.buf[len(stream.buf)-1] == '0' { 68 | stream.buf = stream.buf[:len(stream.buf)-1] 69 | } 70 | } 71 | 72 | // WriteFloat64 write float64 to stream 73 | func (stream *Stream) WriteFloat64(val float64) { 74 | if math.IsInf(val, 0) || math.IsNaN(val) { 75 | stream.Error = fmt.Errorf("unsupported value: %f", val) 76 | return 77 | } 78 | abs := math.Abs(val) 79 | fmt := byte('f') 80 | // Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right. 81 | if abs != 0 { 82 | if abs < 1e-6 || abs >= 1e21 { 83 | fmt = 'e' 84 | } 85 | } 86 | stream.buf = strconv.AppendFloat(stream.buf, float64(val), fmt, -1, 64) 87 | if fmt == 'e' { 88 | // clean up e-09 to e-9 89 | n := len(stream.buf) 90 | if n >= 4 && stream.buf[n-4] == 'e' && stream.buf[n-3] == '-' && stream.buf[n-2] == '0' { 91 | stream.buf[n-2] = stream.buf[n-1] 92 | stream.buf = stream.buf[:n-1] 93 | } 94 | } 95 | } 96 | 97 | // WriteFloat64Lossy write float64 to stream with ONLY 6 digits precision although much much faster 98 | func (stream *Stream) WriteFloat64Lossy(val float64) { 99 | if math.IsInf(val, 0) || math.IsNaN(val) { 100 | stream.Error = fmt.Errorf("unsupported value: %f", val) 101 | return 102 | } 103 | if val < 0 { 104 | stream.writeByte('-') 105 | val = -val 106 | } 107 | if val > 0x4ffffff { 108 | stream.WriteFloat64(val) 109 | return 110 | } 111 | precision := 6 112 | exp := uint64(1000000) // 6 113 | lval := uint64(val*float64(exp) + 0.5) 114 | stream.WriteUint64(lval / exp) 115 | fval := lval % exp 116 | if fval == 0 { 117 | return 118 | } 119 | stream.writeByte('.') 120 | for p := precision - 1; p > 0 && fval < pow10[p]; p-- { 121 | stream.writeByte('0') 122 | } 123 | stream.WriteUint64(fval) 124 | for stream.buf[len(stream.buf)-1] == '0' { 125 | stream.buf = stream.buf[:len(stream.buf)-1] 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /stream_int.go: -------------------------------------------------------------------------------- 1 | package jsoniter 2 | 3 | var digits []uint32 4 | 5 | func init() { 6 | digits = make([]uint32, 1000) 7 | for i := uint32(0); i < 1000; i++ { 8 | digits[i] = (((i / 100) + '0') << 16) + ((((i / 10) % 10) + '0') << 8) + i%10 + '0' 9 | if i < 10 { 10 | digits[i] += 2 << 24 11 | } else if i < 100 { 12 | digits[i] += 1 << 24 13 | } 14 | } 15 | } 16 | 17 | func writeFirstBuf(space []byte, v uint32) []byte { 18 | start := v >> 24 19 | if start == 0 { 20 | space = append(space, byte(v>>16), byte(v>>8)) 21 | } else if start == 1 { 22 | space = append(space, byte(v>>8)) 23 | } 24 | space = append(space, byte(v)) 25 | return space 26 | } 27 | 28 | func writeBuf(buf []byte, v uint32) []byte { 29 | return append(buf, byte(v>>16), byte(v>>8), byte(v)) 30 | } 31 | 32 | // WriteUint8 write uint8 to stream 33 | func (stream *Stream) WriteUint8(val uint8) { 34 | stream.buf = writeFirstBuf(stream.buf, digits[val]) 35 | } 36 | 37 | // WriteInt8 write int8 to stream 38 | func (stream *Stream) WriteInt8(nval int8) { 39 | var val uint8 40 | if nval < 0 { 41 | val = uint8(-nval) 42 | stream.buf = append(stream.buf, '-') 43 | } else { 44 | val = uint8(nval) 45 | } 46 | stream.buf = writeFirstBuf(stream.buf, digits[val]) 47 | } 48 | 49 | // WriteUint16 write uint16 to stream 50 | func (stream *Stream) WriteUint16(val uint16) { 51 | q1 := val / 1000 52 | if q1 == 0 { 53 | stream.buf = writeFirstBuf(stream.buf, digits[val]) 54 | return 55 | } 56 | r1 := val - q1*1000 57 | stream.buf = writeFirstBuf(stream.buf, digits[q1]) 58 | stream.buf = writeBuf(stream.buf, digits[r1]) 59 | return 60 | } 61 | 62 | // WriteInt16 write int16 to stream 63 | func (stream *Stream) WriteInt16(nval int16) { 64 | var val uint16 65 | if nval < 0 { 66 | val = uint16(-nval) 67 | stream.buf = append(stream.buf, '-') 68 | } else { 69 | val = uint16(nval) 70 | } 71 | stream.WriteUint16(val) 72 | } 73 | 74 | // WriteUint32 write uint32 to stream 75 | func (stream *Stream) WriteUint32(val uint32) { 76 | q1 := val / 1000 77 | if q1 == 0 { 78 | stream.buf = writeFirstBuf(stream.buf, digits[val]) 79 | return 80 | } 81 | r1 := val - q1*1000 82 | q2 := q1 / 1000 83 | if q2 == 0 { 84 | stream.buf = writeFirstBuf(stream.buf, digits[q1]) 85 | stream.buf = writeBuf(stream.buf, digits[r1]) 86 | return 87 | } 88 | r2 := q1 - q2*1000 89 | q3 := q2 / 1000 90 | if q3 == 0 { 91 | stream.buf = writeFirstBuf(stream.buf, digits[q2]) 92 | } else { 93 | r3 := q2 - q3*1000 94 | stream.buf = append(stream.buf, byte(q3+'0')) 95 | stream.buf = writeBuf(stream.buf, digits[r3]) 96 | } 97 | stream.buf = writeBuf(stream.buf, digits[r2]) 98 | stream.buf = writeBuf(stream.buf, digits[r1]) 99 | } 100 | 101 | // WriteInt32 write int32 to stream 102 | func (stream *Stream) WriteInt32(nval int32) { 103 | var val uint32 104 | if nval < 0 { 105 | val = uint32(-nval) 106 | stream.buf = append(stream.buf, '-') 107 | } else { 108 | val = uint32(nval) 109 | } 110 | stream.WriteUint32(val) 111 | } 112 | 113 | // WriteUint64 write uint64 to stream 114 | func (stream *Stream) WriteUint64(val uint64) { 115 | q1 := val / 1000 116 | if q1 == 0 { 117 | stream.buf = writeFirstBuf(stream.buf, digits[val]) 118 | return 119 | } 120 | r1 := val - q1*1000 121 | q2 := q1 / 1000 122 | if q2 == 0 { 123 | stream.buf = writeFirstBuf(stream.buf, digits[q1]) 124 | stream.buf = writeBuf(stream.buf, digits[r1]) 125 | return 126 | } 127 | r2 := q1 - q2*1000 128 | q3 := q2 / 1000 129 | if q3 == 0 { 130 | stream.buf = writeFirstBuf(stream.buf, digits[q2]) 131 | stream.buf = writeBuf(stream.buf, digits[r2]) 132 | stream.buf = writeBuf(stream.buf, digits[r1]) 133 | return 134 | } 135 | r3 := q2 - q3*1000 136 | q4 := q3 / 1000 137 | if q4 == 0 { 138 | stream.buf = writeFirstBuf(stream.buf, digits[q3]) 139 | stream.buf = writeBuf(stream.buf, digits[r3]) 140 | stream.buf = writeBuf(stream.buf, digits[r2]) 141 | stream.buf = writeBuf(stream.buf, digits[r1]) 142 | return 143 | } 144 | r4 := q3 - q4*1000 145 | q5 := q4 / 1000 146 | if q5 == 0 { 147 | stream.buf = writeFirstBuf(stream.buf, digits[q4]) 148 | stream.buf = writeBuf(stream.buf, digits[r4]) 149 | stream.buf = writeBuf(stream.buf, digits[r3]) 150 | stream.buf = writeBuf(stream.buf, digits[r2]) 151 | stream.buf = writeBuf(stream.buf, digits[r1]) 152 | return 153 | } 154 | r5 := q4 - q5*1000 155 | q6 := q5 / 1000 156 | if q6 == 0 { 157 | stream.buf = writeFirstBuf(stream.buf, digits[q5]) 158 | } else { 159 | stream.buf = writeFirstBuf(stream.buf, digits[q6]) 160 | r6 := q5 - q6*1000 161 | stream.buf = writeBuf(stream.buf, digits[r6]) 162 | } 163 | stream.buf = writeBuf(stream.buf, digits[r5]) 164 | stream.buf = writeBuf(stream.buf, digits[r4]) 165 | stream.buf = writeBuf(stream.buf, digits[r3]) 166 | stream.buf = writeBuf(stream.buf, digits[r2]) 167 | stream.buf = writeBuf(stream.buf, digits[r1]) 168 | } 169 | 170 | // WriteInt64 write int64 to stream 171 | func (stream *Stream) WriteInt64(nval int64) { 172 | var val uint64 173 | if nval < 0 { 174 | val = uint64(-nval) 175 | stream.buf = append(stream.buf, '-') 176 | } else { 177 | val = uint64(nval) 178 | } 179 | stream.WriteUint64(val) 180 | } 181 | 182 | // WriteInt write int to stream 183 | func (stream *Stream) WriteInt(val int) { 184 | stream.WriteInt64(int64(val)) 185 | } 186 | 187 | // WriteUint write uint to stream 188 | func (stream *Stream) WriteUint(val uint) { 189 | stream.WriteUint64(uint64(val)) 190 | } 191 | -------------------------------------------------------------------------------- /stream_test.go: -------------------------------------------------------------------------------- 1 | package jsoniter 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func Test_writeByte_should_grow_buffer(t *testing.T) { 10 | should := require.New(t) 11 | stream := NewStream(ConfigDefault, nil, 1) 12 | stream.writeByte('1') 13 | should.Equal("1", string(stream.Buffer())) 14 | should.Equal(1, len(stream.buf)) 15 | stream.writeByte('2') 16 | should.Equal("12", string(stream.Buffer())) 17 | should.Equal(2, len(stream.buf)) 18 | stream.writeThreeBytes('3', '4', '5') 19 | should.Equal("12345", string(stream.Buffer())) 20 | } 21 | 22 | func Test_writeBytes_should_grow_buffer(t *testing.T) { 23 | should := require.New(t) 24 | stream := NewStream(ConfigDefault, nil, 1) 25 | stream.Write([]byte{'1', '2'}) 26 | should.Equal("12", string(stream.Buffer())) 27 | should.Equal(2, len(stream.buf)) 28 | stream.Write([]byte{'3', '4', '5', '6', '7'}) 29 | should.Equal("1234567", string(stream.Buffer())) 30 | should.Equal(7, len(stream.buf)) 31 | } 32 | 33 | func Test_writeIndention_should_grow_buffer(t *testing.T) { 34 | should := require.New(t) 35 | stream := NewStream(Config{IndentionStep: 2}.Froze(), nil, 1) 36 | stream.WriteVal([]int{1, 2, 3}) 37 | should.Equal("[\n 1,\n 2,\n 3\n]", string(stream.Buffer())) 38 | } 39 | 40 | func Test_writeRaw_should_grow_buffer(t *testing.T) { 41 | should := require.New(t) 42 | stream := NewStream(ConfigDefault, nil, 1) 43 | stream.WriteRaw("123") 44 | should.Nil(stream.Error) 45 | should.Equal("123", string(stream.Buffer())) 46 | } 47 | 48 | func Test_writeString_should_grow_buffer(t *testing.T) { 49 | should := require.New(t) 50 | stream := NewStream(ConfigDefault, nil, 0) 51 | stream.WriteString("123") 52 | should.Nil(stream.Error) 53 | should.Equal(`"123"`, string(stream.Buffer())) 54 | } 55 | 56 | type NopWriter struct { 57 | bufferSize int 58 | } 59 | 60 | func (w *NopWriter) Write(p []byte) (n int, err error) { 61 | w.bufferSize = cap(p) 62 | return len(p), nil 63 | } 64 | 65 | func Test_flush_buffer_should_stop_grow_buffer(t *testing.T) { 66 | // Stream an array of a zillion zeros. 67 | writer := new(NopWriter) 68 | stream := NewStream(ConfigDefault, writer, 512) 69 | stream.WriteArrayStart() 70 | for i := 0; i < 10000000; i++ { 71 | stream.WriteInt(0) 72 | stream.WriteMore() 73 | stream.Flush() 74 | } 75 | stream.WriteInt(0) 76 | stream.WriteArrayEnd() 77 | 78 | // Confirm that the buffer didn't have to grow. 79 | should := require.New(t) 80 | 81 | // 512 is the internal buffer size set in NewEncoder 82 | // 83 | // Flush is called after each array element, so only the first 8 bytes of it 84 | // is ever used, and it is never extended. Capacity remains 512. 85 | should.Equal(512, writer.bufferSize) 86 | } 87 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | echo "" > coverage.txt 5 | 6 | for d in $(go list ./... | grep -v vendor); do 7 | go test -coverprofile=profile.out -coverpkg=github.com/json-iterator/go $d 8 | if [ -f profile.out ]; then 9 | cat profile.out >> coverage.txt 10 | rm profile.out 11 | fi 12 | done 13 | -------------------------------------------------------------------------------- /type_tests/array_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | func init() { 4 | testCases = append(testCases, 5 | (*[4]bool)(nil), 6 | (*[4]byte)(nil), 7 | (*[4]float64)(nil), 8 | (*[4]int32)(nil), 9 | (*[4]map[int32]string)(nil), 10 | (*[4]map[string]string)(nil), 11 | (*[4]*bool)(nil), 12 | (*[4]*float64)(nil), 13 | (*[4]*int32)(nil), 14 | (*[4]*map[int32]string)(nil), 15 | (*[4]*map[string]string)(nil), 16 | (*[4]*[4]bool)(nil), 17 | (*[4]*[4]byte)(nil), 18 | (*[4]*[4]float64)(nil), 19 | (*[4]*[4]int32)(nil), 20 | (*[4]*[4]*string)(nil), 21 | (*[4]*[4]string)(nil), 22 | (*[4]*[4]uint8)(nil), 23 | (*[4]*string)(nil), 24 | (*[4]*struct { 25 | String string 26 | Int int32 27 | Float float64 28 | Struct struct { 29 | X string 30 | } 31 | Slice [4]string 32 | Map map[string]string 33 | })(nil), 34 | (*[4]*uint8)(nil), 35 | (*[4][4]bool)(nil), 36 | (*[4][4]byte)(nil), 37 | (*[4][4]float64)(nil), 38 | (*[4][4]int32)(nil), 39 | (*[4][4]*string)(nil), 40 | (*[4][4]string)(nil), 41 | (*[4][4]uint8)(nil), 42 | (*[4]string)(nil), 43 | (*[4]struct{})(nil), 44 | (*[4]structEmpty)(nil), 45 | (*[4]struct { 46 | F *string 47 | })(nil), 48 | (*[4]struct { 49 | String string 50 | Int int32 51 | Float float64 52 | Struct struct { 53 | X string 54 | } 55 | Slice [4]string 56 | Map map[string]string 57 | })(nil), 58 | (*[4]uint8)(nil), 59 | ) 60 | } 61 | 62 | type structEmpty struct{} 63 | type arrayAlis [4]stringAlias 64 | -------------------------------------------------------------------------------- /type_tests/builtin_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | func init() { 4 | testCases = append(testCases, 5 | (*bool)(nil), 6 | (*boolAlias)(nil), 7 | (*byte)(nil), 8 | (*byteAlias)(nil), 9 | (*float32)(nil), 10 | (*float32Alias)(nil), 11 | (*float64)(nil), 12 | (*float64Alias)(nil), 13 | (*int8)(nil), 14 | (*int8Alias)(nil), 15 | (*int16)(nil), 16 | (*int16Alias)(nil), 17 | (*int32)(nil), 18 | (*int32Alias)(nil), 19 | (*int64)(nil), 20 | (*int64Alias)(nil), 21 | (*string)(nil), 22 | (*stringAlias)(nil), 23 | (*uint8)(nil), 24 | (*uint8Alias)(nil), 25 | (*uint16)(nil), 26 | (*uint16Alias)(nil), 27 | (*uint32)(nil), 28 | (*uint32Alias)(nil), 29 | (*uintptr)(nil), 30 | (*uintptrAlias)(nil), 31 | (*struct { 32 | A int8Alias `json:"a"` 33 | B int16Alias `json:"stream"` 34 | C int32Alias `json:"c"` 35 | D int64Alias `json:"d"` 36 | E uintAlias `json:"e"` 37 | F uint16Alias `json:"f"` 38 | G uint32Alias `json:"g"` 39 | H uint64Alias `json:"h"` 40 | I float32Alias `json:"i"` 41 | J float64Alias `json:"j"` 42 | K stringAlias `json:"k"` 43 | L intAlias `json:"l"` 44 | M uintAlias `json:"m"` 45 | N boolAlias `json:"n"` 46 | O uintptrAlias `json:"o"` 47 | })(nil), 48 | ) 49 | } 50 | 51 | type boolAlias bool 52 | type byteAlias byte 53 | type float32Alias float32 54 | type float64Alias float64 55 | type ptrFloat64Alias *float64 56 | type int8Alias int8 57 | type int16Alias int16 58 | type int32Alias int32 59 | type ptrInt32Alias *int32 60 | type int64Alias int64 61 | type stringAlias string 62 | type ptrStringAlias *string 63 | type uint8Alias uint8 64 | type uint16Alias uint16 65 | type uint32Alias uint32 66 | type uintptrAlias uintptr 67 | type uintAlias uint 68 | type uint64Alias uint64 69 | type intAlias int 70 | -------------------------------------------------------------------------------- /type_tests/map_key_test.go: -------------------------------------------------------------------------------- 1 | // +build go1.15 2 | // remove these tests temporarily until https://github.com/golang/go/issues/38105 and 3 | // https://github.com/golang/go/issues/38940 is fixed 4 | 5 | package test 6 | 7 | import ( 8 | "encoding" 9 | "strings" 10 | ) 11 | 12 | func init() { 13 | testCases = append(testCases, 14 | (*map[stringKeyType]string)(nil), 15 | (*map[structKeyType]string)(nil), 16 | ) 17 | } 18 | 19 | type stringKeyType string 20 | 21 | func (k stringKeyType) MarshalText() ([]byte, error) { 22 | return []byte("MANUAL__" + k), nil 23 | } 24 | 25 | func (k *stringKeyType) UnmarshalText(text []byte) error { 26 | *k = stringKeyType(strings.TrimPrefix(string(text), "MANUAL__")) 27 | return nil 28 | } 29 | 30 | var _ encoding.TextMarshaler = stringKeyType("") 31 | var _ encoding.TextUnmarshaler = new(stringKeyType) 32 | 33 | type structKeyType struct { 34 | X string 35 | } 36 | 37 | func (k structKeyType) MarshalText() ([]byte, error) { 38 | return []byte("MANUAL__" + k.X), nil 39 | } 40 | 41 | func (k *structKeyType) UnmarshalText(text []byte) error { 42 | k.X = strings.TrimPrefix(string(text), "MANUAL__") 43 | return nil 44 | } 45 | 46 | var _ encoding.TextMarshaler = structKeyType{} 47 | var _ encoding.TextUnmarshaler = &structKeyType{} 48 | -------------------------------------------------------------------------------- /type_tests/map_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | func init() { 4 | testCases = append(testCases, 5 | (*map[int8]string)(nil), 6 | (*map[int16]string)(nil), 7 | (*map[int32]string)(nil), 8 | (*map[int64]string)(nil), 9 | (*map[string][4]string)(nil), 10 | (*map[string]bool)(nil), 11 | (*map[string]byte)(nil), 12 | (*map[string]float64)(nil), 13 | (*map[string]int32)(nil), 14 | (*map[string]map[string]string)(nil), 15 | (*map[string]*[4]string)(nil), 16 | (*map[string]*bool)(nil), 17 | (*map[string]*float64)(nil), 18 | (*map[string]*int32)(nil), 19 | (*map[string]*map[string]string)(nil), 20 | (*map[string]*[]string)(nil), 21 | (*map[string]*string)(nil), 22 | (*map[string]*structVarious)(nil), 23 | (*map[string]*uint8)(nil), 24 | (*map[string][]string)(nil), 25 | (*map[string]string)(nil), 26 | (*map[string]stringAlias)(nil), 27 | (*map[string]struct{})(nil), 28 | (*map[string]structEmpty)(nil), 29 | (*map[string]struct { 30 | F *string 31 | })(nil), 32 | (*map[string]struct { 33 | String string 34 | Int int32 35 | Float float64 36 | Struct struct { 37 | X string 38 | } 39 | Slice []string 40 | Map map[string]string 41 | })(nil), 42 | (*map[string]uint8)(nil), 43 | (*map[stringAlias]string)(nil), 44 | (*map[stringAlias]stringAlias)(nil), 45 | (*map[uint8]string)(nil), 46 | (*map[uint16]string)(nil), 47 | (*map[uint32]string)(nil), 48 | ) 49 | } 50 | 51 | type structVarious struct { 52 | String string 53 | Int int32 54 | Float float64 55 | Struct struct { 56 | X string 57 | } 58 | Slice []string 59 | Map map[string]string 60 | } 61 | -------------------------------------------------------------------------------- /type_tests/marshaler_string_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "bytes" 5 | "encoding/base64" 6 | "encoding/json" 7 | "strings" 8 | ) 9 | 10 | type StringMarshaler string 11 | 12 | func (m StringMarshaler) encode(str string) string { 13 | buf := bytes.Buffer{} 14 | b64 := base64.NewEncoder(base64.StdEncoding, &buf) 15 | if _, err := b64.Write([]byte(str)); err != nil { 16 | panic(err) 17 | } 18 | if err := b64.Close(); err != nil { 19 | panic(err) 20 | } 21 | return buf.String() 22 | } 23 | 24 | func (m StringMarshaler) decode(str string) string { 25 | if len(str) == 0 { 26 | return "" 27 | } 28 | b64 := base64.NewDecoder(base64.StdEncoding, strings.NewReader(str)) 29 | bs := make([]byte, len(str)) 30 | if n, err := b64.Read(bs); err != nil { 31 | panic(err) 32 | } else { 33 | bs = bs[:n] 34 | } 35 | return string(bs) 36 | } 37 | 38 | func (m StringMarshaler) MarshalJSON() ([]byte, error) { 39 | return []byte(`"MANUAL__` + m.encode(string(m)) + `"`), nil 40 | } 41 | 42 | func (m *StringMarshaler) UnmarshalJSON(text []byte) error { 43 | *m = StringMarshaler(m.decode(strings.TrimPrefix(strings.Trim(string(text), `"`), "MANUAL__"))) 44 | return nil 45 | } 46 | 47 | var _ json.Marshaler = *new(StringMarshaler) 48 | var _ json.Unmarshaler = new(StringMarshaler) 49 | 50 | func init() { 51 | testCases = append(testCases, (*StringMarshaler)(nil)) 52 | } 53 | -------------------------------------------------------------------------------- /type_tests/marshaler_struct_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "bytes" 5 | "encoding/base64" 6 | "encoding/json" 7 | "strings" 8 | ) 9 | 10 | type structMarshaler struct { 11 | X string 12 | } 13 | 14 | func (m structMarshaler) encode(str string) string { 15 | buf := bytes.Buffer{} 16 | b64 := base64.NewEncoder(base64.StdEncoding, &buf) 17 | if _, err := b64.Write([]byte(str)); err != nil { 18 | panic(err) 19 | } 20 | if err := b64.Close(); err != nil { 21 | panic(err) 22 | } 23 | return buf.String() 24 | } 25 | 26 | func (m structMarshaler) decode(str string) string { 27 | if len(str) == 0 { 28 | return "" 29 | } 30 | b64 := base64.NewDecoder(base64.StdEncoding, strings.NewReader(str)) 31 | bs := make([]byte, len(str)) 32 | if n, err := b64.Read(bs); err != nil { 33 | panic(err) 34 | } else { 35 | bs = bs[:n] 36 | } 37 | return string(bs) 38 | } 39 | 40 | func (m structMarshaler) MarshalJSON() ([]byte, error) { 41 | return []byte(`"MANUAL__` + m.encode(m.X) + `"`), nil 42 | } 43 | 44 | func (m *structMarshaler) UnmarshalJSON(text []byte) error { 45 | m.X = m.decode(strings.TrimPrefix(strings.Trim(string(text), `"`), "MANUAL__")) 46 | return nil 47 | } 48 | 49 | var _ json.Marshaler = structMarshaler{} 50 | var _ json.Unmarshaler = &structMarshaler{} 51 | 52 | type structMarshalerAlias structMarshaler 53 | 54 | func init() { 55 | testCases = append(testCases, 56 | (*structMarshaler)(nil), 57 | (*structMarshalerAlias)(nil), 58 | (*struct { 59 | S string 60 | M structMarshaler 61 | I int8 62 | })(nil), 63 | (*struct { 64 | S string 65 | M structMarshalerAlias 66 | I int8 67 | })(nil), 68 | ) 69 | } 70 | -------------------------------------------------------------------------------- /type_tests/slice_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | func init() { 4 | testCases = append(testCases, 5 | (*[][4]bool)(nil), 6 | (*[][4]byte)(nil), 7 | (*[][4]float64)(nil), 8 | (*[][4]int32)(nil), 9 | (*[][4]*string)(nil), 10 | (*[][4]string)(nil), 11 | (*[][4]uint8)(nil), 12 | (*[]bool)(nil), 13 | (*[]byte)(nil), 14 | (*[]float64)(nil), 15 | (*[]int32)(nil), 16 | (*[]int64)(nil), 17 | (*[]map[int32]string)(nil), 18 | (*[]map[string]string)(nil), 19 | (*[4]*[4]bool)(nil), 20 | (*[4]*[4]byte)(nil), 21 | (*[4]*[4]float64)(nil), 22 | (*[4]*[4]int32)(nil), 23 | (*[4]*[4]*string)(nil), 24 | (*[4]*[4]string)(nil), 25 | (*[4]*[4]uint8)(nil), 26 | (*[]*bool)(nil), 27 | (*[]*float64)(nil), 28 | (*[]*int32)(nil), 29 | (*[]*map[int32]string)(nil), 30 | (*[]*map[string]string)(nil), 31 | (*[]*[]bool)(nil), 32 | (*[]*[]byte)(nil), 33 | (*[]*[]float64)(nil), 34 | (*[]*[]int32)(nil), 35 | (*[]*[]*string)(nil), 36 | (*[]*[]string)(nil), 37 | (*[]*[]uint8)(nil), 38 | (*[]*string)(nil), 39 | (*[]*struct { 40 | String string 41 | Int int32 42 | Float float64 43 | Struct struct { 44 | X string 45 | } 46 | Slice []string 47 | Map map[string]string 48 | })(nil), 49 | (*[]*uint8)(nil), 50 | (*[][]bool)(nil), 51 | (*[][]byte)(nil), 52 | (*[][]float64)(nil), 53 | (*[][]int32)(nil), 54 | (*[][]*string)(nil), 55 | (*[][]string)(nil), 56 | (*[][]uint8)(nil), 57 | (*[]string)(nil), 58 | (*[]struct{})(nil), 59 | (*[]structEmpty)(nil), 60 | (*[]struct { 61 | F *string 62 | })(nil), 63 | (*[]struct { 64 | String string 65 | Int int32 66 | Float float64 67 | Struct struct { 68 | X string 69 | } 70 | Slice []string 71 | Map map[string]string 72 | })(nil), 73 | (*[]uint8)(nil), 74 | (*[]jsonMarshaler)(nil), 75 | (*[]jsonMarshalerMap)(nil), 76 | (*[]textMarshaler)(nil), 77 | (*[]textMarshalerMap)(nil), 78 | ) 79 | } 80 | 81 | type jsonMarshaler struct { 82 | Id string `json:"id,omitempty" db:"id"` 83 | } 84 | 85 | func (p *jsonMarshaler) MarshalJSON() ([]byte, error) { 86 | return []byte(`{}`), nil 87 | } 88 | 89 | func (p *jsonMarshaler) UnmarshalJSON(input []byte) error { 90 | p.Id = "hello" 91 | return nil 92 | } 93 | 94 | type jsonMarshalerMap map[int]int 95 | 96 | func (p *jsonMarshalerMap) MarshalJSON() ([]byte, error) { 97 | return []byte(`{}`), nil 98 | } 99 | 100 | func (p *jsonMarshalerMap) UnmarshalJSON(input []byte) error { 101 | return nil 102 | } 103 | 104 | type textMarshaler struct { 105 | Id string `json:"id,omitempty" db:"id"` 106 | } 107 | 108 | func (p *textMarshaler) MarshalText() ([]byte, error) { 109 | return []byte(`{}`), nil 110 | } 111 | 112 | func (p *textMarshaler) UnmarshalText(input []byte) error { 113 | p.Id = "hello" 114 | return nil 115 | } 116 | 117 | type textMarshalerMap map[int]int 118 | 119 | func (p *textMarshalerMap) MarshalText() ([]byte, error) { 120 | return []byte(`{}`), nil 121 | } 122 | 123 | func (p *textMarshalerMap) UnmarshalText(input []byte) error { 124 | return nil 125 | } 126 | -------------------------------------------------------------------------------- /type_tests/struct_embedded_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | func init() { 4 | testCases = append(testCases, 5 | (*struct { 6 | EmbeddedFloat64 7 | })(nil), 8 | (*struct { 9 | EmbeddedInt32 10 | })(nil), 11 | (*struct { 12 | F1 float64 13 | StringMarshaler 14 | F2 int32 15 | })(nil), 16 | (*struct { 17 | EmbeddedMapStringString 18 | })(nil), 19 | (*struct { 20 | *EmbeddedFloat64 21 | })(nil), 22 | (*struct { 23 | *EmbeddedInt32 24 | })(nil), 25 | (*struct { 26 | *EmbeddedMapStringString 27 | })(nil), 28 | (*struct { 29 | *EmbeddedSliceString 30 | })(nil), 31 | (*struct { 32 | *EmbeddedString 33 | })(nil), 34 | (*struct { 35 | *EmbeddedStruct 36 | })(nil), 37 | (*struct { 38 | EmbeddedSliceString 39 | })(nil), 40 | (*struct { 41 | EmbeddedString 42 | })(nil), 43 | (*struct { 44 | EmbeddedString `json:"othername"` 45 | })(nil), 46 | (*struct { 47 | EmbeddedStruct 48 | })(nil), 49 | (*struct { 50 | F1 float64 51 | StringTextMarshaler 52 | F2 int32 53 | })(nil), 54 | (*OverlapDifferentLevels)(nil), 55 | (*IgnoreDeeperLevel)(nil), 56 | (*SameLevel1BothTagged)(nil), 57 | (*SameLevel1NoTags)(nil), 58 | (*SameLevel1Tagged)(nil), 59 | (*SameLevel2BothTagged)(nil), 60 | (*SameLevel2NoTags)(nil), 61 | (*SameLevel2Tagged)(nil), 62 | (*EmbeddedPtr)(nil), 63 | (*UnnamedLiteral)(nil), 64 | ) 65 | } 66 | 67 | type EmbeddedFloat64 float64 68 | type EmbeddedInt32 int32 69 | type EmbeddedMapStringString map[string]string 70 | type EmbeddedSliceString []string 71 | type EmbeddedString string 72 | type EmbeddedStruct struct { 73 | String string 74 | Int int32 75 | Float float64 76 | Struct struct { 77 | X string 78 | } 79 | Slice []string 80 | Map map[string]string 81 | } 82 | 83 | type OverlapDifferentLevelsE1 struct { 84 | F1 int32 85 | } 86 | 87 | type OverlapDifferentLevelsE2 struct { 88 | F2 string 89 | } 90 | 91 | type OverlapDifferentLevels struct { 92 | OverlapDifferentLevelsE1 93 | OverlapDifferentLevelsE2 94 | F1 string 95 | } 96 | 97 | type IgnoreDeeperLevelDoubleEmbedded struct { 98 | F1 int32 `json:"F1"` 99 | } 100 | 101 | type IgnoreDeeperLevelE1 struct { 102 | IgnoreDeeperLevelDoubleEmbedded 103 | F1 int32 104 | } 105 | 106 | type IgnoreDeeperLevelE2 struct { 107 | F1 int32 `json:"F1"` 108 | IgnoreDeeperLevelDoubleEmbedded 109 | } 110 | 111 | type IgnoreDeeperLevel struct { 112 | IgnoreDeeperLevelE1 113 | IgnoreDeeperLevelE2 114 | } 115 | 116 | type SameLevel1BothTaggedE1 struct { 117 | F1 int32 `json:"F1"` 118 | } 119 | 120 | type SameLevel1BothTaggedE2 struct { 121 | F1 int32 `json:"F1"` 122 | } 123 | 124 | type SameLevel1BothTagged struct { 125 | SameLevel1BothTaggedE1 126 | SameLevel1BothTaggedE2 127 | } 128 | 129 | type SameLevel1NoTagsE1 struct { 130 | F1 int32 131 | } 132 | 133 | type SameLevel1NoTagsE2 struct { 134 | F1 int32 135 | } 136 | 137 | type SameLevel1NoTags struct { 138 | SameLevel1NoTagsE1 139 | SameLevel1NoTagsE2 140 | } 141 | 142 | type SameLevel1TaggedE1 struct { 143 | F1 int32 144 | } 145 | 146 | type SameLevel1TaggedE2 struct { 147 | F1 int32 `json:"F1"` 148 | } 149 | 150 | type SameLevel1Tagged struct { 151 | SameLevel1TaggedE1 152 | SameLevel1TaggedE2 153 | } 154 | 155 | type SameLevel2BothTaggedDE1 struct { 156 | F1 int32 `json:"F1"` 157 | } 158 | 159 | type SameLevel2BothTaggedE1 struct { 160 | SameLevel2BothTaggedDE1 161 | } 162 | 163 | // DoubleEmbedded2 TEST ONLY 164 | type SameLevel2BothTaggedDE2 struct { 165 | F1 int32 `json:"F1"` 166 | } 167 | 168 | // Embedded2 TEST ONLY 169 | type SameLevel2BothTaggedE2 struct { 170 | SameLevel2BothTaggedDE2 171 | } 172 | 173 | type SameLevel2BothTagged struct { 174 | SameLevel2BothTaggedE1 175 | SameLevel2BothTaggedE2 176 | } 177 | 178 | type SameLevel2NoTagsDE1 struct { 179 | F1 int32 180 | } 181 | 182 | type SameLevel2NoTagsE1 struct { 183 | SameLevel2NoTagsDE1 184 | } 185 | 186 | type SameLevel2NoTagsDE2 struct { 187 | F1 int32 188 | } 189 | 190 | type SameLevel2NoTagsE2 struct { 191 | SameLevel2NoTagsDE2 192 | } 193 | 194 | type SameLevel2NoTags struct { 195 | SameLevel2NoTagsE1 196 | SameLevel2NoTagsE2 197 | } 198 | 199 | // DoubleEmbedded1 TEST ONLY 200 | type SameLevel2TaggedDE1 struct { 201 | F1 int32 202 | } 203 | 204 | // Embedded1 TEST ONLY 205 | type SameLevel2TaggedE1 struct { 206 | SameLevel2TaggedDE1 207 | } 208 | 209 | // DoubleEmbedded2 TEST ONLY 210 | type SameLevel2TaggedDE2 struct { 211 | F1 int32 `json:"F1"` 212 | } 213 | 214 | // Embedded2 TEST ONLY 215 | type SameLevel2TaggedE2 struct { 216 | SameLevel2TaggedDE2 217 | } 218 | 219 | type SameLevel2Tagged struct { 220 | SameLevel2TaggedE1 221 | SameLevel2TaggedE2 222 | } 223 | 224 | type EmbeddedPtrO1 struct { 225 | O1F string 226 | } 227 | 228 | type EmbeddedPtrOption struct { 229 | O1 *EmbeddedPtrO1 230 | } 231 | 232 | type EmbeddedPtr struct { 233 | EmbeddedPtrOption `json:","` 234 | } 235 | 236 | type UnnamedLiteral struct { 237 | _ struct{} 238 | } 239 | -------------------------------------------------------------------------------- /type_tests/struct_field_case_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | func init() { 4 | testCases = append(testCases, 5 | (*struct { 6 | Upper bool `json:"M"` 7 | Lower bool `json:"m"` 8 | })(nil), 9 | ) 10 | asymmetricTestCases = append(asymmetricTestCases, [][2]interface{}{ 11 | { 12 | (*struct { 13 | Field string 14 | })(nil), 15 | (*struct { 16 | FIELD string 17 | })(nil), 18 | }, 19 | { 20 | (*struct { 21 | F1 string 22 | F2 string 23 | F3 string 24 | })(nil), 25 | (*struct { 26 | F1 string 27 | })(nil), 28 | }, 29 | }...) 30 | } 31 | -------------------------------------------------------------------------------- /type_tests/text_marshaler_string_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "bytes" 5 | "encoding" 6 | "encoding/base64" 7 | "strings" 8 | ) 9 | 10 | func init() { 11 | testCases = append(testCases, 12 | (*StringTextMarshaler)(nil), 13 | ) 14 | } 15 | 16 | // StringTextMarshaler TEST ONLY 17 | type StringTextMarshaler string 18 | 19 | func (m StringTextMarshaler) encode(str string) string { 20 | buf := bytes.Buffer{} 21 | b64 := base64.NewEncoder(base64.StdEncoding, &buf) 22 | if _, err := b64.Write([]byte(str)); err != nil { 23 | panic(err) 24 | } 25 | if err := b64.Close(); err != nil { 26 | panic(err) 27 | } 28 | return buf.String() 29 | } 30 | 31 | func (m StringTextMarshaler) decode(str string) string { 32 | if len(str) == 0 { 33 | return "" 34 | } 35 | b64 := base64.NewDecoder(base64.StdEncoding, strings.NewReader(str)) 36 | bs := make([]byte, len(str)) 37 | if n, err := b64.Read(bs); err != nil { 38 | panic(err) 39 | } else { 40 | bs = bs[:n] 41 | } 42 | return string(bs) 43 | } 44 | 45 | // MarshalText TEST ONLY 46 | func (m StringTextMarshaler) MarshalText() ([]byte, error) { 47 | return []byte(`MANUAL__` + m.encode(string(m))), nil 48 | } 49 | 50 | // UnmarshalText TEST ONLY 51 | func (m *StringTextMarshaler) UnmarshalText(text []byte) error { 52 | *m = StringTextMarshaler(m.decode(strings.TrimPrefix(string(text), "MANUAL__"))) 53 | return nil 54 | } 55 | 56 | var _ encoding.TextMarshaler = *new(StringTextMarshaler) 57 | var _ encoding.TextUnmarshaler = new(StringTextMarshaler) 58 | -------------------------------------------------------------------------------- /type_tests/text_marshaler_struct_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "bytes" 5 | "encoding" 6 | "encoding/base64" 7 | "strings" 8 | ) 9 | 10 | func init() { 11 | testCases = append(testCases, 12 | (*structTextMarshaler)(nil), 13 | (*structTextMarshalerAlias)(nil), 14 | (*struct { 15 | S string 16 | M structTextMarshaler 17 | I int8 18 | })(nil), 19 | (*struct { 20 | S string 21 | M structTextMarshalerAlias 22 | I int8 23 | })(nil), 24 | ) 25 | } 26 | 27 | type structTextMarshaler struct { 28 | X string 29 | } 30 | 31 | func (m structTextMarshaler) encode(str string) string { 32 | buf := bytes.Buffer{} 33 | b64 := base64.NewEncoder(base64.StdEncoding, &buf) 34 | if _, err := b64.Write([]byte(str)); err != nil { 35 | panic(err) 36 | } 37 | if err := b64.Close(); err != nil { 38 | panic(err) 39 | } 40 | return buf.String() 41 | } 42 | 43 | func (m structTextMarshaler) decode(str string) string { 44 | if len(str) == 0 { 45 | return "" 46 | } 47 | b64 := base64.NewDecoder(base64.StdEncoding, strings.NewReader(str)) 48 | bs := make([]byte, len(str)) 49 | if n, err := b64.Read(bs); err != nil { 50 | panic(err) 51 | } else { 52 | bs = bs[:n] 53 | } 54 | return string(bs) 55 | } 56 | 57 | func (m structTextMarshaler) MarshalText() ([]byte, error) { 58 | return []byte(`MANUAL__` + m.encode(m.X)), nil 59 | } 60 | 61 | func (m *structTextMarshaler) UnmarshalText(text []byte) error { 62 | m.X = m.decode(strings.TrimPrefix(string(text), "MANUAL__")) 63 | return nil 64 | } 65 | 66 | var _ encoding.TextMarshaler = structTextMarshaler{} 67 | var _ encoding.TextUnmarshaler = &structTextMarshaler{} 68 | 69 | type structTextMarshalerAlias structTextMarshaler 70 | -------------------------------------------------------------------------------- /type_tests/type_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/davecgh/go-spew/spew" 8 | "github.com/google/gofuzz" 9 | "github.com/json-iterator/go" 10 | "reflect" 11 | "strings" 12 | "testing" 13 | ) 14 | 15 | var testCases []interface{} 16 | var asymmetricTestCases [][2]interface{} 17 | 18 | type selectedSymmetricCase struct { 19 | testCase interface{} 20 | } 21 | 22 | func Test_symmetric(t *testing.T) { 23 | for _, testCase := range testCases { 24 | selectedSymmetricCase, found := testCase.(selectedSymmetricCase) 25 | if found { 26 | testCases = []interface{}{selectedSymmetricCase.testCase} 27 | break 28 | } 29 | } 30 | for _, testCase := range testCases { 31 | valType := reflect.TypeOf(testCase).Elem() 32 | t.Run(valType.String(), func(t *testing.T) { 33 | fz := fuzz.New().MaxDepth(10).NilChance(0.3) 34 | for i := 0; i < 100; i++ { 35 | beforePtrVal := reflect.New(valType) 36 | beforePtr := beforePtrVal.Interface() 37 | fz.Fuzz(beforePtr) 38 | before := beforePtrVal.Elem().Interface() 39 | 40 | jbStd, err := json.Marshal(before) 41 | if err != nil { 42 | t.Fatalf("failed to marshal with stdlib: %v", err) 43 | } 44 | if len(strings.TrimSpace(string(jbStd))) == 0 { 45 | t.Fatal("stdlib marshal produced empty result and no error") 46 | } 47 | jbIter, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(before) 48 | if err != nil { 49 | t.Fatalf("failed to marshal with jsoniter: %v", err) 50 | } 51 | if len(strings.TrimSpace(string(jbIter))) == 0 { 52 | t.Fatal("jsoniter marshal produced empty result and no error") 53 | } 54 | if string(jbStd) != string(jbIter) { 55 | t.Fatalf("marshal expected:\n %s\ngot:\n %s\nobj:\n %s", 56 | indent(jbStd, " "), indent(jbIter, " "), dump(before)) 57 | } 58 | 59 | afterStdPtrVal := reflect.New(valType) 60 | afterStdPtr := afterStdPtrVal.Interface() 61 | err = json.Unmarshal(jbIter, afterStdPtr) 62 | if err != nil { 63 | t.Fatalf("failed to unmarshal with stdlib: %v\nvia:\n %s", 64 | err, indent(jbIter, " ")) 65 | } 66 | afterIterPtrVal := reflect.New(valType) 67 | afterIterPtr := afterIterPtrVal.Interface() 68 | err = jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(jbIter, afterIterPtr) 69 | if err != nil { 70 | t.Fatalf("failed to unmarshal with jsoniter: %v\nvia:\n %s", 71 | err, indent(jbIter, " ")) 72 | } 73 | afterStd := afterStdPtrVal.Elem().Interface() 74 | afterIter := afterIterPtrVal.Elem().Interface() 75 | if fingerprint(afterStd) != fingerprint(afterIter) { 76 | t.Fatalf("unmarshal expected:\n %s\ngot:\n %s\nvia:\n %s", 77 | dump(afterStd), dump(afterIter), indent(jbIter, " ")) 78 | } 79 | } 80 | }) 81 | } 82 | } 83 | 84 | func Test_asymmetric(t *testing.T) { 85 | for _, testCase := range asymmetricTestCases { 86 | fromType := reflect.TypeOf(testCase[0]).Elem() 87 | toType := reflect.TypeOf(testCase[1]).Elem() 88 | fz := fuzz.New().MaxDepth(10).NilChance(0.3) 89 | for i := 0; i < 100; i++ { 90 | beforePtrVal := reflect.New(fromType) 91 | beforePtr := beforePtrVal.Interface() 92 | fz.Fuzz(beforePtr) 93 | before := beforePtrVal.Elem().Interface() 94 | 95 | jbStd, err := json.Marshal(before) 96 | if err != nil { 97 | t.Fatalf("failed to marshal with stdlib: %v", err) 98 | } 99 | if len(strings.TrimSpace(string(jbStd))) == 0 { 100 | t.Fatal("stdlib marshal produced empty result and no error") 101 | } 102 | jbIter, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(before) 103 | if err != nil { 104 | t.Fatalf("failed to marshal with jsoniter: %v", err) 105 | } 106 | if len(strings.TrimSpace(string(jbIter))) == 0 { 107 | t.Fatal("jsoniter marshal produced empty result and no error") 108 | } 109 | if string(jbStd) != string(jbIter) { 110 | t.Fatalf("marshal expected:\n %s\ngot:\n %s\nobj:\n %s", 111 | indent(jbStd, " "), indent(jbIter, " "), dump(before)) 112 | } 113 | 114 | afterStdPtrVal := reflect.New(toType) 115 | afterStdPtr := afterStdPtrVal.Interface() 116 | err = json.Unmarshal(jbIter, afterStdPtr) 117 | if err != nil { 118 | t.Fatalf("failed to unmarshal with stdlib: %v\nvia:\n %s", 119 | err, indent(jbIter, " ")) 120 | } 121 | afterIterPtrVal := reflect.New(toType) 122 | afterIterPtr := afterIterPtrVal.Interface() 123 | err = jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(jbIter, afterIterPtr) 124 | if err != nil { 125 | t.Fatalf("failed to unmarshal with jsoniter: %v\nvia:\n %s", 126 | err, indent(jbIter, " ")) 127 | } 128 | afterStd := afterStdPtrVal.Elem().Interface() 129 | afterIter := afterIterPtrVal.Elem().Interface() 130 | if fingerprint(afterStd) != fingerprint(afterIter) { 131 | t.Fatalf("unmarshal expected:\n %s\ngot:\n %s\nvia:\n %s", 132 | dump(afterStd), dump(afterIter), indent(jbIter, " ")) 133 | } 134 | } 135 | } 136 | } 137 | 138 | const indentStr = "> " 139 | 140 | func fingerprint(obj interface{}) string { 141 | c := spew.ConfigState{ 142 | SortKeys: true, 143 | SpewKeys: true, 144 | } 145 | return c.Sprintf("%v", obj) 146 | } 147 | 148 | func dump(obj interface{}) string { 149 | cfg := spew.ConfigState{ 150 | Indent: indentStr, 151 | } 152 | return cfg.Sdump(obj) 153 | } 154 | 155 | func indent(src []byte, prefix string) string { 156 | var buf bytes.Buffer 157 | err := json.Indent(&buf, src, prefix, indentStr) 158 | if err != nil { 159 | return fmt.Sprintf("!!! %v", err) 160 | } 161 | return buf.String() 162 | } 163 | -------------------------------------------------------------------------------- /value_tests/array_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | func init() { 4 | two := float64(2) 5 | marshalCases = append(marshalCases, 6 | [1]*float64{nil}, 7 | [1]*float64{&two}, 8 | [2]*float64{}, 9 | ) 10 | unmarshalCases = append(unmarshalCases, unmarshalCase{ 11 | ptr: (*[0]int)(nil), 12 | input: `[1]`, 13 | }, unmarshalCase{ 14 | ptr: (*[1]int)(nil), 15 | input: `[2]`, 16 | }, unmarshalCase{ 17 | ptr: (*[1]int)(nil), 18 | input: `[]`, 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /value_tests/bool_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | func init() { 4 | unmarshalCases = append(unmarshalCases, unmarshalCase{ 5 | ptr: (*struct { 6 | Field bool `json:"field"` 7 | })(nil), 8 | input: `{"field": null}`, 9 | }) 10 | } 11 | -------------------------------------------------------------------------------- /value_tests/eface_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | func init() { 4 | var pEFace = func(val interface{}) *interface{} { 5 | return &val 6 | } 7 | var pInt = func(val int) *int { 8 | return &val 9 | } 10 | unmarshalCases = append(unmarshalCases, unmarshalCase{ 11 | ptr: (**interface{})(nil), 12 | input: `"hello"`, 13 | }, unmarshalCase{ 14 | ptr: (**interface{})(nil), 15 | input: `1e1`, 16 | }, unmarshalCase{ 17 | ptr: (**interface{})(nil), 18 | input: `1.0e1`, 19 | }, unmarshalCase{ 20 | ptr: (*[]interface{})(nil), 21 | input: `[1.0e1]`, 22 | }, unmarshalCase{ 23 | ptr: (*struct { 24 | Field interface{} 25 | })(nil), 26 | input: `{"field":"hello"}`, 27 | }, unmarshalCase{ 28 | obj: func() interface{} { 29 | type TestData struct { 30 | Name string `json:"name"` 31 | } 32 | o := &TestData{} 33 | return &o 34 | }, 35 | input: `{"name":"value"}`, 36 | }, unmarshalCase{ 37 | obj: func() interface{} { 38 | b := true 39 | return &struct { 40 | Field interface{} `json:"field"` 41 | }{&b} 42 | }, 43 | input: `{"field": null}`, 44 | }, unmarshalCase{ 45 | obj: func() interface{} { 46 | var pb *bool 47 | return &struct { 48 | Field interface{} `json:"field"` 49 | }{&pb} 50 | }, 51 | input: `{"field": null}`, 52 | }, unmarshalCase{ 53 | obj: func() interface{} { 54 | b := true 55 | pb := &b 56 | return &struct { 57 | Field interface{} `json:"field"` 58 | }{&pb} 59 | }, 60 | input: `{"field": null}`, 61 | }) 62 | marshalCases = append(marshalCases, 63 | pEFace("hello"), 64 | struct { 65 | Field interface{} 66 | }{"hello"}, 67 | struct { 68 | Field interface{} 69 | }{struct { 70 | field chan int 71 | }{}}, 72 | struct { 73 | Field interface{} 74 | }{struct { 75 | Field *int 76 | }{pInt(100)}}, 77 | ) 78 | } 79 | -------------------------------------------------------------------------------- /value_tests/error_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "github.com/json-iterator/go" 5 | "github.com/stretchr/testify/require" 6 | "reflect" 7 | "testing" 8 | ) 9 | 10 | func Test_errorInput(t *testing.T) { 11 | for _, testCase := range unmarshalCases { 12 | if testCase.obj != nil { 13 | continue 14 | } 15 | valType := reflect.TypeOf(testCase.ptr).Elem() 16 | t.Run(valType.String(), func(t *testing.T) { 17 | for _, data := range []string{ 18 | `x`, 19 | `n`, 20 | `nul`, 21 | `{x}`, 22 | `{"x"}`, 23 | `{"x": "y"x}`, 24 | `{"x": "y"`, 25 | `{"x": "y", "a"}`, 26 | `[`, 27 | `[{"x": "y"}`, 28 | } { 29 | ptrVal := reflect.New(valType) 30 | ptr := ptrVal.Interface() 31 | err := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal([]byte(data), ptr) 32 | require.Error(t, err, "on input %q", data) 33 | } 34 | }) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /value_tests/float_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "strconv" 8 | "testing" 9 | 10 | jsoniter "github.com/json-iterator/go" 11 | "github.com/stretchr/testify/require" 12 | ) 13 | 14 | func Test_read_float(t *testing.T) { 15 | inputs := []string{ 16 | `1.1`, `1000`, `9223372036854775807`, `12.3`, `-12.3`, `720368.54775807`, `720368.547758075`, 17 | `1e1`, `1e+1`, `1e-1`, `1E1`, `1E+1`, `1E-1`, `-1e1`, `-1e+1`, `-1e-1`, 18 | } 19 | for _, input := range inputs { 20 | // non-streaming 21 | t.Run(fmt.Sprintf("%v", input), func(t *testing.T) { 22 | should := require.New(t) 23 | iter := jsoniter.ParseString(jsoniter.ConfigDefault, input+",") 24 | expected, err := strconv.ParseFloat(input, 32) 25 | should.Nil(err) 26 | should.Equal(float32(expected), iter.ReadFloat32()) 27 | }) 28 | t.Run(fmt.Sprintf("%v", input), func(t *testing.T) { 29 | should := require.New(t) 30 | iter := jsoniter.ParseString(jsoniter.ConfigDefault, input+",") 31 | expected, err := strconv.ParseFloat(input, 64) 32 | should.Nil(err) 33 | should.Equal(expected, iter.ReadFloat64()) 34 | }) 35 | // streaming 36 | t.Run(fmt.Sprintf("%v", input), func(t *testing.T) { 37 | should := require.New(t) 38 | iter := jsoniter.Parse(jsoniter.ConfigDefault, bytes.NewBufferString(input+","), 2) 39 | expected, err := strconv.ParseFloat(input, 32) 40 | should.Nil(err) 41 | should.Equal(float32(expected), iter.ReadFloat32()) 42 | }) 43 | t.Run(fmt.Sprintf("%v", input), func(t *testing.T) { 44 | should := require.New(t) 45 | iter := jsoniter.Parse(jsoniter.ConfigDefault, bytes.NewBufferString(input+","), 2) 46 | val := float64(0) 47 | err := json.Unmarshal([]byte(input), &val) 48 | should.Nil(err) 49 | should.Equal(val, iter.ReadFloat64()) 50 | }) 51 | } 52 | } 53 | 54 | func Test_write_float32(t *testing.T) { 55 | vals := []float32{0, 1, -1, 99, 0xff, 0xfff, 0xffff, 0xfffff, 0xffffff, 0x4ffffff, 0xfffffff, 56 | -0x4ffffff, -0xfffffff, 1.2345, 1.23456, 1.234567, 1.001} 57 | for _, val := range vals { 58 | t.Run(fmt.Sprintf("%v", val), func(t *testing.T) { 59 | should := require.New(t) 60 | buf := &bytes.Buffer{} 61 | stream := jsoniter.NewStream(jsoniter.ConfigDefault, buf, 4096) 62 | stream.WriteFloat32Lossy(val) 63 | stream.Flush() 64 | should.Nil(stream.Error) 65 | output, err := json.Marshal(val) 66 | should.Nil(err) 67 | should.Equal(string(output), buf.String()) 68 | }) 69 | t.Run(fmt.Sprintf("%v", val), func(t *testing.T) { 70 | should := require.New(t) 71 | buf := &bytes.Buffer{} 72 | stream := jsoniter.NewStream(jsoniter.ConfigDefault, buf, 4096) 73 | stream.WriteVal(val) 74 | stream.Flush() 75 | should.Nil(stream.Error) 76 | output, err := json.Marshal(val) 77 | should.Nil(err) 78 | should.Equal(string(output), buf.String()) 79 | }) 80 | } 81 | should := require.New(t) 82 | buf := &bytes.Buffer{} 83 | stream := jsoniter.NewStream(jsoniter.ConfigDefault, buf, 10) 84 | stream.WriteRaw("abcdefg") 85 | stream.WriteFloat32Lossy(1.123456) 86 | stream.Flush() 87 | should.Nil(stream.Error) 88 | should.Equal("abcdefg1.123456", buf.String()) 89 | 90 | stream = jsoniter.NewStream(jsoniter.ConfigDefault, nil, 0) 91 | stream.WriteFloat32(float32(0.0000001)) 92 | should.Equal("1e-7", string(stream.Buffer())) 93 | } 94 | 95 | func Test_write_float64(t *testing.T) { 96 | vals := []float64{0, 1, -1, 99, 0xff, 0xfff, 0xffff, 0xfffff, 0xffffff, 0x4ffffff, 0xfffffff, 97 | -0x4ffffff, -0xfffffff, 1.2345, 1.23456, 1.234567, 1.001} 98 | for _, val := range vals { 99 | t.Run(fmt.Sprintf("%v", val), func(t *testing.T) { 100 | should := require.New(t) 101 | buf := &bytes.Buffer{} 102 | stream := jsoniter.NewStream(jsoniter.ConfigDefault, buf, 4096) 103 | stream.WriteFloat64Lossy(val) 104 | stream.Flush() 105 | should.Nil(stream.Error) 106 | should.Equal(strconv.FormatFloat(val, 'f', -1, 64), buf.String()) 107 | }) 108 | t.Run(fmt.Sprintf("%v", val), func(t *testing.T) { 109 | should := require.New(t) 110 | buf := &bytes.Buffer{} 111 | stream := jsoniter.NewStream(jsoniter.ConfigDefault, buf, 4096) 112 | stream.WriteVal(val) 113 | stream.Flush() 114 | should.Nil(stream.Error) 115 | should.Equal(strconv.FormatFloat(val, 'f', -1, 64), buf.String()) 116 | }) 117 | } 118 | should := require.New(t) 119 | buf := &bytes.Buffer{} 120 | stream := jsoniter.NewStream(jsoniter.ConfigDefault, buf, 10) 121 | stream.WriteRaw("abcdefg") 122 | stream.WriteFloat64Lossy(1.123456) 123 | stream.Flush() 124 | should.Nil(stream.Error) 125 | should.Equal("abcdefg1.123456", buf.String()) 126 | 127 | stream = jsoniter.NewStream(jsoniter.ConfigDefault, nil, 0) 128 | stream.WriteFloat64(float64(0.0000001)) 129 | should.Equal("1e-7", string(stream.Buffer())) 130 | } 131 | -------------------------------------------------------------------------------- /value_tests/iface_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import "io" 4 | 5 | func init() { 6 | var pCloser1 = func(str string) *io.Closer { 7 | closer := io.Closer(strCloser1(str)) 8 | return &closer 9 | } 10 | var pCloser2 = func(str string) *io.Closer { 11 | strCloser := strCloser2(str) 12 | closer := io.Closer(&strCloser) 13 | return &closer 14 | } 15 | marshalCases = append(marshalCases, 16 | pCloser1("hello"), 17 | pCloser2("hello"), 18 | ) 19 | unmarshalCases = append(unmarshalCases, unmarshalCase{ 20 | ptr: (*[]io.Closer)(nil), 21 | input: "[null]", 22 | }, unmarshalCase{ 23 | obj: func() interface{} { 24 | strCloser := strCloser2("") 25 | return &struct { 26 | Field io.Closer 27 | }{ 28 | &strCloser, 29 | } 30 | }, 31 | input: `{"Field": "hello"}`, 32 | }) 33 | } 34 | 35 | type strCloser1 string 36 | 37 | func (closer strCloser1) Close() error { 38 | return nil 39 | } 40 | 41 | type strCloser2 string 42 | 43 | func (closer *strCloser2) Close() error { 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /value_tests/map_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "math/big" 7 | "time" 8 | ) 9 | 10 | func init() { 11 | var pRawMessage = func(val json.RawMessage) *json.RawMessage { 12 | return &val 13 | } 14 | nilMap := map[string]string(nil) 15 | marshalCases = append(marshalCases, 16 | map[string]interface{}{"abc": 1}, 17 | map[string]MyInterface{"hello": MyString("world")}, 18 | map[*big.Float]string{big.NewFloat(1.2): "2"}, 19 | map[string]interface{}{ 20 | "3": 3, 21 | "1": 1, 22 | "2": 2, 23 | }, 24 | map[uint64]interface{}{ 25 | uint64(1): "a", 26 | uint64(2): "a", 27 | uint64(4): "a", 28 | }, 29 | nilMap, 30 | &nilMap, 31 | map[string]*json.RawMessage{"hello": pRawMessage(json.RawMessage("[]"))}, 32 | map[Date]bool{{}: true}, 33 | map[Date2]bool{{}: true}, 34 | map[customKey]string{customKey(1): "bar"}, 35 | ) 36 | unmarshalCases = append(unmarshalCases, unmarshalCase{ 37 | ptr: (*map[string]string)(nil), 38 | input: `{"k\"ey": "val"}`, 39 | }, unmarshalCase{ 40 | ptr: (*map[string]string)(nil), 41 | input: `null`, 42 | }, unmarshalCase{ 43 | ptr: (*map[string]*json.RawMessage)(nil), 44 | input: "{\"test\":[{\"key\":\"value\"}]}", 45 | }, unmarshalCase{ 46 | ptr: (*map[Date]bool)(nil), 47 | input: `{ 48 | "2018-12-12": true, 49 | "2018-12-13": true, 50 | "2018-12-14": true 51 | }`, 52 | }, unmarshalCase{ 53 | ptr: (*map[Date2]bool)(nil), 54 | input: `{ 55 | "2018-12-12": true, 56 | "2018-12-13": true, 57 | "2018-12-14": true 58 | }`, 59 | }, unmarshalCase{ 60 | ptr: (*map[customKey]string)(nil), 61 | input: `{"foo": "bar"}`, 62 | }) 63 | } 64 | 65 | type MyInterface interface { 66 | Hello() string 67 | } 68 | 69 | type MyString string 70 | 71 | func (ms MyString) Hello() string { 72 | return string(ms) 73 | } 74 | 75 | type Date struct { 76 | time.Time 77 | } 78 | 79 | func (d *Date) UnmarshalJSON(b []byte) error { 80 | dateStr := string(b) // something like `"2017-08-20"` 81 | 82 | if dateStr == "null" { 83 | return nil 84 | } 85 | 86 | t, err := time.Parse(`"2006-01-02"`, dateStr) 87 | if err != nil { 88 | return fmt.Errorf("cant parse date: %#v", err) 89 | } 90 | 91 | d.Time = t 92 | return nil 93 | } 94 | 95 | func (d *Date) MarshalJSON() ([]byte, error) { 96 | return []byte(d.Time.Format("2006-01-02")), nil 97 | } 98 | 99 | type Date2 struct { 100 | time.Time 101 | } 102 | 103 | func (d Date2) UnmarshalJSON(b []byte) error { 104 | dateStr := string(b) // something like `"2017-08-20"` 105 | 106 | if dateStr == "null" { 107 | return nil 108 | } 109 | 110 | t, err := time.Parse(`"2006-01-02"`, dateStr) 111 | if err != nil { 112 | return fmt.Errorf("cant parse date: %#v", err) 113 | } 114 | 115 | d.Time = t 116 | return nil 117 | } 118 | 119 | func (d Date2) MarshalJSON() ([]byte, error) { 120 | return []byte(d.Time.Format("2006-01-02")), nil 121 | } 122 | 123 | type customKey int32 124 | 125 | func (c customKey) MarshalText() ([]byte, error) { 126 | return []byte("foo"), nil 127 | } 128 | 129 | func (c *customKey) UnmarshalText(value []byte) error { 130 | *c = 1 131 | return nil 132 | } 133 | -------------------------------------------------------------------------------- /value_tests/marshaler_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "encoding" 5 | "encoding/json" 6 | ) 7 | 8 | func init() { 9 | jm := json.Marshaler(jmOfStruct{}) 10 | tm1 := encoding.TextMarshaler(tmOfStruct{}) 11 | tm2 := encoding.TextMarshaler(&tmOfStructInt{}) 12 | marshalCases = append(marshalCases, 13 | jmOfStruct{}, 14 | &jm, 15 | tmOfStruct{}, 16 | &tm1, 17 | tmOfStructInt{}, 18 | &tm2, 19 | map[tmOfStruct]int{ 20 | {}: 100, 21 | }, 22 | map[*tmOfStruct]int{ 23 | {}: 100, 24 | }, 25 | map[encoding.TextMarshaler]int{ 26 | tm1: 100, 27 | }, 28 | ) 29 | unmarshalCases = append(unmarshalCases, unmarshalCase{ 30 | ptr: (*tmOfMap)(nil), 31 | input: `"{1:2}"`, 32 | }, unmarshalCase{ 33 | ptr: (*tmOfMapPtr)(nil), 34 | input: `"{1:2}"`, 35 | }) 36 | } 37 | 38 | type jmOfStruct struct { 39 | F2 chan []byte 40 | } 41 | 42 | func (q jmOfStruct) MarshalJSON() ([]byte, error) { 43 | return []byte(`""`), nil 44 | } 45 | 46 | func (q *jmOfStruct) UnmarshalJSON(value []byte) error { 47 | return nil 48 | } 49 | 50 | type tmOfStruct struct { 51 | F2 chan []byte 52 | } 53 | 54 | func (q tmOfStruct) MarshalText() ([]byte, error) { 55 | return []byte(`""`), nil 56 | } 57 | 58 | func (q *tmOfStruct) UnmarshalText(value []byte) error { 59 | return nil 60 | } 61 | 62 | type tmOfStructInt struct { 63 | Field2 int 64 | } 65 | 66 | func (q *tmOfStructInt) MarshalText() ([]byte, error) { 67 | return []byte(`"abc"`), nil 68 | } 69 | 70 | func (q *tmOfStructInt) UnmarshalText(value []byte) error { 71 | return nil 72 | } 73 | 74 | type tmOfMap map[int]int 75 | 76 | func (q tmOfMap) UnmarshalText(value []byte) error { 77 | return nil 78 | } 79 | 80 | type tmOfMapPtr map[int]int 81 | 82 | func (q *tmOfMapPtr) UnmarshalText(value []byte) error { 83 | return nil 84 | } 85 | -------------------------------------------------------------------------------- /value_tests/number_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import "encoding/json" 4 | 5 | func init() { 6 | unmarshalCases = append(unmarshalCases, unmarshalCase{ 7 | ptr: (*json.Number)(nil), 8 | input: `"500"`, 9 | }, unmarshalCase{ 10 | ptr: (*json.Number)(nil), 11 | input: `1`, 12 | }, unmarshalCase{ 13 | ptr: (*json.Number)(nil), 14 | input: `null`, 15 | }) 16 | marshalCases = append(marshalCases, json.Number("")) 17 | } 18 | -------------------------------------------------------------------------------- /value_tests/ptr_114_test.go: -------------------------------------------------------------------------------- 1 | // +build go1.14 2 | 3 | package test 4 | 5 | func init() { 6 | unmarshalCases = append(unmarshalCases, unmarshalCase{ 7 | obj: func() interface{} { 8 | var i int 9 | pi := &i 10 | ppi := &pi 11 | return &ppi 12 | }, 13 | input: "null", 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /value_tests/ptr_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | func init() { 4 | var pInt = func(val int) *int { 5 | return &val 6 | } 7 | marshalCases = append(marshalCases, 8 | (*int)(nil), 9 | pInt(100), 10 | ) 11 | unmarshalCases = append(unmarshalCases, unmarshalCase{ 12 | obj: func() interface{} { 13 | var i int 14 | return &i 15 | }, 16 | input: "null", 17 | }, unmarshalCase{ 18 | obj: func() interface{} { 19 | var i *int 20 | return &i 21 | }, 22 | input: "10", 23 | }, unmarshalCase{ 24 | obj: func() interface{} { 25 | var i int 26 | pi := &i 27 | return &pi 28 | }, 29 | input: "null", 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /value_tests/raw_message_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | func init() { 8 | marshalCases = append(marshalCases, 9 | json.RawMessage("{}"), 10 | json.RawMessage("12345"), 11 | json.RawMessage("3.14"), 12 | json.RawMessage("-0.5e10"), 13 | struct { 14 | Env string `json:"env"` 15 | Extra json.RawMessage `json:"extra,omitempty"` 16 | }{ 17 | Env: "jfdk", 18 | }, 19 | ) 20 | unmarshalCases = append(unmarshalCases, unmarshalCase{ 21 | ptr: (*json.RawMessage)(nil), 22 | input: `[1,2,3]`, 23 | }, unmarshalCase{ 24 | ptr: (*json.RawMessage)(nil), 25 | input: `1.122e+250`, 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /value_tests/slice_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | func init() { 4 | nilSlice := []string(nil) 5 | marshalCases = append(marshalCases, 6 | []interface{}{"hello"}, 7 | nilSlice, 8 | &nilSlice, 9 | []byte{1, 2, 3}, 10 | ) 11 | unmarshalCases = append(unmarshalCases, unmarshalCase{ 12 | ptr: (*[]string)(nil), 13 | input: "null", 14 | }, unmarshalCase{ 15 | ptr: (*[]string)(nil), 16 | input: "[]", 17 | }, unmarshalCase{ 18 | ptr: (*[]byte)(nil), 19 | input: "[1,2,3]", 20 | }, unmarshalCase{ 21 | ptr: (*[]byte)(nil), 22 | input: `"aGVsbG8="`, 23 | }, unmarshalCase{ 24 | ptr: (*[]byte)(nil), 25 | input: `"c3ViamVjdHM\/X2Q9MQ=="`, 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /value_tests/string_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/json-iterator/go" 6 | "testing" 7 | "unicode/utf8" 8 | ) 9 | 10 | func init() { 11 | marshalCases = append(marshalCases, 12 | `>`, 13 | `"数字山谷"`, 14 | "he\u2029\u2028he", 15 | ) 16 | for i := 0; i < utf8.RuneSelf; i++ { 17 | marshalCases = append(marshalCases, string([]byte{byte(i)})) 18 | } 19 | } 20 | 21 | func Test_read_string(t *testing.T) { 22 | badInputs := []string{ 23 | ``, 24 | `"`, 25 | `"\"`, 26 | `"\\\"`, 27 | "\"\n\"", 28 | `"\U0001f64f"`, 29 | `"\uD83D\u00"`, 30 | } 31 | for i := 0; i < 32; i++ { 32 | // control characters are invalid 33 | badInputs = append(badInputs, string([]byte{'"', byte(i), '"'})) 34 | } 35 | 36 | for _, input := range badInputs { 37 | testReadString(t, input, "", true, "json.Unmarshal", json.Unmarshal) 38 | testReadString(t, input, "", true, "jsoniter.Unmarshal", jsoniter.Unmarshal) 39 | testReadString(t, input, "", true, "jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal", jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal) 40 | } 41 | 42 | goodInputs := []struct { 43 | input string 44 | expectValue string 45 | }{ 46 | {`""`, ""}, 47 | {`"a"`, "a"}, 48 | {`null`, ""}, 49 | {`"Iñtërnâtiônàlizætiøn,💝🐹🌇⛔"`, "Iñtërnâtiônàlizætiøn,💝🐹🌇⛔"}, 50 | {`"\uD83D"`, string([]byte{239, 191, 189})}, 51 | {`"\uD83D\\"`, string([]byte{239, 191, 189, '\\'})}, 52 | {`"\uD83D\ub000"`, string([]byte{239, 191, 189, 235, 128, 128})}, 53 | {`"\uD83D\ude04"`, "😄"}, 54 | {`"\uDEADBEEF"`, string([]byte{239, 191, 189, 66, 69, 69, 70})}, 55 | {`"hel\"lo"`, `hel"lo`}, 56 | {`"hel\\\/lo"`, `hel\/lo`}, 57 | {`"hel\\blo"`, `hel\blo`}, 58 | {`"hel\\\blo"`, "hel\\\blo"}, 59 | {`"hel\\nlo"`, `hel\nlo`}, 60 | {`"hel\\\nlo"`, "hel\\\nlo"}, 61 | {`"hel\\tlo"`, `hel\tlo`}, 62 | {`"hel\\flo"`, `hel\flo`}, 63 | {`"hel\\\flo"`, "hel\\\flo"}, 64 | {`"hel\\\rlo"`, "hel\\\rlo"}, 65 | {`"hel\\\tlo"`, "hel\\\tlo"}, 66 | {`"\u4e2d\u6587"`, "中文"}, 67 | {`"\ud83d\udc4a"`, "\xf0\x9f\x91\x8a"}, 68 | } 69 | 70 | for _, tc := range goodInputs { 71 | testReadString(t, tc.input, tc.expectValue, false, "json.Unmarshal", json.Unmarshal) 72 | testReadString(t, tc.input, tc.expectValue, false, "jsoniter.Unmarshal", jsoniter.Unmarshal) 73 | testReadString(t, tc.input, tc.expectValue, false, "jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal", jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal) 74 | } 75 | } 76 | 77 | func testReadString(t *testing.T, input string, expectValue string, expectError bool, marshalerName string, marshaler func([]byte, interface{}) error) { 78 | var value string 79 | err := marshaler([]byte(input), &value) 80 | if expectError != (err != nil) { 81 | t.Errorf("%q: %s: expected error %v, got %v", input, marshalerName, expectError, err) 82 | return 83 | } 84 | if value != expectValue { 85 | t.Errorf("%q: %s: expected %q, got %q", input, marshalerName, expectValue, value) 86 | return 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /value_tests/struct_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "time" 7 | ) 8 | 9 | func init() { 10 | var pString = func(val string) *string { 11 | return &val 12 | } 13 | epoch := time.Unix(0, 0) 14 | unmarshalCases = append(unmarshalCases, unmarshalCase{ 15 | ptr: (*struct { 16 | Field interface{} 17 | })(nil), 18 | input: `{"Field": "hello"}`, 19 | }, unmarshalCase{ 20 | ptr: (*struct { 21 | Field interface{} 22 | })(nil), 23 | input: `{"Field": "hello"} `, 24 | }, unmarshalCase{ 25 | ptr: (*struct { 26 | Field int `json:"field"` 27 | })(nil), 28 | input: `{"field": null}`, 29 | }, unmarshalCase{ 30 | ptr: (*struct { 31 | Field int `json:"field,string"` 32 | })(nil), 33 | input: `{"field": null}`, 34 | }, unmarshalCase{ 35 | ptr: (*struct { 36 | ID int `json:"id"` 37 | Payload map[string]interface{} `json:"payload"` 38 | buf *bytes.Buffer 39 | })(nil), 40 | input: ` {"id":1, "payload":{"account":"123","password":"456"}}`, 41 | }, unmarshalCase{ 42 | ptr: (*struct { 43 | Field1 string 44 | })(nil), 45 | input: `{"Field\"1":"hello"}`, 46 | }, unmarshalCase{ 47 | ptr: (*struct { 48 | Field1 string 49 | })(nil), 50 | input: `{"\u0046ield1":"hello"}`, 51 | }, unmarshalCase{ 52 | ptr: (*struct { 53 | Field1 *string 54 | Field2 *string 55 | })(nil), 56 | input: `{"field1": null, "field2": "world"}`, 57 | }, unmarshalCase{ 58 | ptr: (*struct { 59 | Field1 string 60 | Field2 json.RawMessage 61 | })(nil), 62 | input: `{"field1": "hello", "field2":[1,2,3]}`, 63 | }, unmarshalCase{ 64 | ptr: (*struct { 65 | a int 66 | b <-chan int 67 | C int 68 | d *time.Timer 69 | })(nil), 70 | input: `{"a": 444, "b":"bad", "C":256, "d":{"not":"a timer"}}`, 71 | }, unmarshalCase{ 72 | ptr: (*struct { 73 | A string 74 | B string 75 | C string 76 | D string 77 | E string 78 | F string 79 | G string 80 | H string 81 | I string 82 | J string 83 | K string 84 | })(nil), 85 | input: `{"a":"1","b":"2","c":"3","d":"4","e":"5","f":"6","g":"7","h":"8","i":"9","j":"10","k":"11"}`, 86 | }, unmarshalCase{ 87 | ptr: (*struct { 88 | T float64 `json:"T"` 89 | })(nil), 90 | input: `{"t":10.0}`, 91 | }, unmarshalCase{ 92 | ptr: (*struct { 93 | T float64 `json:"T"` 94 | })(nil), 95 | input: `{"T":10.0}`, 96 | }, unmarshalCase{ 97 | ptr: (*struct { 98 | T float64 `json:"t"` 99 | })(nil), 100 | input: `{"T":10.0}`, 101 | }, unmarshalCase{ 102 | ptr: (*struct { 103 | KeyString string `json:"key_string"` 104 | Type string `json:"type"` 105 | Asks [][2]float64 `json:"asks"` 106 | })(nil), 107 | input: `{"key_string": "KEYSTRING","type": "TYPE","asks": [[1e+66,1]]}`, 108 | }) 109 | marshalCases = append(marshalCases, 110 | struct { 111 | Field map[string]interface{} 112 | }{ 113 | map[string]interface{}{"hello": "world"}, 114 | }, 115 | struct { 116 | Field map[string]interface{} 117 | Field2 string 118 | }{ 119 | map[string]interface{}{"hello": "world"}, "", 120 | }, 121 | struct { 122 | Field interface{} 123 | }{ 124 | 1024, 125 | }, 126 | struct { 127 | Field MyInterface 128 | }{ 129 | MyString("hello"), 130 | }, 131 | struct { 132 | F *float64 133 | }{}, 134 | struct { 135 | *time.Time 136 | }{&epoch}, 137 | struct { 138 | *StructVarious 139 | }{&StructVarious{}}, 140 | struct { 141 | *StructVarious 142 | Field int 143 | }{nil, 10}, 144 | struct { 145 | Field1 int 146 | Field2 [1]*float64 147 | }{}, 148 | struct { 149 | Field interface{} `json:"field,omitempty"` 150 | }{}, 151 | struct { 152 | Field MyInterface `json:"field,omitempty"` 153 | }{}, 154 | struct { 155 | Field MyInterface `json:"field,omitempty"` 156 | }{MyString("hello")}, 157 | struct { 158 | Field json.Marshaler `json:"field"` 159 | }{}, 160 | struct { 161 | Field MyInterface `json:"field"` 162 | }{}, 163 | struct { 164 | Field MyInterface `json:"field"` 165 | }{MyString("hello")}, 166 | struct { 167 | Field1 string `json:"field-1,omitempty"` 168 | Field2 func() `json:"-"` 169 | }{}, 170 | structRecursive{}, 171 | struct { 172 | *CacheItem 173 | 174 | // Omit bad keys 175 | OmitMaxAge omit `json:"cacheAge,omitempty"` 176 | 177 | // Add nice keys 178 | MaxAge int `json:"max_age"` 179 | }{ 180 | CacheItem: &CacheItem{ 181 | Key: "value", 182 | MaxAge: 100, 183 | }, 184 | MaxAge: 20, 185 | }, 186 | structOrder{}, 187 | struct { 188 | Field1 *string 189 | Field2 *string 190 | }{Field2: pString("world")}, 191 | struct { 192 | a int 193 | b <-chan int 194 | C int 195 | d *time.Timer 196 | }{ 197 | a: 42, 198 | b: make(<-chan int, 10), 199 | C: 21, 200 | d: time.NewTimer(10 * time.Second), 201 | }, 202 | struct { 203 | _UnderscoreField string 204 | }{ 205 | "should not marshal", 206 | }, 207 | ) 208 | } 209 | 210 | type StructVarious struct { 211 | Field0 string 212 | Field1 []string 213 | Field2 map[string]interface{} 214 | } 215 | 216 | type structRecursive struct { 217 | Field1 string 218 | Me *structRecursive 219 | } 220 | 221 | type omit *struct{} 222 | type CacheItem struct { 223 | Key string `json:"key"` 224 | MaxAge int `json:"cacheAge"` 225 | } 226 | 227 | type orderA struct { 228 | Field2 string 229 | } 230 | 231 | type orderC struct { 232 | Field5 string 233 | } 234 | 235 | type orderB struct { 236 | Field4 string 237 | orderC 238 | Field6 string 239 | } 240 | 241 | type structOrder struct { 242 | Field1 string 243 | orderA 244 | Field3 string 245 | orderB 246 | Field7 string 247 | } 248 | -------------------------------------------------------------------------------- /value_tests/value_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/json-iterator/go" 7 | "github.com/modern-go/reflect2" 8 | "github.com/stretchr/testify/require" 9 | "testing" 10 | ) 11 | 12 | type unmarshalCase struct { 13 | obj func() interface{} 14 | ptr interface{} 15 | input string 16 | selected bool 17 | } 18 | 19 | var unmarshalCases []unmarshalCase 20 | 21 | var marshalCases = []interface{}{ 22 | nil, 23 | } 24 | 25 | type selectedMarshalCase struct { 26 | marshalCase interface{} 27 | } 28 | 29 | func Test_unmarshal(t *testing.T) { 30 | for _, testCase := range unmarshalCases { 31 | if testCase.selected { 32 | unmarshalCases = []unmarshalCase{testCase} 33 | break 34 | } 35 | } 36 | for i, testCase := range unmarshalCases { 37 | t.Run(fmt.Sprintf("[%v]%s", i, testCase.input), func(t *testing.T) { 38 | should := require.New(t) 39 | var obj1 interface{} 40 | var obj2 interface{} 41 | if testCase.obj != nil { 42 | obj1 = testCase.obj() 43 | obj2 = testCase.obj() 44 | } else { 45 | valType := reflect2.TypeOfPtr(testCase.ptr).Elem() 46 | obj1 = valType.New() 47 | obj2 = valType.New() 48 | } 49 | err1 := json.Unmarshal([]byte(testCase.input), obj1) 50 | should.NoError(err1, "json") 51 | err2 := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal([]byte(testCase.input), obj2) 52 | should.NoError(err2, "jsoniter") 53 | should.Equal(obj1, obj2) 54 | }) 55 | } 56 | } 57 | 58 | func Test_marshal(t *testing.T) { 59 | for _, testCase := range marshalCases { 60 | selectedMarshalCase, found := testCase.(selectedMarshalCase) 61 | if found { 62 | marshalCases = []interface{}{selectedMarshalCase.marshalCase} 63 | break 64 | } 65 | } 66 | for i, testCase := range marshalCases { 67 | var name string 68 | if testCase != nil { 69 | name = fmt.Sprintf("[%v]%v/%s", i, testCase, reflect2.TypeOf(testCase).String()) 70 | } 71 | t.Run(name, func(t *testing.T) { 72 | should := require.New(t) 73 | output1, err1 := json.Marshal(testCase) 74 | should.NoError(err1, "json") 75 | output2, err2 := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(testCase) 76 | should.NoError(err2, "jsoniter") 77 | should.Equal(string(output1), string(output2)) 78 | }) 79 | } 80 | } 81 | --------------------------------------------------------------------------------