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

732 Commits

Author SHA1 Message Date
e6b9536d36 Merge pull request #513 from AllenX2018/fix-issue-510
fix issue 510
2020-11-18 09:31:58 +08:00
a3465d79a9 fix issue 510 2020-11-17 17:37:55 +08:00
9b79a3e192 fix issue 508 (#512) 2020-11-14 09:45:31 +08:00
1779031cda fix issue 508 2020-11-13 15:58:42 +08:00
6821bec9fa jsoniter: Fix errors during reading integers from chunked io.Reader (#477)
This commit fixes bug in Iterator.assertInteger method if the next
conditions are met:
- Iterator reads data from `io.Reader`,
- expected value is `0` (zero)
- `Iterator.tail == Iterator.head + 1`
- `Iterator.tail < len(Iterator.buf)`
- value in the buffer after `Iterator.tail` is presented from the previous read and has '.' character.

Typical error which user cal see is:
- assertInteger: can not decode float as int, error found in #X byte of ...

Regression test added for checking the correct behaviour.

Fixes #476
2020-08-06 09:14:08 +08:00
9461257643 fixed null in number json tag string (#480) 2020-08-03 10:12:41 +08:00
5bce16d299 fix issue #469 2020-07-21 17:07:23 +08:00
yjh
a1ca083078 update readme (#464) 2020-06-08 10:58:30 +08:00
cd6773e694 remove quotation check for key when decoding map
we don't need to check if the key is surrounded by quotation.
In fact, the key might not be strings if we register an extension to
customize the map key encoder/decoder.It may be an integer, float, or
even a struct.
2020-05-31 18:18:48 +08:00
55287ed53a disable map_key_test temporarily
- disable this test until golang issue 38105&38940 is fixed
2020-05-08 15:08:25 +08:00
8961be9c21 Map keys of custom types should serialize using MarshalText when available (#461)
* Map keys of custom types should serialize/deserialize using MarshalText/UnmarshalText when available

- this brings marshaling/unmarshaling behavior in line with encoding/json
- in general, any types that implement the interfaces from the encoding package (TextUnmarshaler, TextMarshaler, etc.) should use the provided method when available
2020-05-08 10:21:59 +08:00
0f8241d334 Merge pull request #441 from robfig/flush2
(*Stream).WriteMore: do not Flush
2020-04-29 14:50:44 +08:00
53b9d06ba7 temporarily comment out some test case to fix the CI fail since go1.14 released 2020-03-30 16:42:45 +08:00
1f7ee05ef8 Merge pull request #433 from AllenX2018/fix-anonymous-struct-error-message
fix issue #421
2020-03-30 16:26:14 +08:00
b22f393858 Merge pull request #450 from fantastao/fix_any_str_panic
fix any str ToInt64 ToUint64 panic
2020-03-26 19:37:32 +08:00
bede7b9e40 fix any str ToInt64 ToUint64 panic
Change-Id: Ic1690713d96940258811cdc149b1604128aa91a2
2020-03-26 18:17:29 +08:00
58aeb59006 fix issue #449 2020-03-26 14:46:47 +08:00
7acbb404a4 Merge pull request #432 from AllenX2018/fix-raw-message-integer-issue
fix issue #389 #411
2020-02-06 09:56:04 +08:00
69f2a91ff4 Merge pull request #429 from AllenX2018/fix-typo
fix issue #326
2020-02-05 09:51:54 +08:00
11a37a0774 (*Stream).WriteMore: remove implicit Flush
I believe that WriteMore should not call Flush for these reasons:

1. This is surprising for users because of inconsistency. Why call Flush in
   WriteMore and not in WriteObjectEnd?

2. It is not necessary; callers are free to call Flush if their use case demands
   it.

3. It harms performance in the common case by flushing the buffer much more
   frequently than it needs to be flushed.

The stream benchmark shows a 7% benefit to removing the Flush call, and I
observed a similar speedup in my real-world use case.

    benchmark                                        old ns/op     new ns/op     delta
    Benchmark_encode_string_with_SetEscapeHTML-8     442           437           -1.13%
    Benchmark_jsoniter_large_file-8                  21222         21062         -0.75%
    Benchmark_json_large_file-8                      40187         40266         +0.20%
    Benchmark_stream_encode_big_object-8             8611          7956          -7.61%

    benchmark                                        old allocs     new allocs     delta
    Benchmark_encode_string_with_SetEscapeHTML-8     6              6              +0.00%
    Benchmark_jsoniter_large_file-8                  78             78             +0.00%
    Benchmark_json_large_file-8                      13             13             +0.00%
    Benchmark_stream_encode_big_object-8             0              0              +0.00%

    benchmark                                        old bytes     new bytes     delta
    Benchmark_encode_string_with_SetEscapeHTML-8     760           760           +0.00%
    Benchmark_jsoniter_large_file-8                  4920          4920          +0.00%
    Benchmark_json_large_file-8                      6640          6640          +0.00%
    Benchmark_stream_encode_big_object-8             0             0             +0.00%

Backwards compatibility - I believe there is little to no risk that this breaks
callers. WriteMore does not leave the JSON in a valid state, so it must be
followed by other Write* methods. To get the finished JSON out, the caller must
already be calling Flush.
2020-01-17 22:14:50 -05:00
91f4a6405d (*Stream).Flush: reset buffer to beginning
Previously it would append to the end of the buffer instead of reusing the
now-free space.

Benchmark demonstrates the improvement, run with -benchtime=10s

    benchmark                                        old ns/op     new ns/op     delta
    Benchmark_encode_string_with_SetEscapeHTML-8     447           442           -1.12%
    Benchmark_jsoniter_large_file-8                  20998         21222         +1.07%
    Benchmark_json_large_file-8                      39593         40187         +1.50%
    Benchmark_stream_encode_big_object-8             10787         8611          -20.17%

    benchmark                                        old allocs     new allocs     delta
    Benchmark_encode_string_with_SetEscapeHTML-8     6              6              +0.00%
    Benchmark_jsoniter_large_file-8                  78             78             +0.00%
    Benchmark_json_large_file-8                      13             13             +0.00%
    Benchmark_stream_encode_big_object-8             31             0              -100.00%

    benchmark                                        old bytes     new bytes     delta
    Benchmark_encode_string_with_SetEscapeHTML-8     760           760           +0.00%
    Benchmark_jsoniter_large_file-8                  4920          4920          +0.00%
    Benchmark_json_large_file-8                      6640          6640          +0.00%
    Benchmark_stream_encode_big_object-8             10056         0             -100.00%

Fixes #438
2020-01-17 21:57:59 -05:00
a54d350455 benchmarks: add benchmark for Stream 2020-01-17 16:50:46 -05:00
6f4c196d95 add more testcase 2020-01-16 17:17:18 +08:00
8302a17e8c fix issue #421 2020-01-15 20:21:20 +08:00
3987001e27 fix issue #389 #411 2020-01-14 14:14:02 +08:00
78d9e97b7a fix issue #326 2020-01-10 16:11:04 +08:00
49c900ee46 Merge pull request #427 from AllenX2018/fix-typo
fix the error message typo of ReadObjectCB & ReadMapCb function
2020-01-06 10:38:24 +08:00
9c0685d8d3 fix the error message typo of ReadObjectCB & ReadMapCb function 2020-01-03 15:31:12 +08:00
acfec88f7a Merge pull request #422 from JensErat/map-invalid-type
pass nested error in compatible configuration, fixes #388
2019-12-21 11:10:28 +08:00
e88512faf8 Merge pull request #423 from vano144/fix-attachments-on-stream
fix nil attachment on stream in custom encoder on sorted map
2019-12-21 11:09:53 +08:00
b681149eae Merge pull request #424 from aaronbee/sortKeysMapAllocations
Reduce allocations in sortKeysMapEncoder
2019-12-21 11:09:15 +08:00
d1af7639b3 Merge pull request #425 from liggitt/default-max-depth
Revert "Merge pull request #418 from bbrks/configurable_maxDepth"
2019-12-21 11:04:54 +08:00
7c9f8c2d20 Revert "Merge pull request #418 from bbrks/configurable_maxDepth"
This reverts commit 44a7e7340d, reversing
changes made to dc11f49689.
2019-12-19 19:06:29 -05:00
f814d6c0f1 Reduce allocations in sortKeysMapEncoder
Use one buffer for all values.
2019-12-03 11:55:47 -08:00
aba8654400 fix nil attachment on stream in custom encoder on sorted map 2019-11-28 17:39:42 +03:00
a1c9557592 pass nested error in compatible configuration
When invalid types inside a map were marshalled (in general, as soon as
sorted maps have been configured), the error message has not been
propagated out of the map's `subStream`.

Also fix and re-enable the channel test, which now resembles the
behavior of `encoding/json` and tests both default and compatible
configurations.

Signed-off-by: Jens Erat <email@jenserat.de>
2019-11-22 16:56:59 +01:00
44a7e7340d Merge pull request #418 from bbrks/configurable_maxDepth
Add MaxDepth as a config option
2019-11-12 22:47:28 +08:00
2834c7e43c Remove large test values that fail on 32-bit architectures 2019-11-11 16:35:50 +00:00
d296277d5c Adds MaxDepth config option
Defaults to 10,000 to match the existing maxDepth constant everywhetre,
except when using `ConfigCompatibleWithStandardLibrary` - which retains
the limitless depth (and causes a stack overflow).

Added tests for the new config, and also up to jsoniter's stack overflow limit.
2019-11-11 16:13:59 +00:00
dc11f49689 Merge pull request #416 from jarredhawkins/issue-415
Ignore unnamed literals in structs
2019-10-30 08:35:33 +08:00
83f7b825b3 Unnamed struct literals 2019-10-28 23:05:10 -07:00
03217c3e97 Merge pull request #410 from liggitt/stack
Limit nesting depth
2019-10-12 21:07:04 +08:00
908eaed151 Merge pull request #408 from onelrdm/master
skip - tag before spliting parts
2019-10-12 21:05:04 +08:00
eec24895fe Limit nesting depth 2019-10-08 11:17:01 -04:00
1ba732a07d skip - tag before spliting parts 2019-09-28 17:17:44 +08:00
819acad769 Merge pull request #398 from teou/master
use json.Marshaler then trim the last '\n' in reflect_marshaler
2019-09-23 14:02:24 +08:00
695ec2b83b Merge pull request #406 from bbrks/fix_nil_map_encoding
Fixes #405 - Encode nil map into null
2019-09-23 13:59:22 +08:00
028e2ef2bd Fixes #405 - Encode nil map into null 2019-09-19 13:11:30 +01:00
976454858b use json.Marshaler then trim the last '\n' in reflect_marshaler
N/A
2019-08-14 10:10:02 +08:00
27518f6661 Merge pull request #373 from ernado/append-skip
fix #372: add AppendSkip iterator method
2019-06-22 00:12:01 +08:00
94869abf43 Merge pull request #368 from alextomaili/fix-memory-allocation-overhead
allocate string for error description only if it really required
2019-06-22 00:11:00 +08:00
459f0e30ae fix #37: add SkipAndAppendBytes iterator method 2019-06-10 12:40:05 +03:00
0039f4ac3d Merge pull request #371 from nikhita/byte-base64-encode
Don't marshal empty byte or uint8 slice as null
2019-06-03 19:06:22 +02:00
fb5614a4ca Don't marshal empty byte or uint8 slice as null
[]byte or []uint8 are encoded as base-64 encoded string. Per this, non-nil
empty slice should not get marshalled as null, rather as "".

This restores compatibility with the standard library.
2019-06-03 16:19:17 +05:30
f71b9090aa allocate string for error description only if it really required 2019-05-27 03:02:21 +03:00
08047c174c fix #365, return error for +inf -inf and NaN 2019-05-23 13:57:43 +08:00
68347ec4d6 Merge pull request #366 from stephen-obashitech/master
Fix typo in UnmarshalFromString documentation
2019-05-22 13:56:51 +08:00
0fd91468bb Fix typo in UnmarshalFromString documentation 2019-05-21 12:48:31 +01:00
1bc9828b4f Merge pull request #361 from lggomez/master
Add go module definition
2019-05-19 23:23:02 +08:00
24c3d57281 Add go module definition 2019-04-25 17:40:48 -03:00
0ff49de124 update README 2019-03-06 22:29:09 +08:00
5bc9320502 Merge pull request #316 from proemergotech/master
fix #308 do NOT skip embedded structs without tag when OnlyTaggedFiel…
2019-02-08 15:56:42 +08:00
f64ce68b6e Merge pull request #338 from dvrkps/master
Clean go vet error and ineffassign warnings.
2019-01-14 23:53:30 +08:00
2d42ff74dd Merge pull request #337 from denverdino/fix-encode-with-MarshalJSON
Fix the incompatible encoding #336
2019-01-14 23:52:16 +08:00
3a023a5fbc clean readPositiveFloat64 2019-01-10 18:00:15 +01:00
16aef10b2b clean readPositiveFloat32 2019-01-10 17:55:28 +01:00
ae4c002f78 rename ExampleMyKey 2019-01-10 17:51:25 +01:00
e4aa2ec063 Fix the incompatible encoding 2019-01-03 18:19:22 +08:00
d05f387f50 fix #317, try parse as BigFloat if overflow 2018-11-12 14:45:56 +08:00
a9403d25cd fix #308 do NOT skip embedded structs without tag when OnlyTaggedField is set to true 2018-10-29 12:00:51 +01:00
05d041de10 fix #313 support json marshaller type as map key 2018-10-24 23:28:41 +08:00
5916df66b3 fix #311 handle nil any 2018-10-24 21:05:37 +08:00
2433035e51 Merge pull request #304 from Quasilyte/quasilyte/emptyFallthrough
use multi-value case clause instead of fallthrough
2018-09-14 09:48:43 +08:00
6dfc0bf2dd Merge pull request #305 from Quasilyte/quasilyte/assignOp
simplify `x = x <op> y` to `x <op>= y`
2018-09-14 09:48:25 +08:00
b9be8dd373 Merge pull request #306 from Quasilyte/quasilyte/underef
remove redundant dereferencing expressions
2018-09-14 09:47:54 +08:00
b8d78b6aaf Merge pull request #307 from Quasilyte/quasilyte/commentedOutCode
any_tests: remove commented-out code
2018-09-14 09:47:25 +08:00
7109b5e7dd any_tests: remove commented-out code
Found using https://go-critic.github.io/overview#commentedOutCode-ref
2018-09-13 21:57:53 +03:00
4cc76529e8 remove redundant dereferencing expressions
Found using https://go-critic.github.io/overview#underef-ref
2018-09-13 21:51:59 +03:00
c5ddac9dc3 simplify x = x <op> y to x <op>= y
Found using https://go-critic.github.io/overview#assignOp-ref
2018-09-13 21:48:13 +03:00
f76d712086 use multi-value case clause instead of fallthrough
Found using https://go-critic.github.io/overview#emptyFallthrough-ref
2018-09-13 21:43:37 +03:00
1624edc445 fix #295 decoder more was not compatible with standard library 2018-08-06 14:07:27 +08:00
5d789e5e02 fix #291 omit empty was not handled properly for json raw message 2018-08-06 13:58:33 +08:00
0260c89b54 fix #286 calcHash should use byte not rune to calc hash 2018-08-06 13:23:06 +08:00
10a568c511 fix #293 copy extensions 2018-07-22 11:51:51 +08:00
ab8a2e0c74 fix #276 allow rename when set naming strategy 2018-07-01 15:16:28 +08:00
2fbdfbb595 merge 2018-07-01 13:06:34 +08:00
720ab8dc7f add tests for #283 2018-07-01 13:05:25 +08:00
f2b4162afb Merge pull request #285 from nikhita/fix-case-sensitivity
Fix case sensitivity
2018-06-12 13:28:35 -07:00
3830516ed0 Fix case sensitivity for nested fields 2018-06-12 11:27:24 +05:30
7cceb6c2e3 Merge pull request #282 from caesarxuchao/optional-case-sensitivity
Make case sensitivity optional
2018-06-10 17:13:47 +08:00
b92cf78708 Make case sensitivity optional. Fix
https://github.com/kubernetes/kubernetes/issues/64612
2018-06-07 21:01:05 -07:00
8744d7c5c7 \n should not be ignored in base64 decode 2018-05-26 09:43:29 +08:00
37cc313d18 fix #274, unescape before base64 decode 2018-05-26 09:38:52 +08:00
2ddf6d7582 Merge pull request #266 from ceshihao/fix_base64_with_whitespace
fix base64 contains newline case
2018-04-24 08:46:23 +08:00
6a6742f0a2 fix base64 contains newline characters \r or \n 2018-04-23 23:10:55 +08:00
6c702ce12a fix #264 check io.EOF when test decoder.More 2018-04-20 16:10:56 +08:00
f88871b601 fix #263, support empty string as 0 in fuzz mode 2018-04-18 16:34:54 +08:00
f246f80f14 fix #260, support rename for extra.SupportPrivateFields 2018-04-18 16:28:55 +08:00
51dd70305b add more test for #252 2018-04-18 16:22:47 +08:00
a949c42748 fix #261 should load from reader 2018-04-18 16:11:14 +08:00
f89479f5c0 Merge pull request #257 from ash2k/release-writer
Release writer to enable GC
2018-04-08 08:25:46 +08:00
b858ec296c Release writer to enable GC 2018-04-07 21:40:08 +10:00
885a41a0a6 Merge branch 'master' of https://github.com/json-iterator/go 2018-04-03 13:41:12 +08:00
9e9a97040e always benchmark yourself 2018-04-03 13:41:01 +08:00
fb4d53e4cc Merge pull request #255 from bboreham/error-test
Add a test for input errors, and fix one bug that it finds
2018-04-02 13:50:44 +08:00
b53656d459 Check that a struct ends with closing brace 2018-04-01 22:02:44 +00:00
8f27a81d90 Add a test for input errors
Send various malformed JSON strings into the decoder for each type,
and check we get an error each time.
2018-04-01 22:01:21 +00:00
4930b053b8 explit test case sensitive for #252 2018-03-24 22:38:32 +08:00
06e0f9391e fix #250 case insensitive field match 2018-03-20 21:43:30 +08:00
ca39e5af3e suport encode map[interface{}]interface{} 2018-03-15 21:28:16 +08:00
39acec93e0 expose DecoderOf and EncoderOf 2018-03-14 23:18:20 +08:00
25fa392355 fix #245, always reuse existing value even UseNumber 2018-03-01 19:23:20 +08:00
d51e841de0 fix build on 1.8 2018-02-28 20:29:23 +08:00
3353055b2a use concurrent.Map for 1.8 support 2018-02-28 17:11:57 +08:00
455b3f8bb8 move reflect2 from plz to modern-go 2018-02-28 17:09:30 +08:00
2a93f9003e fix #244 use BinaryAsStringExtension to make []byte pretty, while the output is valid json, but it can not be decoded by other json codec, as \x01 is decoded as \x01 by them, which is not original input 2018-02-27 12:40:48 +08:00
9472474ffd test []byte behavior 2018-02-27 12:04:11 +08:00
ad83167dc6 fix #243 fuzzy decoder should take null as valid input 2018-02-26 23:22:22 +08:00
fff342fd04 gofmt 2018-02-24 22:04:41 +08:00
8d6662b81b fix #242 add CreateMapKeyEncoder and CreateMapKeyDecoder to extension spi 2018-02-24 22:04:11 +08:00
a377e2656b add map key example 2018-02-23 18:20:14 +08:00
0ac74bba4a upgrade reflect2 2018-02-23 08:20:31 +08:00
ebe943a4a6 fix #241, support 32bit platform 2018-02-23 08:12:45 +08:00
414d0307c9 fix struct decoder report error 2018-02-22 13:30:59 +08:00
86e9fd72bc update pkg 2018-02-22 11:48:56 +08:00
be70f29b04 detect remaining bytes 2018-02-22 10:37:32 +08:00
a3fdd37b9a use sync.Pool 2018-02-22 10:29:29 +08:00
d346ea6e55 get encoder without get type first 2018-02-22 10:20:19 +08:00
820ec30bd6 get decoder without get type first 2018-02-22 10:18:27 +08:00
df8295a48a fix 1.8 2018-02-22 10:13:38 +08:00
99fc16a363 use reflect2 to replace reflect 2018-02-22 10:12:08 +08:00
a3866383f5 support recursive struct 2018-02-21 17:59:41 +08:00
2fcbb23d96 rewrite how eface and iface are handled 2018-02-21 12:16:50 +08:00
ea6403326b fix #239, empty slice 2018-02-21 07:24:22 +08:00
404d90796f move type declaration into separate files 2018-02-20 23:08:58 +08:00
b79587753b move any codec 2018-02-20 23:04:04 +08:00
63ea5e3131 move encoder/decoder of native 2018-02-20 22:55:31 +08:00
895a19f2dc move json raw message 2018-02-20 22:38:35 +08:00
6327145300 move json number impl 2018-02-19 23:13:33 +08:00
c99d73acd0 rename 2018-02-19 23:08:01 +08:00
3d39af6dd9 remove feature prefix 2018-02-19 23:04:25 +08:00
a016e87b9f move any codec 2018-02-19 23:01:19 +08:00
08218647c3 use reflect2 to implement map decoder 2018-02-19 22:53:42 +08:00
d6f02cbd48 remove sliceHeader 2018-02-19 14:39:57 +08:00
b53aa13eb0 rename 2018-02-19 14:30:44 +08:00
e322da5531 rename 2018-02-19 14:30:23 +08:00
1d41f3c0ed only consider ptr type if not root 2018-02-19 14:30:01 +08:00
29604bf5c3 use reflect2 decode slice 2018-02-19 14:18:42 +08:00
cbc1786a76 change slice encoder to use reflect2 2018-02-18 23:27:34 +08:00
5a696808d6 fix any codec 2018-02-18 22:57:01 +08:00
d8e64aa825 support TextMarshaler as map key 2018-02-18 22:49:06 +08:00
577ddede74 use extension to implement configs 2018-02-18 21:14:37 +08:00
43d9384d67 fix marshaler support for iface case 2018-02-18 21:05:42 +08:00
2074f25bd3 use extension to implement EscapeHtml config option 2018-02-17 22:55:35 +08:00
ef3038593b check nil for interface{} 2018-02-17 22:33:09 +08:00
9dafbc667f when embedded ptr is nil, the fields should be omitted 2018-02-16 17:32:41 +08:00
a7a34507ab use reflect2 for json.Marshaler 2018-02-16 15:42:37 +08:00
0e2b54800a remove EncodeInterface 2018-02-14 15:04:23 +08:00
e7c7f3b337 fix coverage 2018-02-14 14:06:32 +08:00
75810179f6 remove n from stream 2018-02-14 13:58:51 +08:00
6a8f9fa342 Merge branch 'float-allocs' of git://github.com/brian-brazil/go into 1.1 2018-02-14 11:37:27 +08:00
24bb2eee9f fix #236 case sensitive when both upper case and lower case presents 2018-02-14 11:33:17 +08:00
64cc784089 remove special handling for field 2018-02-14 10:31:55 +08:00
477be43d00 consolidate more tests 2018-02-14 10:13:34 +08:00
a8708bca85 consolidate more tests 2018-02-14 08:58:59 +08:00
658ff9ef15 consolidate more tests 2018-02-14 08:48:12 +08:00
64c1c67885 consolidate more tests 2018-02-14 08:39:18 +08:00
e3bc511e5a consolidate more tests 2018-02-14 08:28:17 +08:00
8fa357ab7b consolidate mor tests 2018-02-13 23:49:40 +08:00
761ce8cce2 consolidate more tests 2018-02-13 20:58:29 +08:00
c3b6c1e845 consolidate skip tests 2018-02-13 20:41:21 +08:00
0ed9de94f2 support asymmetric tests 2018-02-13 20:25:27 +08:00
6fded6eb5f consolidate struct tags tests 2018-02-13 17:22:47 +08:00
dc3395f770 consolidate struct tests 2018-02-13 17:06:28 +08:00
bd4e013f98 consolidate slice tests 2018-02-13 16:20:08 +08:00
48a4a1e4db consolidate map tests 2018-02-13 16:07:14 +08:00
9bc223734a consolidate marshaler tests 2018-02-13 16:00:08 +08:00
eb9aeccee2 consolidate builtin tests 2018-02-13 15:48:39 +08:00
28adca2a14 consolidate array test 2018-02-13 15:43:10 +08:00
a9b3f36b2f add test framework 2018-02-13 15:32:21 +08:00
1e8e785321 Remove allocs from WriteFloat32/WriteFloat64
The use of strconv.FormatFloat causes a string allocation,
by setting aside a reusable buffer and using strconv.AppendFloat
this can be avoided.

Before:
BenchmarkRespond-4           300           5392189 ns/op          618936 B/op      20010 allocs/op

After:
BenchmarkRespond-4           300           4713746 ns/op          139744 B/op         10 allocs/op

This benchmark is using a custom encoder that calls WriteFloat64 20k
times, which is the bulk of the work.
2018-02-07 17:30:57 +00:00
002b5ae342 fix tests 2018-02-05 23:45:42 +08:00
07f99a1124 fix build 2018-02-05 23:05:57 +08:00
71f74dc71e implement #230 DisallowUnknownFields option added 2018-02-05 23:03:53 +08:00
7990317be5 gofmt 2018-02-05 22:45:04 +08:00
9edd73f752 fix build 2018-02-05 22:26:39 +08:00
3d5ee1098a Merge branch 'master' of https://github.com/json-iterator/go 2018-02-05 21:43:52 +08:00
ee8cfb7547 cache frozenConfig 2018-02-05 21:43:37 +08:00
bca911dae0 Update README.md 2018-01-28 22:27:09 +08:00
28452fcdec cow cache is not same, as map read will modify the underlying map. use sync.Map for 1.9 and above, and mutex if sync.Map not available 2018-01-28 17:00:11 +08:00
ea8c33040f fix #228 2018-01-27 16:25:48 +08:00
358cfc3929 Merge branch 'master' of https://github.com/json-iterator/go 2018-01-25 14:48:02 +08:00
c39a632e65 fix #227, fix empty json.Number 2018-01-25 14:47:50 +08:00
e31252f2e2 Merge pull request #225 from mgood/empty-array-fix
Fix encoding 0-length arrays
2018-01-23 23:31:07 +08:00
807e4a8b20 Optimize 0-length array case
Instead of checking the array length in encode, this can be checked up
front in `encoderOfArray` since the array type has a fixed length
determined at compile time. So return an `emptyArrayEncoder` that simply
writes an empty array to the stream.
2018-01-22 14:03:50 -08:00
e78b7e89b6 Merge branch 'master' of https://github.com/json-iterator/go 2018-01-21 20:59:32 +08:00
945d1aaa19 fix #140 uintptr will no lock the address from gc 2018-01-21 20:59:18 +08:00
ba3857729b Fix encoding 0-length arrays
The array encoder assumed that arrays had at least one value, so it
would serialize them with a zero-value for the array, such as `[0]`.

This adds a test to reproduce the issue, and updates the encoder to
write an empty array if the length is 0.
2018-01-16 11:02:03 -08:00
c3ed5e85e0 Merge pull request #222 from neverlee/mydev
加入一个OnlyTaggedField选项
2018-01-09 18:30:25 +08:00
c27f6f9350 config: add OnlyTaggedField config, only process tagged fields in struct 2018-01-09 17:29:47 +08:00
0ab880662f fix #219 should check real value for empty instead of just the pointer for nested field 2018-01-07 13:57:46 +08:00
6dad2de6cc fix build 2018-01-04 17:18:16 +08:00
11c1cce0d8 fix #217 when input is null, non-decodable type should not be considered as error, to be compatible with stdlib 2018-01-04 16:19:26 +08:00
96fcb84835 fix #215 lazy load more 2017-12-23 10:52:17 +08:00
e7a8aea845 Merge branch 'master' of https://github.com/json-iterator/go 2017-12-21 22:18:40 +08:00
60a9df5ebc fix #214 report EOF like stdlib 2017-12-21 22:18:28 +08:00
7b060ec866 Merge pull request #210 from coocood/master
add ReadNumber for Iterator.
2017-12-18 08:22:47 +09:00
25f147f530 add ReadNumber for Iterator. 2017-12-17 16:44:04 +08:00
a9b9c73b4d fix #207 delay unsupported type error reporting 2017-12-15 10:13:11 +08:00
e0df39fda2 fix #206, do not allow nil pointer as unmarshal input 2017-12-14 17:18:05 +08:00
13f86432b8 do not use defer() in read int 2017-12-12 18:52:41 +08:00
d2a7335211 fix #202 #203 #204 map encoder not proplery initialized 2017-12-08 21:18:59 +08:00
b2a706d14b reverse last commit, need a better fix 2017-12-08 06:15:49 +08:00
23078876c5 fix #203 consider MarshalJSON as non empty 2017-12-07 23:20:43 +08:00
051434fab7 fix #198, use dep for vendoring 2017-11-30 10:42:24 +08:00
be6688fc1a fix #200, do not use symbolic link in the code 2017-11-30 10:34:05 +08:00
ff2b70c1db support config level extension 2017-11-23 00:09:35 +08:00
f7279a603e fix out of range 2017-11-15 23:34:21 +08:00
9f088cbcc4 fix #195 when decode float as int, report it clearly 2017-11-15 23:25:12 +08:00
3c0e5762c4 fix #196 do not hard code 1 << 49 2017-11-15 23:15:31 +08:00
d394a135a1 #197 fix place holder encoder to use EncodeInterface, WriteToStream is unsafe when the real encoder is unknown 2017-11-15 22:56:23 +08:00
9fddff05f0 try to fix #194 with larger array 2017-11-11 08:31:44 +08:00
b1b003864e expose OptionalEncoder&OptionalDecoder; add attachment to Stream&Iterator for customized decoder/encoder 2017-11-08 11:41:45 +08:00
aed5a81f09 fix #190 handle empty input 2017-10-31 22:47:02 +08:00
f1258b01aa fix #191 do not always assume the object field is simple string 2017-10-31 22:38:41 +08:00
fbd210edfc Merge pull request #189 from ggaaooppeenngg/compatible-with-map
Fix standard compatiblility
2017-10-26 18:39:38 -05:00
640251ab91 Fix standard compatiblility
Non-nil but empty map with omitempty should be ignored.

Signed-off-by: Peng Gao <peng.gao.dut@gmail.com>
2017-10-27 01:43:41 +08:00
06b2a7cf1d Merge pull request #188 from ggaaooppeenngg/compatible
Fix standard compatiblility
2017-10-26 06:41:01 -05:00
5fffb9b8f7 Fix standard compatiblility
Encode has trailing newline at the end.

Signed-off-by: Peng Gao <peng.gao.dut@gmail.com>
2017-10-26 15:15:36 +08:00
7e3b776024 change jsoniter-sloppy to jsoniter_sloppy 2017-10-23 15:03:44 +08:00
6240e1e798 #185 add jsoniter.Valid 2017-10-10 08:57:02 +08:00
0149a5cf4a fix #183 error message not only show expectation, but also the actual value 2017-10-09 08:24:51 +08:00
5068c8baaf #183 limit error message size 2017-10-09 08:16:52 +08:00
16f78601b5 fix #184, support null as number 2017-10-07 09:29:32 +08:00
8f50a91be2 fix #181, support string as json.Number and jsoniter.Number 2017-10-06 18:08:14 +08:00
73c7bc881e fix #180, add missing methods to jsoniter.Number 2017-10-06 17:56:36 +08:00
4de15a3a87 Merge pull request #182 from MOZGIII/patch-1
Used writeTwoBytes in Stream.WriteEmptyArray
2017-10-04 02:25:32 -05:00
14b28b2226 Used writeTwoBytes in Stream.WriteEmptyArray 2017-10-03 22:14:36 +03:00
abe3c4016b fix #179 2017-09-26 15:35:55 +08:00
dbb1ef3f63 #177 flush buffer should check available again 2017-09-21 21:04:45 +08:00
46b20bbbec #178 SkipAndReturnBytes should return copy of memory 2017-09-21 20:18:45 +08:00
fdfe0b9a69 Merge branch 'olegshaldybin-skip-unexported-fields' 2017-09-19 10:06:59 +08:00
faa3dcf46a do not report error when field is unexported 2017-09-19 10:06:34 +08:00
1f58120d43 Always skip unexported fields when encoding
Skip creating encoders for unexported fields. They are not participating
in JSON marshaling anyway. This allows using unexported fields of
non-marshalable types in structs.

As a side-effect of this change it's no longer possible to marshal
unexported JSON fields by adding a custom type extenstion. It seems this
is desired behavior since it matches standard library and jsoniter
already disallows `json:"-"` fields from participating in custom
extensions.

Fixes #174.
2017-09-18 11:02:15 -07:00
6ed27152e0 Update README.md 2017-09-17 16:07:42 +08:00
3c298d8a76 Merge pull request #172 from olegshaldybin/more-stdlib-compat
Improve stdlib compatibility
2017-09-17 03:05:36 -05:00
9f6e5962a9 Improve stdlib compatibility
1. Null values for primitive types no longer clear the original value in
the destination object.

2. Dereference multiple levels of pointers in the destination interface{}
type before unmarshaling into it. This is needed to match stdlib
behavior when working with nested interface{} fields. If the destination
object is a pointer to interface{} then the incoming nil value should
nil out the destination object but keep the reference to that nil value
on its parent object. However if the destination object is an
interface{} value it should set the reference to nil but keep the
original object intact.

3. Correctly handle typed nil decode destinations.
2017-09-16 16:57:51 -07:00
c463aa12c4 Merge pull request #173 from toffaletti/more-nil-interface-fixes
More nil interface fixes
2017-09-16 18:36:43 -05:00
b5d2607a6d replace should.Equal(nil, err) with should.NoError(err) 2017-09-16 16:30:04 -07:00
48cc4d965a improve test 2017-09-16 16:27:32 -07:00
c59c42fda0 fix decoding of nil non-empty interface 2017-09-16 16:24:55 -07:00
8324374402 add tests for decoding nil interfaces 2017-09-16 16:24:27 -07:00
2017f3866b fix encoding of nil marshaler interface 2017-09-16 16:08:32 -07:00
ddc5af4512 fix encoding of nil non-empty interface 2017-09-16 16:04:36 -07:00
2f7e5c8dd7 add failing tests for nil non-empty interfaces 2017-09-16 16:00:48 -07:00
92772579dd Merge pull request #170 from olegshaldybin/marshal-enum-pointer
Fix custom marshaler for enum types
2017-09-15 09:08:45 -05:00
ae57d167e8 Fix custom marshaler for enum types
When MarshalJSON was defined on a pointer receiver custom enum type
marshaling/unmarshaling was panicing since the underlying primitive type
was treated as a pointer.

Since method set for pointer receivers includes value receiver methods
we don't really need optionalEncoder and can just use marshalEncoder
directly.
2017-09-14 23:26:12 -07:00
eef35e549b Merge pull request #169 from toffaletti/fix-nil-interface
Fix handling of nil empty interface
2017-09-15 00:45:11 -05:00
005d86dc44 fix handling of nil empty interface 2017-09-14 21:32:42 -07:00
e658f6597a add failing test for handling of nil interface with omitempty 2017-09-14 20:44:42 -07:00
f8eb43eda3 Merge pull request #168 from olegshaldybin/null-booleans
Allow null booleans
2017-09-14 20:58:31 -05:00
18a241d40b Allow null booleans
Make sure we do the same thing as stdlib with null booleans by not
touching the original value and discarding the null.

Another somewhat related change is nulling out null interface values in
the original structure. This also matches stdlib behavior.
2017-09-14 16:47:35 -07:00
0fdf883ac0 Merge pull request #167 from olegshaldybin/shorter-sleep
Shorter sleep while waiting for encoder/decoder
2017-09-14 18:23:29 -05:00
34fbec74ad Shorter sleep while waiting for encoder/decoder
If the client is using the same jsoniter config with multiple goroutines
it's very likely that few initial operations will encounter a placeholder
encoder/decoder while the real one is being created by another
goroutine. Having a full second sleep seems too conservative, since
encoder/decoder will be created in a very short time. This is very easy
to reproduce in any real environment with a few concurrent requests of
the same type. A few initial requests will have 1s+ response time.

Changing to 10ms should smooth out marshal/unmarshal times for these
initial concurrent requests.
2017-09-14 12:37:47 -07:00
90574c5ca3 #166 support ValidateJsonRawMessage in ConfigCompatibleWithStandardLibrary 2017-09-14 23:54:40 +08:00
6a4ba7bfa9 Merge branch 'master' of https://github.com/json-iterator/go 2017-09-09 08:46:07 +08:00
0828e559d0 #164 support interface{} with ptr 2017-09-09 08:45:57 +08:00
2c67d0f68a Merge pull request #163 from dvrkps/patch-2
travis: add 1.x to go versions
2017-09-07 11:25:07 -05:00
f29a0391bc travis: add 1.x to go versions 2017-09-07 17:12:42 +02:00
374e68a144 Merge pull request #162 from cch123/fix-bool-to-number
fix fuzzy decoder from bool value to number
2017-09-06 00:19:18 -05:00
b134d86290 optimize code 2017-09-06 13:18:05 +08:00
bc3221879d fix fuzzy decoder from bool value to number 2017-09-06 12:31:56 +08:00
8c7fc7584a #159 fix fuzzy decoder, the newIter assigned io.EOF error to original iterator, which stopped further processing 2017-09-06 00:31:25 +08:00
db32ee8c2d #157 number can be null 2017-09-05 13:00:03 +08:00
d80309af3b #156 invoke Marshaler defined on pointer types 2017-09-01 15:44:12 +08:00
36b14963da #153 fix invalid utf8 using same implementation as the standard library 2017-08-29 23:58:51 +08:00
f706335302 #153 fix critical bug: infinite loop when write string is invalid utf8 2017-08-29 23:39:43 +08:00
2dc0031b26 #152 gofmt 2017-08-25 12:53:23 +08:00
cdbd2ed810 #145 interface {} customizatoin is recursive 2017-08-22 10:39:01 +08:00
39e9d67807 Merge branch 'master' of https://github.com/json-iterator/go 2017-08-22 00:12:18 +08:00
2066b01acb #146 support config TagKey 2017-08-22 00:12:09 +08:00
ac3b3cd160 test []interface{} 2017-08-21 22:43:51 +08:00
887789156a Merge pull request #147 from thockin/output_tests
Add tests for int64
2017-08-11 12:55:48 +08:00
7df5a67d0d Add tests for int64 2017-08-10 20:58:49 -07:00
9c358632dc #144 make []byte support Unmarshaler&Marshaler 2017-08-09 13:59:40 +08:00
1cfa233923 #143 make jsoniter.Number same meaning as json.Number, however UseNumber still returns json.Number. 1.9 alias support should be added later 2017-08-05 07:22:53 +08:00
d249b05a85 rename ValueType, to avoid collision with json.Number 2017-08-05 07:10:15 +08:00
abbd16da6c #140 blind fix 2017-08-02 09:20:43 +08:00
b67201557a avoid gc issue 2017-08-01 08:34:38 +08:00
5124683f24 #140 try fix: maybe memory collected before assigned to existing object graph 2017-07-31 23:24:58 +08:00
4892de725b add ad 2017-07-31 21:49:02 +08:00
34a2174be3 #142 decode struct field should be case insensitiveyet another fix 2017-07-31 21:48:22 +08:00
24ecaff2a1 #142 decode struct field should be case insensitive, the bug only happen for struct with more than 10 fields 2017-07-31 20:50:07 +08:00
c15b4d116c #139 unmarshal non base64 into []byte 2017-07-19 12:04:22 +08:00
12cd299fa8 add benchmark for Skip() 2017-07-19 00:22:41 +08:00
60ba332980 acknowledge @mattn for #138 #137 #136 #135 2017-07-19 00:09:50 +08:00
f705934fbf #138 fix - without following digits; fix 1.e1 2017-07-18 23:48:40 +08:00
17a26a6e20 remove debug print 2017-07-18 23:24:21 +08:00
156284b028 #137 fix unicode surrogate incompatibility 2017-07-18 23:17:52 +08:00
6b6938829d #136 strconv.ParseFloat can not validate 1. , added extra validation for this special case 2017-07-18 22:19:52 +08:00
e066e54964 #135 verify 1e1 and 1.0e1 is handled same as std 2017-07-18 11:28:19 +08:00
18d6ae2668 #135 fix leading zero 2017-07-18 11:23:29 +08:00
c966eaa031 #135 fix double negative 2017-07-18 11:05:39 +08:00
f6da8e62c3 #133 validate json when Skip() 2017-07-18 09:45:25 +08:00
5eded4f6ae implement skip number and string strictly 2017-07-18 09:01:43 +08:00
9b3ec40fd9 #133 fix empty struct skip; fix ] as empty array 2017-07-17 09:09:00 +08:00
0d604da7d7 Merge pull request #134 from thockin/output_tests
Add a fuzz test for non-JSON input
2017-07-17 08:47:36 +08:00
b6ace7d51b Add a fuzz test for non-JSON input 2017-07-16 17:24:46 -07:00
6a4fbb9892 ensure buffer flushed to io.Writer 2017-07-16 10:47:24 +08:00
4ae426c4b7 Merge branch 'master' of https://github.com/json-iterator/go 2017-07-15 18:09:14 +08:00
b46d0a2324 make test faster 2017-07-15 18:09:06 +08:00
8b03604184 Merge pull request #131 from cch123/feature-increase-coverage
update conversion table, add string escape test
2017-07-13 19:01:45 +08:00
93ce14316d increase coverage 2017-07-13 15:32:26 +08:00
779c3e2164 update conversion table 2017-07-13 00:13:04 +08:00
4b33139ad0 #130 loadMore should use iter.captured 2017-07-12 17:56:51 +08:00
dc388588a3 Merge branch 'master' of https://github.com/json-iterator/go 2017-07-12 16:40:14 +08:00
bd4364ab7c #129 fix read map with reader, should use ReadMapCB instead of ReadObjectCB 2017-07-12 16:40:05 +08:00
b9dc3ebda7 Merge pull request #128 from carlcarl/fix-json-use-number
Fix #123, `UseNumber` not works with iterator
2017-07-12 07:04:30 +08:00
90137b4a60 Use readNumberAsString 2017-07-12 00:23:49 +08:00
be9d4ded4f Use json.Number as the return 2017-07-12 00:11:50 +08:00
7b1fd129cf Add test for iterator UseNumber 2017-07-11 23:39:09 +08:00
b91b7ac682 Fix #123, make iterator read int if using number 2017-07-11 22:07:08 +08:00
845d8438db #126 fix space in case map key is sorted 2017-07-11 01:07:18 +08:00
d37197e176 #126 add space between map key and value when MarshalIndent 2017-07-10 22:14:11 +08:00
45c22b130b Merge branch 'master' of https://github.com/json-iterator/go 2017-07-10 15:24:04 +08:00
4a84b0b30e Merge branch 'liggitt-malformed-string-test' 2017-07-10 15:23:52 +08:00
0187038bad check null/true/false 2017-07-10 15:23:35 +08:00
c38e47d169 control character in string is invalid 2017-07-10 15:13:31 +08:00
b27718d16b Merge pull request #125 from liggitt/exponents
Fix exponent parsing
2017-07-10 14:51:10 +08:00
a447a8f797 Add tests for malformed string input 2017-07-10 02:44:15 -04:00
0d6dae80e1 Fix exponent parsing 2017-07-10 02:06:37 -04:00
d336ee6da6 fix build 2017-07-09 16:28:35 +08:00
3606750b83 document public symbols 2017-07-09 16:26:30 +08:00
db3f5046d7 remove GetObject & GetArray from Any 2017-07-09 16:15:45 +08:00
f0487718f6 document public symbols 2017-07-09 16:09:23 +08:00
46574e7d09 document public symbols 2017-07-09 15:23:18 +08:00
3a6ecf051c make receiver names consistent 2017-07-09 15:11:24 +08:00
5862c51768 extract out feature_reflect_struct_decoder 2017-07-09 15:07:53 +08:00
ce479f3476 fix golint: document exported symbols 2017-07-09 14:57:49 +08:00
bede1d7f40 fix build; add document for exported symbols 2017-07-09 14:48:34 +08:00
d3448d3dbd fix golint: document exported symbols 2017-07-09 14:21:12 +08:00
8fbed91768 fix golint: document exported symbols 2017-07-09 14:17:40 +08:00
3b6853d209 fix golint: do not export test types 2017-07-09 14:12:58 +08:00
4351a2e6e9 fix golint: do not export test types 2017-07-09 11:55:58 +08:00
891d33b415 fix golint: do not export test types 2017-07-09 11:40:45 +08:00
ad20f12c34 fix golint: do not export test types 2017-07-09 11:33:03 +08:00
9ecb1fd36d fix go vet 2017-07-09 11:24:26 +08:00
6d0e6f3733 fix go report card 2017-07-09 11:12:37 +08:00
711f836582 fix go report card 2017-07-09 11:10:44 +08:00
37ba1b32b5 Merge branch 'javierprovecho-master' 2017-07-09 01:01:13 +08:00
2c10d8e6bb test(object): add test for ignored field on not valid type 2017-07-08 16:50:11 +02:00
aaf6160146 fix(reflect): don't process ignored struct fields 2017-07-08 16:50:05 +02:00
b1afefe058 Merge pull request #119 from cch123/feature-increase-coverage
user defined simple type test
2017-07-07 21:43:33 +08:00
3bb49c1e47 add type def marshal/unmarshal test 2017-07-07 19:01:53 +08:00
eb68fff85c Merge pull request #118 from cch123/feature-increase-coverage
add int/int8/int32/int64 overflow test
2017-07-07 18:49:35 +08:00
e07a4ca5ec add int/int8/int32/int64 overflow test 2017-07-07 17:14:52 +08:00
dfa4bdf888 merge 2017-07-07 09:14:24 +08:00
b74ffb2e03 import github.com/stretchr/testify/require 2017-07-07 09:13:25 +08:00
a46060dedc Merge pull request #117 from cch123/feature-increase-extra-coverage
increase extra coverage
2017-07-06 21:09:34 +08:00
5eadecbb66 increase extra coverage 2017-07-06 20:44:38 +08:00
5bc013d6a3 merge 2017-07-06 16:06:30 +08:00
f7df62f1b5 #115 check object end 2017-07-06 16:04:52 +08:00
07f423d248 Merge pull request #114 from cch123/feature-increase-coverage
increase reflect object coverage, perhaps need to optimize in the future
2017-07-06 15:44:56 +08:00
d4c0cb2986 increase reflect object coverage, need optimize in the future 2017-07-06 15:31:35 +08:00
84ed6b3caf Merge pull request #113 from cch123/feature-increase-coverage
increase coverage
2017-07-06 12:27:18 +08:00
ee6536c50a increase coverage 2017-07-06 11:44:39 +08:00
b6eb62e96b Merge pull request #112 from cch123/feature-increase-coverage
increase coverage
2017-07-05 20:42:42 +08:00
8675af13bf increase coverage 2017-07-05 20:30:54 +08:00
21ca11f96a Merge pull request #111 from cch123/feature-increase-coverage
fix codecov yaml ignore
2017-07-05 19:24:44 +08:00
dd88d25090 add require 2017-07-05 18:59:28 +08:00
6a289f32c2 fix codecov.yml curl https://codecov.io/validate --data-binary @.codecov.yml 2017-07-05 16:49:42 +08:00
4907dc00f6 change codecov file 2017-07-05 16:41:24 +08:00
2350982504 Merge pull request #109 from cch123/feature-increase-coverage
increase coverage
2017-07-05 16:18:34 +08:00
27725b7139 update codecov.yml 2017-07-05 15:17:39 +08:00
ca6a524d4f add codecov.yml ignore output tests 2017-07-05 14:36:15 +08:00
1de44419ea increase coverage 2017-07-05 13:55:10 +08:00
550531a046 increase coverage 2017-07-05 11:40:20 +08:00
1745078ab7 Merge pull request #108 from cch123/feature-add-string-tests
add string convert tests
2017-07-05 07:20:29 +08:00
6129e85d53 increase coverage 2017-07-05 01:21:33 +08:00
ee3313111c add string tests 2017-07-05 00:39:20 +08:00
4e65952c09 fix float convert 2017-07-05 00:23:00 +08:00
3829a470ae Merge pull request #106 from cch123/feature-add-int-tests
fix negative number to uint
2017-07-04 22:37:09 +08:00
3f35bed884 simplify float convert 2017-07-04 22:28:24 +08:00
8d7efe886c Merge pull request #107 from cch123/feature-add-convert-table-doc
add convert table doc
2017-07-04 21:07:43 +08:00
f245011c7d add any to float 2017-07-04 19:59:34 +08:00
4ea96ac7c3 change all negative convert to uint 0 2017-07-04 18:48:55 +08:00
50beb4f15d update fuzzy convert table 2017-07-04 17:05:39 +08:00
e5d7a65616 add convert table 2017-07-04 16:38:07 +08:00
d7b6b4e0bb add convert table document 2017-07-04 15:29:47 +08:00
712ddb1942 fix negative number to uint 2017-07-04 14:00:24 +08:00
ca8dd93d0b Merge pull request #105 from cch123/feature-add-int-tests
add any to int/uint test
2017-07-04 08:09:48 +08:00
ac8dd56dfb object cannot covert to int/float in php, so change covert result to zero 2017-07-04 00:53:10 +08:00
d8dbf14af4 add array to int 2017-07-04 00:29:19 +08:00
402c6c79e2 Merge branch 'master' of https://github.com/json-iterator/go into feature-add-int-tests 2017-07-04 00:20:16 +08:00
2e10d5fdad add basic int test 2017-07-04 00:19:41 +08:00
f0b07a2313 Merge pull request #104 from cch123/feature-add-bool-convert-test
add bool convert test map
2017-07-03 20:00:29 +08:00
919a2eff5c fix bool test 2017-07-03 19:40:12 +08:00
a743df1b8a add bool convert test map 2017-07-03 19:10:49 +08:00
8700644196 cut 0.9.18 2017-07-03 10:17:02 +08:00
6a7bf91c45 only test against 1.8 2017-07-02 19:26:06 +08:00
e417330822 fuzzy test from 1000 to 100 2017-07-02 18:18:12 +08:00
af876518e1 remove 1.6 and 1.7 2017-07-02 15:25:28 +08:00
21b9254da4 skip t.Run below 1.8 2017-07-02 15:20:18 +08:00
c009421781 fix write float compatibility 2017-07-02 15:11:36 +08:00
c4f54740f7 separate 1.8 tests 2017-07-02 14:27:16 +08:00
368bd0c1d8 fix 1.6 compatibility 2017-07-02 13:22:55 +08:00
11975d2a26 test with more go versions 2017-07-02 13:18:20 +08:00
e7404f3065 add badges 2017-07-02 13:10:00 +08:00
f60a6a17c2 test and cover multiple packages 2017-07-02 12:58:14 +08:00
2dfdcdd9db add travis.yml 2017-07-02 12:09:49 +08:00
496a8ea225 list contributors 2017-07-02 11:59:11 +08:00
ed6c434851 gofmt 2017-07-02 11:56:01 +08:00
bf002a02be #102 create correct type when pointer to Marshaler/Unmarshaler is nil 2017-07-02 11:35:30 +08:00
815aa331a8 #101 checkIsEmpty can not reuse createEncoderOfSimpleType, otherwise it will fail when struct member is not serializable 2017-07-02 11:13:21 +08:00
54ab168362 #99 support uintptr 2017-07-02 11:03:13 +08:00
87d1ea0c60 Merge pull request #100 from thockin/output_tests
Output tests for text marshal and uintptr
2017-07-02 07:41:50 +08:00
6268a1cbc8 add test for uintptr 2017-07-01 10:57:42 -07:00
d56566286c Add tests for omitempty and text methods 2017-07-01 10:28:59 -07:00
f2c50ef73b #97 omit empty behavior should follow the original type 2017-07-02 00:41:23 +08:00
d0c5988985 Merge pull request #98 from thockin/output_tests
Add tests for omitempty and json methods
2017-07-02 00:15:51 +08:00
17eed15fb5 Add tests for omitempty and json methods 2017-07-01 08:26:42 -07:00
e260979001 #95 struct and empty struct is considered as notempty 2017-07-01 11:48:17 +08:00
3cf9b7c253 Merge pull request #96 from thockin/output_tests
Add test for omitempty on struct
2017-07-01 11:33:17 +08:00
800df52ccd Add test for omitempty on struct 2017-06-30 20:32:27 -07:00
e3ba0e7b7e #93 fix omitempty within embedded struct 2017-07-01 09:27:48 +08:00
e366c72b81 Merge pull request #94 from thockin/output_tests
Add test for embedded+omitempty
2017-07-01 09:01:13 +08:00
69551ef38f Add test for embedded+omitempty 2017-06-30 15:16:42 -07:00
abcf9a8d76 fix tests 2017-07-01 00:35:19 +08:00
fa0965a968 #90 trim spaces when UnmarshalJSON 2017-07-01 00:33:42 +08:00
04eae11ba5 #91 fix one ptr embedded struct 2017-07-01 00:09:40 +08:00
ea8fa7cc63 #91 fix embedded and , 2017-07-01 00:00:38 +08:00
6540266aaf Merge pull request #92 from thockin/output_tests
Better tests for field name tags
2017-06-30 23:53:02 +08:00
ddfbb0c62e better tests for field name tags 2017-06-30 08:16:43 -07:00
a3a2d1cd25 #87 fix embedded field sorting order 2017-06-30 14:01:50 +08:00
401a56bc20 #88 #85 support embedded struct with json tags 2017-06-30 13:26:50 +08:00
cfaa11f837 Merge pull request #89 from thockin/output_tests
More output tests
2017-06-30 13:21:54 +08:00
c1411e0ad5 Add test for json tag on embedded field 2017-06-29 22:01:16 -07:00
9ec64591b6 Enhance test for overlap and embedded 2017-06-29 22:01:16 -07:00
b07d1abc4f Add output_test for partial unmarshal 2017-06-29 22:01:14 -07:00
79c4040505 sync up some straggler json_tests 2017-06-29 22:00:39 -07:00
7fa780bd5d Uncommit files accidentally added 2017-06-29 22:00:27 -07:00
7244d730b9 Merge pull request #84 from thockin/output_tests
Output tests for embedded marshalers and arrays
2017-06-29 22:51:46 +08:00
12be6e0d43 Add tests for arrays 2017-06-29 07:25:19 -07:00
3cfe590a13 Add output test for embedded marshalers 2017-06-29 07:14:33 -07:00
678c297af3 #75 support MarshalIndent 2017-06-29 20:48:27 +08:00
1253b8edd3 gofmt 2017-06-29 20:40:25 +08:00
fb382c0ec1 fix -, 2017-06-29 20:40:07 +08:00
09cb1d9236 #81 handle field name conflict properly 2017-06-29 20:34:40 +08:00
e6c24947ee array is just like struct, one element case special for interface{ 2017-06-29 18:58:40 +08:00
a6673c983a slice embedded in struct will not be optimized by interface{} 2017-06-29 18:52:03 +08:00
ec7b40d104 update ignore 2017-06-29 18:45:20 +08:00
84fa033353 #80 fix the case when embedded struct ptr is nil 2017-06-29 18:45:11 +08:00
4e608af2c7 #80 fix embedded builtins 2017-06-29 10:45:29 +08:00
3458ccdb20 Merge pull request #83 from thockin/output_tests
Methods on aliased types are not valid map keys
2017-06-29 09:43:12 +08:00
45ccfb031f Methods on aliased types are not valid map keys 2017-06-28 09:38:15 -07:00
545a32f2a1 #76 support TextUnmarshal 2017-06-29 00:14:55 +08:00
08dbc98040 #76 fix UnmarshalJSON 2017-06-28 23:55:32 +08:00
82dabdcdbf #77 support -, 2017-06-28 23:47:32 +08:00
76e62088df #78 not null pointer is considered not empty 2017-06-28 23:39:46 +08:00
faaa59222a #79 fix string tag on string field 2017-06-28 23:37:10 +08:00
91ef89a6a2 WIP: MarshalIndent 2017-06-28 23:22:05 +08:00
3e3caf9184 Merge pull request #82 from thockin/output_tests
Huge updates to output tests
2017-06-28 23:20:59 +08:00
03a2daaeee Add a test for caseless unmarshal 2017-06-28 08:11:38 -07:00
4652ac6cc2 Add output tests for embedded types 2017-06-28 08:11:38 -07:00
8a9f2b9179 Add output test for manual text marshalers 2017-06-28 08:11:33 -07:00
0db2d74de8 Add output test for manual json marshalers 2017-06-28 08:11:02 -07:00
d6ef711c18 Better errors in output_tests 2017-06-28 07:48:55 -07:00
628fedf63c Add output tests for map keys with TextMarshal 2017-06-28 07:48:55 -07:00
5bb7a1f7af Add output test for 'string' tag 2017-06-28 07:48:55 -07:00
c2c9981062 catch errors in output_tests 2017-06-28 07:48:55 -07:00
e40d614037 Add output test for json tag field names 2017-06-28 07:48:55 -07:00
1589ab2fd7 Add output tests for omitempty 2017-06-28 07:48:49 -07:00
97ee4ad4a2 Reorganize output_tests for structs
As I added more and more cases, I found the dir structure hard to
navigate.  The new structure follows how you read a type:

   e.g. struct { F *string } -> struct/ptr_string

This exposed some redundant cases and some missing cases, too.
2017-06-27 19:51:13 +01:00
f09f778ca9 Reorganize output_tests for slices
As I added more and more cases, I found the dir structure hard to
navigate.  The new structure follows how you read a type:

   e.g. []*string -> slice/ptr_string

This exposed some redundant cases and some missing cases, too.
2017-06-27 19:21:17 +01:00
9fc858b117 Reorganize output_tests for maps
As I added more and more cases, I found the dir structure hard to
navigate.  The new structure follows how you read a type:

e.g. map[string]*string -> map/string/ptr_string

This exposed some redundant cases and some missing cases, too. Now
map[string] is the "main" test case that exercises all the variants of
value types, and the other key types are just to prove that they
basically work.
2017-06-27 19:19:37 +01:00
f93d25f8b1 Add output tests for slices of slices 2017-06-27 18:28:52 +01:00
7cd7a6cc7c Add output tests for maps of maps/slices/structs 2017-06-27 11:58:07 +01:00
7d6c9374e8 Add output test for structs of struct ptrs 2017-06-27 11:46:55 +01:00
e16ee7f8ac Add output tests for structs of slices and maps 2017-06-27 11:41:17 +01:00
cf6367546b Add output tests for structs of structs 2017-06-27 11:28:17 +01:00
dc44e85a86 #73 fix interface{} optimization for one ptr field struct and array 2017-06-26 22:37:24 +08:00
85f7a1b0b3 Merge pull request #74 from thockin/output_tests
Output tests for structs
2017-06-26 21:49:06 +08:00
507a446eda Add output_tests for structs of ptrs 2017-06-26 06:09:00 -07:00
76eefc25ba Add output_test for empty structs 2017-06-26 05:45:54 -07:00
21a16bd252 Add output_tests for structs of builtins 2017-06-26 05:37:47 -07:00
0c0c9f119f update value proposition 2017-06-26 14:28:03 +08:00
cf77980493 #71 fixed []byte alias 2017-06-26 14:25:56 +08:00
7d681fe2c2 #71 fix map key type of string alias 2017-06-26 11:52:22 +08:00
0c07128d3c #71 sort non string map keys 2017-06-26 10:42:47 +08:00
f771d32291 #70 decode null to nil for map/slice 2017-06-26 10:20:49 +08:00
d100b0d41f fix typo 2017-06-26 10:02:45 +08:00
81e64121ba #71 fix html escape for string alias 2017-06-26 09:57:45 +08:00
dcc91365ee Merge pull request #72 from thockin/output_tests
Output tests
2017-06-26 08:51:10 +08:00
8f3de9c412 Adapt tests to use new Config structs
the unit test uses compatible mode.  The benchmarks measure compat,
default, and fastest.

This still fails for strings and slices and maps all over the place.
2017-06-25 10:29:48 -07:00
5d3508979f Add output tests for slices of builtins 2017-06-25 10:00:35 -07:00
8f8e16b4c2 #63 keep struct field order 2017-06-23 08:21:02 +08:00
d7ea1acd3f #63 fix embed struct at last 2017-06-23 07:45:18 +08:00
ebed7df895 fix unicode and escape 2017-06-22 16:00:47 +08:00
caaa04195e #69 fix unicode support 2017-06-21 18:25:37 +08:00
ff3c624fa9 fix anonymous fields 2017-06-21 00:26:18 +08:00
3333ec11a0 support private fields 2017-06-20 23:48:41 +08:00
29a928e1d2 support naming strategy 2017-06-20 23:09:53 +08:00
83fa27ff9a #67 time as int64 with decoder 2017-06-20 17:52:41 +08:00
cefb2972fd #67 time as int64 with specified precision 2017-06-20 17:46:29 +08:00
486534c67c #67 time as int64 2017-06-20 17:43:47 +08:00
ed79b1726e fix encoder/decoder cast issue 2017-06-20 17:01:21 +08:00
85be06b145 #68 empty array to object/map 2017-06-20 16:36:22 +08:00
086001225d #68 string to float64 2017-06-20 16:20:56 +08:00
2ea4d48e1f #68 string to float32 2017-06-20 16:17:00 +08:00
417011b497 #68 remove redundant math max constants 2017-06-20 16:10:29 +08:00
ae6ce2fc3f #68 fuzzy all kinds of integer 2017-06-20 16:07:30 +08:00
8ef0c22f25 #68 handle float to int safely 2017-06-20 15:46:22 +08:00
a5ae3a2649 #68 float to int 2017-06-20 15:20:56 +08:00
306b2896cf #68 string to int 2017-06-20 15:18:24 +08:00
818ae1331a #68 number to string 2017-06-20 15:11:01 +08:00
8f6a840c63 fix anonymous struct 2017-06-20 13:33:40 +08:00
be221df432 #66 Make extension api like the java version 2017-06-20 10:41:54 +08:00
499412ec4c #66 extract out feacture_reflect_extension 2017-06-20 08:42:36 +08:00
c36a7ed7cd #66 extract out feacture_reflect_extension 2017-06-20 08:42:25 +08:00
14588726a1 expose ValEncoder & ValDecoder 2017-06-20 08:08:59 +08:00
aa01f57b7f rename AdaptedDecoder => Decoder and AdaptedEncoder => Encoder 2017-06-20 08:00:43 +08:00
b3170a8cef rename Encoder => ValEncoder and Decoder => ValDecoder 2017-06-20 07:59:45 +08:00
43a832beee add isEmptyFunc 2017-06-20 07:57:23 +08:00
39c9bb226a fix lossy float marshal and omit empty 2017-06-20 07:51:38 +08:00
945fe53724 fix html escape test and omit empty 2017-06-20 07:46:13 +08:00
8367a97ad8 gofmt 2017-06-20 07:39:54 +08:00
365d399192 #65 make placeholder thread safe 2017-06-20 07:39:38 +08:00
839247df05 #63 fix Marshaler and Unmarshaler on struct 2017-06-20 07:23:22 +08:00
f5edf564c8 gofmt 2017-06-19 23:43:53 +08:00
c3f5a2c536 #64 support fixed array 2017-06-19 23:43:28 +08:00
c6a598e292 # add jsoniter.RawMessage 2017-06-19 23:10:20 +08:00
eecb062c32 #63 support decode anonymous struct 2017-06-19 23:02:57 +08:00
50583f6bae #63 support *json.RawMessage 2017-06-19 22:57:43 +08:00
3b883aeffc #63 add more tests for json.RawMessage 2017-06-19 21:24:59 +08:00
baca358b53 add MustBeValid to Any 2017-06-19 21:21:20 +08:00
514db10f97 add Any.ToVal 2017-06-19 15:40:00 +08:00
31afe6450e add Api interface to allow save the frozen config 2017-06-19 13:43:22 +08:00
7e9017caa2 remove unused files 2017-06-18 23:43:01 +08:00
b6dfbbd6bc add document 2017-06-18 23:42:23 +08:00
3ffa5af7ec #61 remove internal buffer from mapAny 2017-06-18 23:18:32 +08:00
02cf6a73cc #61 remove internal buffer from objectAny 2017-06-18 23:09:30 +08:00
15c92d48df #61 remove internal buffer from numberLazyAny 2017-06-18 22:48:28 +08:00
a84cdaa694 #61 remove internal buffer from arrayAny 2017-06-18 22:40:18 +08:00
9f9ca4c9fc #61 remove stringLazyAny 2017-06-18 22:24:11 +08:00
985e263300 #61 removed internal buffer from lazy array and object; jsoniter.Get replaced jsoniter.UnmarshalAny 2017-06-18 22:22:13 +08:00
1ec246d16b #61 read any reuse skip impl 2017-06-18 17:00:28 +08:00
54dbcda64d #62 SkipAndReturnBytes should support reader 2017-06-18 16:28:43 +08:00
7a049ec79c #60 support read interface{} as json.Number 2017-06-18 15:22:37 +08:00
77dcffe77d tweak performance 2017-06-17 22:42:11 +08:00
8ab46965bd extract out any tests 2017-06-17 21:32:48 +08:00
2503ef17eb marshal lazy array/object using the config 2017-06-17 21:13:17 +08:00
0195110b5b gofmt 2017-06-17 21:11:23 +08:00
55fc498d27 use iterator from cache for any 2017-06-17 21:10:08 +08:00
50e4910c63 document how to get best performance 2017-06-17 17:14:34 +08:00
f29fe7407e downgrade to lower golang version 2017-06-17 16:27:19 +08:00
3c8bd9ef54 #57 copy bytes 2017-06-17 14:36:38 +08:00
952a42af6c #57 copy bytes 2017-06-17 14:36:05 +08:00
17bd91fd71 #57 reuse stream and iterator 2017-06-17 14:23:02 +08:00
3d5f6d3a4a #58 string mode support both encoding and decoding 2017-06-17 11:38:09 +08:00
b31b1301e2 #59 add ConfigFastest 2017-06-17 10:21:37 +08:00
69bc64b6d8 #54 support sort map keys 2017-06-16 16:46:30 +08:00
e0e2423e9a #53 test compatibility without html escape 2017-06-16 16:03:02 +08:00
a6ea770365 #53 implement SetEscapeHtml 2017-06-16 00:10:05 +08:00
5f22e50c89 #53 support escapeHtml 2017-06-15 23:55:04 +08:00
d867c8ba5c #53 split config into Config and frozenConfig 2017-06-13 18:49:35 +08:00
d0418857ce #53 move current config EnableXXX 2017-06-13 17:47:40 +08:00
48e9f6ec84 move IndentionStep to config 2017-06-13 17:03:27 +08:00
acddcf5bbf #53 extract out config 2017-06-13 16:58:53 +08:00
788918b85d #56 nil map or array should be null not [] or {} 2017-06-13 09:14:19 +08:00
6e5817b773 Merge pull request #55 from thockin/output_tests
WIP: Tests to compare against stdlib
2017-06-12 01:47:50 -05:00
7480e41836 Add output tests for maps of builtins
This tests for exact stdlib compatibility.
2017-06-11 21:09:56 -07:00
9215b3c508 Add output tests for builtin types
This fuzzes a type, marshals it with stdlib and json-iterator, compares,
then unmarshals with stdlib and json-iterator and compares.  This is
checking for literal, byte-for-byte compatibility.

In every case the test is exactly the same.

It also include benchmark functions to compare stdlib vs json-iterator.

This depends on a couple PRs to be merged in gofuzz.
2017-06-11 21:04:59 -07:00
64e500f3c8 Merge branch 'master' of https://github.com/json-iterator/go 2017-06-12 10:13:22 +08:00
3307ce3ba2 #50 map key unlike object field, can contain escaped char 2017-06-12 10:13:13 +08:00
6f50f15678 decoder/encoder;float precision doc 2017-06-11 16:30:31 +08:00
cee09816e3 decoder/encoder;float precision doc 2017-06-11 16:28:31 +08:00
cdbad22d22 test more package description 2017-06-11 15:35:45 +08:00
b0c9f047e2 test more than one pakcage description 2017-06-11 15:32:58 +08:00
6bd13c2948 Merge branch 'master' of https://github.com/json-iterator/go 2017-06-09 17:06:38 +08:00
84ad508437 #48 should return error if concrete tpye unknown 2017-06-09 17:06:27 +08:00
4f909776cf Merge pull request #49 from zhaitianduo/master
Use jsoniter instead of json in example
2017-06-09 03:32:31 -05:00
962c470806 fix import not use 2017-06-09 16:28:20 +08:00
46d443fbad use jsoniter for example 2017-06-09 16:25:58 +08:00
2608d40f2a example unmarshal 2017-06-08 12:08:47 +08:00
3cf822853f example unmarshal 2017-06-08 12:07:03 +08:00
26708bccc9 report error when string end not found 2017-06-08 09:46:19 +08:00
d75b539bad add test for scientific float 2017-06-07 21:34:56 +08:00
cfffa29c8a gofmt 2017-06-06 23:27:00 +08:00
925df245d3 good enough indent implementation 2017-06-06 23:18:37 +08:00
962a8cd303 #40 support UseNumber 2017-06-06 23:15:15 +08:00
6509ba05df Merge pull request #41 from 1046102779/master
解析时,如果输出参数不是指针类型,直接报错,避免程序挂掉
2017-06-06 10:03:06 -05:00
579dbf3c1d Merge pull request #42 from 1046102779/patch-1
把floatDigits改为intDigits
2017-06-06 10:02:26 -05:00
aa5181db67 把floatDigits改为intDigits 2017-06-06 21:08:04 +08:00
67be6df2b1 Update feature_adapter.go 2017-06-06 20:01:43 +08:00
0f5379494a unmarshal failed return non-pointer error 2017-06-06 19:36:33 +08:00
d09e2419ba update benchmark 2017-06-06 16:55:32 +08:00
e1a71f6ba1 update benchmark 2017-06-06 16:54:26 +08:00
dcb78991c4 flush when buffer is large enough 2017-06-06 14:16:54 +08:00
9e8238cdc6 remove unused file 2017-06-06 12:41:13 +08:00
a4e5abf492 support []byte; marshal without copy 2017-06-06 09:44:56 +08:00
3979955e69 support TextMarshaler as map key 2017-06-06 00:09:33 +08:00
5fd09f0e02 remove mapInterfaceEncoder 2017-06-05 23:56:37 +08:00
af4982b22c support decode int key map 2017-06-05 23:53:48 +08:00
29dc1d407d write map with int key 2017-06-05 23:01:00 +08:00
5b27aaa62c update test 2017-06-05 22:10:01 +08:00
106636a191 update test 2017-06-05 22:08:28 +08:00
f50c4cfbbe Merge branch 'master' of https://github.com/json-iterator/go 2017-06-05 22:05:02 +08:00
87149ae489 add simple marshal benchmark 2017-06-05 22:04:52 +08:00
c0a4ad72e1 example test 2017-06-05 20:37:08 +08:00
404c0ee44b Decoder doc 2017-06-05 19:57:20 +08:00
10c1506f87 link test 2017-06-05 19:38:34 +08:00
9a43fe6468 adapter api comment 2017-06-05 19:31:30 +08:00
95e03f2937 Marshal comment 2017-06-05 19:19:46 +08:00
4406ed9e62 Marshal comment 2017-06-05 19:18:12 +08:00
ff027701f5 Marshal comment 2017-06-05 19:15:56 +08:00
c69b61f879 Marshal comment 2017-06-05 19:14:40 +08:00
d97f5db769 Marshal comment 2017-06-05 19:11:16 +08:00
45bbb40a9f #34 implement NewEncoder 2017-06-02 18:46:44 +08:00
e36f926072 fix random go test failure 2017-06-02 17:34:40 +08:00
59e71bacc8 #36 handle anonymous 2017-06-02 16:52:20 +08:00
5cb0d35610 eof is not error 2017-06-02 16:06:33 +08:00
69b742e73a #34 support More() and Buffered() 2017-06-02 16:00:12 +08:00
a7f992f0e1 #35 fix json.Number matches string 2017-06-02 15:43:58 +08:00
4cc44e7380 #34 add decoder adapter 2017-06-02 15:38:20 +08:00
5310d4aa9a syntax highlight 2017-06-02 11:20:54 +08:00
2051e3b8ae simplify readme 2017-06-02 11:20:24 +08:00
fe9fa8900e #31 support json.RawMessage 2017-06-02 10:50:23 +08:00
ad3a7fde32 #30 support json.Number 2017-06-02 10:21:43 +08:00
377b892102 support big float and int 2017-05-31 12:40:50 +08:00
707ed3b091 support non empty interface 2017-05-27 00:36:21 +08:00
a7a7c7879a allocate less buffer for writing to []byte 2017-05-26 07:52:54 +08:00
f20f74519d RegisterTypeEncoder and RegisterTypeDecoder should have higher priority 2017-05-24 23:39:34 +08:00
7d2ae80c37 #27 support json.Unmarshaler 2017-05-24 16:04:11 +08:00
f6f159e108 #27 support json.Marshaler 2017-05-24 14:34:00 +08:00
e5a1e704ad #25 make fielding binding case insensitive 2017-05-24 13:16:09 +08:00
7d5f90261e #28 extension should support specifying encoder 2017-05-24 10:58:56 +08:00
6126a6d3ca #23 hide unexported fields by default 2017-05-24 09:39:11 +08:00
5fbe4e387d #21 #22 marshal float precisely by default 2017-05-24 09:08:15 +08:00
fc44cb2d91 #26 do not enforce pointer as optional 2017-05-23 18:46:11 +08:00
7e046e6aa7 simplify read string, and support null 2017-05-23 18:32:39 +08:00
5488fde97f fix one field struct interface{} optimization compatibility 2017-05-23 17:44:50 +08:00
53f8d370b5 fix wrap any 2017-05-19 19:44:27 +08:00
3f1fcaff87 demonstrate how to customize float encoding 2017-05-11 08:00:50 +08:00
1df353727b customize []byte encoder 2017-05-06 20:52:36 +08:00
b893a0359d trim end space 2017-05-05 17:44:09 +08:00
a92111261c fix struct with one pointer field 2017-05-05 17:27:41 +08:00
91b9e828b7 support recursive type 2017-05-05 16:51:05 +08:00
6bd835aeb1 test type encoder 2017-05-05 08:22:19 +08:00
90888390bc fix readFloat64SlowPath not advancing the cursor 2017-05-02 10:15:21 +08:00
ccb972f58c merge 2017-04-28 09:10:06 +08:00
8711c74c85 support Any as field type 2017-04-28 09:09:24 +08:00
abcf2759ed Merge pull request #14 from eruca/master
fix the omitempty bug
2017-04-18 10:23:05 +10:00
e5476f70e7 #16 fix slice of map 2017-04-16 14:05:08 +08:00
b986d86f26 add test 2017-03-11 18:19:39 +08:00
9a138f8b6a fix bug of another comma added
previous code will add comma if the field is empty when is not first, like 
```
{"account":"1120","name":"nick",,"department":"fsaf","role":1,"privilege":32,,"created_at":1489226500}
```
2017-03-11 18:17:34 +08:00
d1aa59e34e #12 implement omitempty 2017-03-08 07:38:25 -08:00
ceb8c8a733 create map if nil 2017-03-07 18:36:58 -08:00
62028f1ede Merge pull request #11 from Kisesy/patch-1
fix #10
2017-02-26 09:42:45 -06:00
696f962eda fix https://github.com/json-iterator/go/issues/10
Update feature_iter_string.go

Update feature_iter_string.go
2017-02-25 13:30:55 +08:00
9d1feb5431 fix tests 2017-02-12 22:49:45 +08:00
9670a03165 fix issue on 32bit platform 2017-02-09 13:50:06 +08:00
17cbb770f0 fix issue on 32bit platform 2017-02-09 13:35:58 +08:00
da7ed7809b use hash for field dispatching 2017-02-08 13:46:28 +08:00
14f696c6f5 Merge branch 'master' of https://github.com/json-iterator/go 2017-02-07 09:24:46 +08:00
2922666717 fix read int 2017-02-07 09:24:36 +08:00
06078a3afb Merge pull request #8 from smartczy/patch-1
Update README.md
2017-02-04 07:25:34 -06:00
232e0df179 Update README.md
modify the useage of function Read().
2017-02-04 14:34:00 +08:00
6880076b44 fix map[string]interface{} in struct 2017-02-03 18:44:54 +08:00
1e91dbbf58 map any support get all 2017-01-31 23:16:40 +08:00
bf459b9a49 object lazy any support get all 2017-01-31 20:26:35 +08:00
498ab96d90 array any support get all 2017-01-31 20:10:16 +08:00
edb96991a8 add all integer types 2017-01-29 16:55:32 +08:00
10a1fb8762 wrap map 2017-01-28 23:11:29 +08:00
95823d0bf1 wrap object 2017-01-28 22:45:03 +08:00
64c9bd0b1c wrap array 2017-01-28 21:11:36 +08:00
fa16f3c75c wrap string 2017-01-26 16:41:49 +08:00
cf4113fc22 wrap float 2017-01-26 16:33:16 +08:00
85edb698c8 Any Get will never return nil 2017-01-26 16:24:01 +08:00
9abc2f52b0 add Any GetInterface 2017-01-26 15:44:10 +08:00
97472ecd96 expose Parse from Any 2017-01-26 15:40:38 +08:00
4d7e181f9f implement Any WriteTo 2017-01-26 14:56:31 +08:00
9b587c0f22 suport encode interface 2017-01-26 00:25:17 +08:00
ce1a1f1e98 test reading into custom interface 2017-01-25 23:25:25 +08:00
f1c4dbde29 support map of interface{} 2017-01-25 22:43:57 +08:00
94ae645ab9 array/object to bool/int 2017-01-24 23:13:58 +08:00
ee54218b0a lazy array get 2017-01-24 22:56:18 +08:00
fa165c684f array lazy iterator 2017-01-24 22:47:56 +08:00
8656482625 array lazy fill and full fill 2017-01-24 22:36:16 +08:00
2d647f04ca implement lazy object three iteration way 2017-01-24 00:23:07 +08:00
38d613acf2 bool any 2017-01-23 08:45:57 +08:00
b9fe012eea string any 2017-01-23 08:33:43 +08:00
d49ea1bc49 float any 2017-01-22 23:38:55 +08:00
ba410b045b int lazy any 2017-01-22 23:29:48 +08:00
9df37bbd68 remove any 2017-01-22 19:36:19 +08:00
9c2b1d24b3 optimize true/false/null: 2017-01-22 19:28:14 +08:00
1163c348f6 fix write string 2017-01-22 00:04:08 +08:00
102cd8748e fix write int 2017-01-21 23:22:38 +08:00
8345c731dd rename 2017-01-21 17:11:38 +08:00
1d29fa38ef read to interface{} 2017-01-21 16:09:38 +08:00
928bc4ce72 ReadArrayCB 2017-01-20 13:54:51 +08:00
80c86e63b1 read object cb 2017-01-20 12:56:49 +08:00
d14b025931 make ReadObject return safe string 2017-01-20 12:40:52 +08:00
a73e48e8bf optimize read nil/true/false 2017-01-18 23:33:40 +08:00
6efc6c44ac test read string and read string as slice 2017-01-16 23:57:09 +08:00
e7ff7339b2 optimize read string 2017-01-16 23:43:20 +08:00
b9e3f01bfd fix int min max 2017-01-16 09:01:44 +08:00
54a69f1da2 fix read int ranges 2017-01-15 23:17:17 +08:00
399ee3faaa optimize read int32 2017-01-15 22:50:31 +08:00
c3c57d61b5 optimize read float64 2017-01-15 18:08:49 +08:00
5b2a731963 implement readFloat32 fast path 2017-01-15 17:30:02 +08:00
a57c8c6202 test array of interface 2017-01-09 21:00:01 +08:00
1f0a0e9a2d encode optional 2017-01-09 20:51:09 +08:00
90fc0b822f array encoder 2017-01-09 19:48:57 +08:00
552afb3625 struct encoder 2017-01-09 19:19:48 +08:00
5b0609f901 support int/string encode 2017-01-09 17:47:21 +08:00
bdbf8dcd42 write float64 2017-01-07 23:32:54 +08:00
ba3c30799b write float 32 2017-01-07 23:06:48 +08:00
e034897e69 write array 2017-01-07 22:16:20 +08:00
21549b9fd8 write object 2017-01-07 22:08:45 +08:00
5e50b3e11c write int 2017-01-07 21:25:23 +08:00
6f57d41461 add stream 2017-01-07 12:28:16 +08:00
5af8cc4b09 split files 2017-01-07 07:49:50 +08:00
c78023531e add more tests 2017-01-06 20:27:48 +08:00
101dfdbb2a fix 4 fields object decoding 2017-01-06 20:17:47 +08:00
c70437c6b9 Merge pull request #5 from hanbang-wang/master
Formated, doced. Also fixed few minor bugs.
2017-01-05 21:41:45 +08:00
d63a00f0bf Formated, doced. Also fixed few minor bugs. 2017-01-05 21:23:08 +08:00
97849e019f fix read any; reuse existing obj for optional 2017-01-05 13:53:38 +08:00
247a23a637 remove skipWhitespaces 2017-01-05 09:16:41 +08:00
e7ec3988a6 fix tag field name not used bug 2016-12-19 12:12:15 +08:00
c7d0dd5b66 Merge branch 'master' of https://github.com/json-iterator/go 2016-12-17 17:38:33 +08:00
7d5f2aed7b update extension 2016-12-17 17:38:13 +08:00
e38192e48f #3 fix nextToken 2016-12-16 00:25:35 +08:00
157 changed files with 18280 additions and 3785 deletions

3
.codecov.yml Normal file
View File

@ -0,0 +1,3 @@
ignore:
- "output_tests/.*"

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/vendor
/bug_test.go
/coverage.txt
/.idea

14
.travis.yml Normal file
View File

@ -0,0 +1,14 @@
language: go
go:
- 1.8.x
- 1.x
before_install:
- go get -t -v ./...
script:
- ./test.sh
after_success:
- bash <(curl -s https://codecov.io/bash)

21
Gopkg.lock generated Normal file
View File

@ -0,0 +1,21 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
name = "github.com/modern-go/concurrent"
packages = ["."]
revision = "e0a39a4cb4216ea8db28e22a69f4ec25610d513a"
version = "1.0.0"
[[projects]]
name = "github.com/modern-go/reflect2"
packages = ["."]
revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd"
version = "1.0.1"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "ea54a775e5a354cb015502d2e7aa4b74230fc77e894f34a838b268c25ec8eeb8"
solver-name = "gps-cdcl"
solver-version = 1

26
Gopkg.toml Normal file
View File

@ -0,0 +1,26 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
ignored = ["github.com/davecgh/go-spew*","github.com/google/gofuzz*","github.com/stretchr/testify*"]
[[constraint]]
name = "github.com/modern-go/reflect2"
version = "1.0.1"

View File

@ -1,60 +1,72 @@
jsoniter (json-iterator) is fast and flexible JSON parser available in [Java](https://github.com/json-iterator/java) and [Go](https://github.com/json-iterator/go) [![Sourcegraph](https://sourcegraph.com/github.com/json-iterator/go/-/badge.svg)](https://sourcegraph.com/github.com/json-iterator/go?badge)
[![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](https://pkg.go.dev/github.com/json-iterator/go)
[![Build Status](https://travis-ci.org/json-iterator/go.svg?branch=master)](https://travis-ci.org/json-iterator/go)
[![codecov](https://codecov.io/gh/json-iterator/go/branch/master/graph/badge.svg)](https://codecov.io/gh/json-iterator/go)
[![rcard](https://goreportcard.com/badge/github.com/json-iterator/go)](https://goreportcard.com/report/github.com/json-iterator/go)
[![License](http://img.shields.io/badge/license-mit-blue.svg?style=flat-square)](https://raw.githubusercontent.com/json-iterator/go/master/LICENSE)
[![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/json-iterator/Lobby)
# Why jsoniter? A high-performance 100% compatible drop-in replacement of "encoding/json"
* Jsoniter is the fastest JSON parser. It could be up to 10x faster than normal parser, data binding included. Shameless self [benchmark](http://jsoniter.com/benchmark.html) You can also use thrift like JSON using [thrift-iterator](https://github.com/thrift-iterator/go)
* Extremely flexible api. You can mix and match three different styles: bind-api, any-api or iterator-api. Checkout your [api choices](http://jsoniter.com/api.html)
* Unique iterator api can iterate through JSON directly, zero memory allocation! See how [iterator](http://jsoniter.com/api.html#iterator-api) works
# Show off # Benchmark
Here is a quick show off, for more complete report you can checkout the full [benchmark](http://jsoniter.com/benchmark.html) with [in-depth optimization](http://jsoniter.com/benchmark.html#optimization-used) to back the numbers up ![benchmark](http://jsoniter.com/benchmarks/go-benchmark.png)
![go-medium](http://jsoniter.com/benchmarks/go-medium.png) Source code: https://github.com/json-iterator/go-benchmark/blob/master/src/github.com/json-iterator/go-benchmark/benchmark_medium_payload_test.go
# Bind-API is the best Raw Result (easyjson requires static code generation)
Bind-api should always be the first choice. Given this JSON document `[0,1,2,3]` | | ns/op | allocation bytes | allocation times |
| --------------- | ----------- | ---------------- | ---------------- |
| std decode | 35510 ns/op | 1960 B/op | 99 allocs/op |
| easyjson decode | 8499 ns/op | 160 B/op | 4 allocs/op |
| jsoniter decode | 5623 ns/op | 160 B/op | 3 allocs/op |
| std encode | 2213 ns/op | 712 B/op | 5 allocs/op |
| easyjson encode | 883 ns/op | 576 B/op | 3 allocs/op |
| jsoniter encode | 837 ns/op | 384 B/op | 4 allocs/op |
Parse with Go bind-api Always benchmark with your own workload.
The result depends heavily on the data input.
# Usage
100% compatibility with standard lib
Replace
```go ```go
import "github.com/json-iterator/go" import "encoding/json"
iter := jsoniter.ParseString(`[0,1,2,3]`) json.Marshal(&data)
val := []int{}
iter.Read(&val)
fmt.Println(val[3])
``` ```
# Iterator-API for quick extraction with
When you do not need to get all the data back, just extract some.
Parse with Go iterator-api
```go ```go
import "github.com/json-iterator/go" import jsoniter "github.com/json-iterator/go"
iter := ParseString(`[0, [1, 2], [3, 4], 5]`)
count := 0 var json = jsoniter.ConfigCompatibleWithStandardLibrary
for iter.ReadArray() { json.Marshal(&data)
iter.skip()
count++
}
fmt.Println(count) // 4
``` ```
# Any-API for maximum flexibility Replace
Parse with Go any-api
```go ```go
import "github.com/json-iterator/go" import "encoding/json"
iter := jsoniter.ParseString(`[{"field1":"11","field2":"12"},{"field1":"21","field2":"22"}]`) json.Unmarshal(input, &data)
val := iter.ReadAny()
fmt.Println(val.ToInt(1, "field2")) // 22
``` ```
Notice you can extract from nested data structure, and convert any type to the type to you want. with
```go
import jsoniter "github.com/json-iterator/go"
var json = jsoniter.ConfigCompatibleWithStandardLibrary
json.Unmarshal(input, &data)
```
[More documentation](http://jsoniter.com/migrate-from-go-std.html)
# How to get # How to get
@ -64,4 +76,12 @@ go get github.com/json-iterator/go
# Contribution Welcomed ! # Contribution Welcomed !
Contributors
- [thockin](https://github.com/thockin)
- [mattn](https://github.com/mattn)
- [cch123](https://github.com/cch123)
- [Oleg Shaldybin](https://github.com/olegshaldybin)
- [Jason Toffaletti](https://github.com/toffaletti)
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) 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)

150
adapter.go Normal file
View File

@ -0,0 +1,150 @@
package jsoniter
import (
"bytes"
"io"
)
// RawMessage to make replace json with jsoniter
type RawMessage []byte
// Unmarshal adapts to json/encoding Unmarshal API
//
// Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v.
// Refer to https://godoc.org/encoding/json#Unmarshal for more information
func Unmarshal(data []byte, v interface{}) error {
return ConfigDefault.Unmarshal(data, v)
}
// UnmarshalFromString is a convenient method to read from string instead of []byte
func UnmarshalFromString(str string, v interface{}) error {
return ConfigDefault.UnmarshalFromString(str, v)
}
// Get quick method to get value from deeply nested JSON structure
func Get(data []byte, path ...interface{}) Any {
return ConfigDefault.Get(data, path...)
}
// Marshal adapts to json/encoding Marshal API
//
// Marshal returns the JSON encoding of v, adapts to json/encoding Marshal API
// Refer to https://godoc.org/encoding/json#Marshal for more information
func Marshal(v interface{}) ([]byte, error) {
return ConfigDefault.Marshal(v)
}
// MarshalIndent same as json.MarshalIndent. Prefix is not supported.
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
return ConfigDefault.MarshalIndent(v, prefix, indent)
}
// MarshalToString convenient method to write as string instead of []byte
func MarshalToString(v interface{}) (string, error) {
return ConfigDefault.MarshalToString(v)
}
// NewDecoder adapts to json/stream NewDecoder API.
//
// NewDecoder returns a new decoder that reads from r.
//
// Instead of a json/encoding Decoder, an Decoder is returned
// Refer to https://godoc.org/encoding/json#NewDecoder for more information
func NewDecoder(reader io.Reader) *Decoder {
return ConfigDefault.NewDecoder(reader)
}
// Decoder reads and decodes JSON values from an input stream.
// Decoder provides identical APIs with json/stream Decoder (Token() and UseNumber() are in progress)
type Decoder struct {
iter *Iterator
}
// Decode decode JSON into interface{}
func (adapter *Decoder) Decode(obj interface{}) error {
if adapter.iter.head == adapter.iter.tail && adapter.iter.reader != nil {
if !adapter.iter.loadMore() {
return io.EOF
}
}
adapter.iter.ReadVal(obj)
err := adapter.iter.Error
if err == io.EOF {
return nil
}
return adapter.iter.Error
}
// More is there more?
func (adapter *Decoder) More() bool {
iter := adapter.iter
if iter.Error != nil {
return false
}
c := iter.nextToken()
if c == 0 {
return false
}
iter.unreadByte()
return c != ']' && c != '}'
}
// Buffered remaining buffer
func (adapter *Decoder) Buffered() io.Reader {
remaining := adapter.iter.buf[adapter.iter.head:adapter.iter.tail]
return bytes.NewReader(remaining)
}
// UseNumber causes the Decoder to unmarshal a number into an interface{} as a
// Number instead of as a float64.
func (adapter *Decoder) UseNumber() {
cfg := adapter.iter.cfg.configBeforeFrozen
cfg.UseNumber = true
adapter.iter.cfg = cfg.frozeWithCacheReuse(adapter.iter.cfg.extraExtensions)
}
// DisallowUnknownFields causes the Decoder to return an error when the destination
// is a struct and the input contains object keys which do not match any
// non-ignored, exported fields in the destination.
func (adapter *Decoder) DisallowUnknownFields() {
cfg := adapter.iter.cfg.configBeforeFrozen
cfg.DisallowUnknownFields = true
adapter.iter.cfg = cfg.frozeWithCacheReuse(adapter.iter.cfg.extraExtensions)
}
// NewEncoder same as json.NewEncoder
func NewEncoder(writer io.Writer) *Encoder {
return ConfigDefault.NewEncoder(writer)
}
// Encoder same as json.Encoder
type Encoder struct {
stream *Stream
}
// Encode encode interface{} as JSON to io.Writer
func (adapter *Encoder) Encode(val interface{}) error {
adapter.stream.WriteVal(val)
adapter.stream.WriteRaw("\n")
adapter.stream.Flush()
return adapter.stream.Error
}
// SetIndent set the indention. Prefix is not supported
func (adapter *Encoder) SetIndent(prefix, indent string) {
config := adapter.stream.cfg.configBeforeFrozen
config.IndentionStep = len(indent)
adapter.stream.cfg = config.frozeWithCacheReuse(adapter.stream.cfg.extraExtensions)
}
// SetEscapeHTML escape html by default, set to false to disable
func (adapter *Encoder) SetEscapeHTML(escapeHTML bool) {
config := adapter.stream.cfg.configBeforeFrozen
config.EscapeHTML = escapeHTML
adapter.stream.cfg = config.frozeWithCacheReuse(adapter.stream.cfg.extraExtensions)
}
// Valid reports whether data is a valid JSON encoding.
func Valid(data []byte) bool {
return ConfigDefault.Valid(data)
}

700
any.go
View File

@ -1,427 +1,325 @@
package jsoniter package jsoniter
import ( import (
"errors"
"fmt" "fmt"
"github.com/modern-go/reflect2"
"io"
"reflect" "reflect"
"strconv" "strconv"
"unsafe"
) )
type Any struct { // Any generic object representation.
val interface{} // The lazy json implementation holds []byte and parse lazily.
Error error type Any interface {
LastAccessed interface{} LastError() error
ValueType() ValueType
MustBeValid() Any
ToBool() bool
ToInt() int
ToInt32() int32
ToInt64() int64
ToUint() uint
ToUint32() uint32
ToUint64() uint64
ToFloat32() float32
ToFloat64() float64
ToString() string
ToVal(val interface{})
Get(path ...interface{}) Any
Size() int
Keys() []string
GetInterface() interface{}
WriteTo(stream *Stream)
} }
func MakeAny(val interface{}) *Any { type baseAny struct{}
return &Any{val, nil, nil}
func (any *baseAny) Get(path ...interface{}) Any {
return &invalidAny{baseAny{}, fmt.Errorf("GetIndex %v from simple value", path)}
} }
func (any *Any) Get(keys ...interface{}) interface{} { func (any *baseAny) Size() int {
ret, err := getPath(any.val, keys...) return 0
any.LastAccessed = ret }
if err != nil {
any.Error = err func (any *baseAny) Keys() []string {
return ""; return []string{}
}
func (any *baseAny) ToVal(obj interface{}) {
panic("not implemented")
}
// WrapInt32 turn int32 into Any interface
func WrapInt32(val int32) Any {
return &int32Any{baseAny{}, val}
}
// WrapInt64 turn int64 into Any interface
func WrapInt64(val int64) Any {
return &int64Any{baseAny{}, val}
}
// WrapUint32 turn uint32 into Any interface
func WrapUint32(val uint32) Any {
return &uint32Any{baseAny{}, val}
}
// WrapUint64 turn uint64 into Any interface
func WrapUint64(val uint64) Any {
return &uint64Any{baseAny{}, val}
}
// WrapFloat64 turn float64 into Any interface
func WrapFloat64(val float64) Any {
return &floatAny{baseAny{}, val}
}
// WrapString turn string into Any interface
func WrapString(val string) Any {
return &stringAny{baseAny{}, val}
}
// Wrap turn a go object into Any interface
func Wrap(val interface{}) Any {
if val == nil {
return &nilAny{}
} }
return ret asAny, isAny := val.(Any)
} if isAny {
return asAny
func (any *Any) GetValueType(keys ...interface{}) ValueType {
ret, err := getPath(any.val, keys...)
any.LastAccessed = ret
if err != nil {
any.Error = err
return Invalid;
} }
typ := reflect2.TypeOf(val)
switch reflect.TypeOf(ret).Kind() { switch typ.Kind() {
case reflect.Uint8: case reflect.Slice:
return Number; return wrapArray(val)
case reflect.Int8:
return Number;
case reflect.Uint16:
return Number;
case reflect.Int16:
return Number;
case reflect.Uint32:
return Number;
case reflect.Int32:
return Number;
case reflect.Uint64:
return Number;
case reflect.Int64:
return Number;
case reflect.Int:
return Number;
case reflect.Uint:
return Number;
case reflect.Float32:
return Number;
case reflect.Float64:
return Number;
case reflect.String:
return String;
case reflect.Bool:
return Bool;
case reflect.Array:
return Array;
case reflect.Struct: case reflect.Struct:
return Object; return wrapStruct(val)
default: case reflect.Map:
return Invalid return wrapMap(val)
} case reflect.String:
} return WrapString(val.(string))
case reflect.Int:
func (any *Any) ToString(keys ...interface{}) string { if strconv.IntSize == 32 {
ret, err := getPath(any.val, keys...) return WrapInt32(int32(val.(int)))
any.LastAccessed = ret
if err != nil {
any.Error = err
return "";
}
switch ret := ret.(type) {
case uint8:
return strconv.FormatInt(int64(ret), 10);
case int8:
return strconv.FormatInt(int64(ret), 10);
case uint16:
return strconv.FormatInt(int64(ret), 10);
case int16:
return strconv.FormatInt(int64(ret), 10);
case uint32:
return strconv.FormatInt(int64(ret), 10);
case int32:
return strconv.FormatInt(int64(ret), 10);
case uint64:
return strconv.FormatUint(uint64(ret), 10);
case int64:
return strconv.FormatInt(int64(ret), 10);
case int:
return strconv.FormatInt(int64(ret), 10);
case uint:
return strconv.FormatInt(int64(ret), 10);
case float32:
return strconv.FormatFloat(float64(ret), 'E', -1, 32);
case float64:
return strconv.FormatFloat(ret, 'E', -1, 64);
case string:
return ret
default:
return fmt.Sprintf("%v", ret)
}
}
func (any *Any) ToUint8(keys ...interface{}) uint8 {
ret, err := getPathAsInt64(any, keys...)
if err != nil {
any.Error = err
return 0;
}
return uint8(ret)
}
func (any *Any) ToInt8(keys ...interface{}) int8 {
ret, err := getPathAsInt64(any, keys...)
if err != nil {
any.Error = err
return 0;
}
return int8(ret)
}
func (any *Any) ToUint16(keys ...interface{}) uint16 {
ret, err := getPathAsInt64(any, keys...)
if err != nil {
any.Error = err
return 0;
}
return uint16(ret)
}
func (any *Any) ToInt16(keys ...interface{}) int16 {
ret, err := getPathAsInt64(any, keys...)
if err != nil {
any.Error = err
return 0;
}
return int16(ret)
}
func (any *Any) ToUint32(keys ...interface{}) uint32 {
ret, err := getPathAsInt64(any, keys...)
if err != nil {
any.Error = err
return 0;
}
return uint32(ret)
}
func (any *Any) ToInt32(keys ...interface{}) int32 {
ret, err := getPathAsInt64(any, keys...)
if err != nil {
any.Error = err
return 0;
}
return int32(ret)
}
func (any *Any) ToUint64(keys ...interface{}) uint64 {
ret, err := getPathAsUint64(any, keys...)
if err != nil {
any.Error = err
return 0;
}
return uint64(ret)
}
func (any *Any) ToInt64(keys ...interface{}) int64 {
ret, err := getPathAsInt64(any, keys...)
if err != nil {
any.Error = err
return 0;
}
return int64(ret)
}
func (any *Any) ToInt(keys ...interface{}) int {
ret, err := getPathAsInt64(any, keys...)
if err != nil {
any.Error = err
return 0;
}
return int(ret)
}
func (any *Any) ToUint(keys ...interface{}) uint {
ret, err := getPathAsInt64(any, keys...)
if err != nil {
any.Error = err
return 0;
}
return uint(ret)
}
func (any *Any) ToFloat32(keys ...interface{}) float32 {
ret, err := getPathAsFloat64(any, keys...)
if err != nil {
any.Error = err
return 0;
}
return float32(ret)
}
func (any *Any) ToFloat64(keys ...interface{}) float64 {
ret, err := getPathAsFloat64(any, keys...)
if err != nil {
any.Error = err
return 0;
}
return ret
}
func (any *Any) ToBool(keys ...interface{}) bool {
ret, err := getPath(any.val, keys...)
any.LastAccessed = ret
if err != nil {
any.Error = err
return false;
}
typedRet, ok := ret.(bool)
if !ok {
any.Error = fmt.Errorf("%v is not bool", ret)
return false;
}
return typedRet
}
func (any *Any) IsNull(keys ...interface{}) bool {
ret, err := getPath(any.val, keys...)
any.LastAccessed = ret
if err != nil {
any.Error = err
return false;
}
return reflect.ValueOf(ret).IsNil()
}
func getPathAsInt64(any *Any, keys ...interface{}) (int64, error) {
ret, err := getPath(any.val, keys...)
any.LastAccessed = ret
if err != nil {
any.Error = err
return 0, err
}
switch ret := ret.(type) {
case uint8:
return int64(ret), nil;
case int8:
return int64(ret), nil;
case uint16:
return int64(ret), nil;
case int16:
return int64(ret), nil;
case uint32:
return int64(ret), nil;
case int32:
return int64(ret), nil;
case uint64:
return int64(ret), nil;
case int64:
return int64(ret), nil;
case int:
return int64(ret), nil;
case uint:
return int64(ret), nil;
case float32:
return int64(ret), nil;
case float64:
return int64(ret), nil;
case string:
intVal, err := strconv.ParseInt(ret, 10, 64)
if err != nil {
return 0, err
} }
return intVal, nil; return WrapInt64(int64(val.(int)))
default: case reflect.Int8:
return 0, fmt.Errorf("%v is not number", ret) return WrapInt32(int32(val.(int8)))
} case reflect.Int16:
} return WrapInt32(int32(val.(int16)))
case reflect.Int32:
func getPathAsUint64(any *Any, keys ...interface{}) (uint64, error) { return WrapInt32(val.(int32))
ret, err := getPath(any.val, keys...) case reflect.Int64:
any.LastAccessed = ret return WrapInt64(val.(int64))
if err != nil { case reflect.Uint:
any.Error = err if strconv.IntSize == 32 {
return 0, err return WrapUint32(uint32(val.(uint)))
}
switch ret := ret.(type) {
case uint8:
return uint64(ret), nil;
case int8:
return uint64(ret), nil;
case uint16:
return uint64(ret), nil;
case int16:
return uint64(ret), nil;
case uint32:
return uint64(ret), nil;
case int32:
return uint64(ret), nil;
case uint64:
return uint64(ret), nil;
case int64:
return uint64(ret), nil;
case int:
return uint64(ret), nil;
case uint:
return uint64(ret), nil;
case float32:
return uint64(ret), nil;
case float64:
return uint64(ret), nil;
case string:
intVal, err := strconv.ParseUint(ret, 10, 64)
if err != nil {
return 0, err
} }
return intVal, nil; return WrapUint64(uint64(val.(uint)))
default: case reflect.Uintptr:
return 0, fmt.Errorf("%v is not number", ret) if ptrSize == 32 {
} return WrapUint32(uint32(val.(uintptr)))
}
func getPathAsFloat64(any *Any, keys ...interface{}) (float64, error) {
ret, err := getPath(any.val, keys...)
any.LastAccessed = ret
if err != nil {
any.Error = err
return 0, err
}
switch ret := ret.(type) {
case uint8:
return float64(ret), nil;
case int8:
return float64(ret), nil;
case uint16:
return float64(ret), nil;
case int16:
return float64(ret), nil;
case uint32:
return float64(ret), nil;
case int32:
return float64(ret), nil;
case uint64:
return float64(ret), nil;
case int64:
return float64(ret), nil;
case int:
return float64(ret), nil;
case uint:
return float64(ret), nil;
case float32:
return float64(ret), nil;
case float64:
return float64(ret), nil;
case string:
floatVal, err := strconv.ParseFloat(ret, 64)
if err != nil {
return 0, err
} }
return floatVal, nil; return WrapUint64(uint64(val.(uintptr)))
default: case reflect.Uint8:
return 0, fmt.Errorf("%v is not number", ret) return WrapUint32(uint32(val.(uint8)))
} case reflect.Uint16:
} return WrapUint32(uint32(val.(uint16)))
case reflect.Uint32:
func getPath(val interface{}, keys ...interface{}) (interface{}, error) { return WrapUint32(uint32(val.(uint32)))
if (len(keys) == 0) { case reflect.Uint64:
return val, nil; return WrapUint64(val.(uint64))
} case reflect.Float32:
switch key := keys[0].(type) { return WrapFloat64(float64(val.(float32)))
case string: case reflect.Float64:
nextVal, err := getFromMap(val, key) return WrapFloat64(val.(float64))
if err != nil { case reflect.Bool:
return nil, err if val.(bool) == true {
return &trueAny{}
} }
nextKeys := make([]interface{}, len(keys) - 1) return &falseAny{}
copy(nextKeys, keys[1:]) }
return getPath(nextVal, nextKeys...) return &invalidAny{baseAny{}, fmt.Errorf("unsupported type: %v", typ)}
case int: }
nextVal, err := getFromArray(val, key)
if err != nil { // ReadAny read next JSON element as an Any object. It is a better json.RawMessage.
return nil, err func (iter *Iterator) ReadAny() Any {
} return iter.readAny()
nextKeys := make([]interface{}, len(keys) - 1) }
copy(nextKeys, keys[1:])
return getPath(nextVal, nextKeys...) func (iter *Iterator) readAny() Any {
c := iter.nextToken()
switch c {
case '"':
iter.unreadByte()
return &stringAny{baseAny{}, iter.ReadString()}
case 'n':
iter.skipThreeBytes('u', 'l', 'l') // null
return &nilAny{}
case 't':
iter.skipThreeBytes('r', 'u', 'e') // true
return &trueAny{}
case 'f':
iter.skipFourBytes('a', 'l', 's', 'e') // false
return &falseAny{}
case '{':
return iter.readObjectAny()
case '[':
return iter.readArrayAny()
case '-':
return iter.readNumberAny(false)
case 0:
return &invalidAny{baseAny{}, errors.New("input is empty")}
default: default:
return nil, fmt.Errorf("%v is not string or int", keys[0]); return iter.readNumberAny(true)
} }
return getPath(val, keys);
} }
func getFromMap(val interface{}, key string) (interface{}, error) { func (iter *Iterator) readNumberAny(positive bool) Any {
mapVal, ok := val.(map[string]interface{}) iter.startCapture(iter.head - 1)
if !ok { iter.skipNumber()
return nil, fmt.Errorf("%v is not map[string]interface{}", val) lazyBuf := iter.stopCapture()
} return &numberLazyAny{baseAny{}, iter.cfg, lazyBuf, nil}
ret, found := mapVal[key]
if !found {
return nil, fmt.Errorf("%v not found in %v", key, mapVal)
}
return ret, nil
} }
func getFromArray(val interface{}, key int) (interface{}, error) { func (iter *Iterator) readObjectAny() Any {
arrayVal, ok := val.([]interface{}) iter.startCapture(iter.head - 1)
if !ok { iter.skipObject()
return nil, fmt.Errorf("%v is not []interface{}", val) lazyBuf := iter.stopCapture()
} return &objectLazyAny{baseAny{}, iter.cfg, lazyBuf, nil}
if key >= len(arrayVal) { }
return nil, fmt.Errorf("%v exceed %v", key, arrayVal)
} func (iter *Iterator) readArrayAny() Any {
if key < 0 { iter.startCapture(iter.head - 1)
return nil, fmt.Errorf("%v exceed %v", key, arrayVal) iter.skipArray()
} lazyBuf := iter.stopCapture()
return arrayVal[key], nil return &arrayLazyAny{baseAny{}, iter.cfg, lazyBuf, nil}
}
func locateObjectField(iter *Iterator, target string) []byte {
var found []byte
iter.ReadObjectCB(func(iter *Iterator, field string) bool {
if field == target {
found = iter.SkipAndReturnBytes()
return false
}
iter.Skip()
return true
})
return found
}
func locateArrayElement(iter *Iterator, target int) []byte {
var found []byte
n := 0
iter.ReadArrayCB(func(iter *Iterator) bool {
if n == target {
found = iter.SkipAndReturnBytes()
return false
}
iter.Skip()
n++
return true
})
return found
}
func locatePath(iter *Iterator, path []interface{}) Any {
for i, pathKeyObj := range path {
switch pathKey := pathKeyObj.(type) {
case string:
valueBytes := locateObjectField(iter, pathKey)
if valueBytes == nil {
return newInvalidAny(path[i:])
}
iter.ResetBytes(valueBytes)
case int:
valueBytes := locateArrayElement(iter, pathKey)
if valueBytes == nil {
return newInvalidAny(path[i:])
}
iter.ResetBytes(valueBytes)
case int32:
if '*' == pathKey {
return iter.readAny().Get(path[i:]...)
}
return newInvalidAny(path[i:])
default:
return newInvalidAny(path[i:])
}
}
if iter.Error != nil && iter.Error != io.EOF {
return &invalidAny{baseAny{}, iter.Error}
}
return iter.readAny()
}
var anyType = reflect2.TypeOfPtr((*Any)(nil)).Elem()
func createDecoderOfAny(ctx *ctx, typ reflect2.Type) ValDecoder {
if typ == anyType {
return &directAnyCodec{}
}
if typ.Implements(anyType) {
return &anyCodec{
valType: typ,
}
}
return nil
}
func createEncoderOfAny(ctx *ctx, typ reflect2.Type) ValEncoder {
if typ == anyType {
return &directAnyCodec{}
}
if typ.Implements(anyType) {
return &anyCodec{
valType: typ,
}
}
return nil
}
type anyCodec struct {
valType reflect2.Type
}
func (codec *anyCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
panic("not implemented")
}
func (codec *anyCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
obj := codec.valType.UnsafeIndirect(ptr)
any := obj.(Any)
any.WriteTo(stream)
}
func (codec *anyCodec) IsEmpty(ptr unsafe.Pointer) bool {
obj := codec.valType.UnsafeIndirect(ptr)
any := obj.(Any)
return any.Size() == 0
}
type directAnyCodec struct {
}
func (codec *directAnyCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*(*Any)(ptr) = iter.readAny()
}
func (codec *directAnyCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
any := *(*Any)(ptr)
if any == nil {
stream.WriteNil()
return
}
any.WriteTo(stream)
}
func (codec *directAnyCodec) IsEmpty(ptr unsafe.Pointer) bool {
any := *(*Any)(ptr)
return any.Size() == 0
} }

278
any_array.go Normal file
View File

@ -0,0 +1,278 @@
package jsoniter
import (
"reflect"
"unsafe"
)
type arrayLazyAny struct {
baseAny
cfg *frozenConfig
buf []byte
err error
}
func (any *arrayLazyAny) ValueType() ValueType {
return ArrayValue
}
func (any *arrayLazyAny) MustBeValid() Any {
return any
}
func (any *arrayLazyAny) LastError() error {
return any.err
}
func (any *arrayLazyAny) ToBool() bool {
iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter)
return iter.ReadArray()
}
func (any *arrayLazyAny) ToInt() int {
if any.ToBool() {
return 1
}
return 0
}
func (any *arrayLazyAny) ToInt32() int32 {
if any.ToBool() {
return 1
}
return 0
}
func (any *arrayLazyAny) ToInt64() int64 {
if any.ToBool() {
return 1
}
return 0
}
func (any *arrayLazyAny) ToUint() uint {
if any.ToBool() {
return 1
}
return 0
}
func (any *arrayLazyAny) ToUint32() uint32 {
if any.ToBool() {
return 1
}
return 0
}
func (any *arrayLazyAny) ToUint64() uint64 {
if any.ToBool() {
return 1
}
return 0
}
func (any *arrayLazyAny) ToFloat32() float32 {
if any.ToBool() {
return 1
}
return 0
}
func (any *arrayLazyAny) ToFloat64() float64 {
if any.ToBool() {
return 1
}
return 0
}
func (any *arrayLazyAny) ToString() string {
return *(*string)(unsafe.Pointer(&any.buf))
}
func (any *arrayLazyAny) ToVal(val interface{}) {
iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter)
iter.ReadVal(val)
}
func (any *arrayLazyAny) Get(path ...interface{}) Any {
if len(path) == 0 {
return any
}
switch firstPath := path[0].(type) {
case int:
iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter)
valueBytes := locateArrayElement(iter, firstPath)
if valueBytes == nil {
return newInvalidAny(path)
}
iter.ResetBytes(valueBytes)
return locatePath(iter, path[1:])
case int32:
if '*' == firstPath {
iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter)
arr := make([]Any, 0)
iter.ReadArrayCB(func(iter *Iterator) bool {
found := iter.readAny().Get(path[1:]...)
if found.ValueType() != InvalidValue {
arr = append(arr, found)
}
return true
})
return wrapArray(arr)
}
return newInvalidAny(path)
default:
return newInvalidAny(path)
}
}
func (any *arrayLazyAny) Size() int {
size := 0
iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter)
iter.ReadArrayCB(func(iter *Iterator) bool {
size++
iter.Skip()
return true
})
return size
}
func (any *arrayLazyAny) WriteTo(stream *Stream) {
stream.Write(any.buf)
}
func (any *arrayLazyAny) GetInterface() interface{} {
iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter)
return iter.Read()
}
type arrayAny struct {
baseAny
val reflect.Value
}
func wrapArray(val interface{}) *arrayAny {
return &arrayAny{baseAny{}, reflect.ValueOf(val)}
}
func (any *arrayAny) ValueType() ValueType {
return ArrayValue
}
func (any *arrayAny) MustBeValid() Any {
return any
}
func (any *arrayAny) LastError() error {
return nil
}
func (any *arrayAny) ToBool() bool {
return any.val.Len() != 0
}
func (any *arrayAny) ToInt() int {
if any.val.Len() == 0 {
return 0
}
return 1
}
func (any *arrayAny) ToInt32() int32 {
if any.val.Len() == 0 {
return 0
}
return 1
}
func (any *arrayAny) ToInt64() int64 {
if any.val.Len() == 0 {
return 0
}
return 1
}
func (any *arrayAny) ToUint() uint {
if any.val.Len() == 0 {
return 0
}
return 1
}
func (any *arrayAny) ToUint32() uint32 {
if any.val.Len() == 0 {
return 0
}
return 1
}
func (any *arrayAny) ToUint64() uint64 {
if any.val.Len() == 0 {
return 0
}
return 1
}
func (any *arrayAny) ToFloat32() float32 {
if any.val.Len() == 0 {
return 0
}
return 1
}
func (any *arrayAny) ToFloat64() float64 {
if any.val.Len() == 0 {
return 0
}
return 1
}
func (any *arrayAny) ToString() string {
str, _ := MarshalToString(any.val.Interface())
return str
}
func (any *arrayAny) Get(path ...interface{}) Any {
if len(path) == 0 {
return any
}
switch firstPath := path[0].(type) {
case int:
if firstPath < 0 || firstPath >= any.val.Len() {
return newInvalidAny(path)
}
return Wrap(any.val.Index(firstPath).Interface())
case int32:
if '*' == firstPath {
mappedAll := make([]Any, 0)
for i := 0; i < any.val.Len(); i++ {
mapped := Wrap(any.val.Index(i).Interface()).Get(path[1:]...)
if mapped.ValueType() != InvalidValue {
mappedAll = append(mappedAll, mapped)
}
}
return wrapArray(mappedAll)
}
return newInvalidAny(path)
default:
return newInvalidAny(path)
}
}
func (any *arrayAny) Size() int {
return any.val.Len()
}
func (any *arrayAny) WriteTo(stream *Stream) {
stream.WriteVal(any.val)
}
func (any *arrayAny) GetInterface() interface{} {
return any.val.Interface()
}

137
any_bool.go Normal file
View File

@ -0,0 +1,137 @@
package jsoniter
type trueAny struct {
baseAny
}
func (any *trueAny) LastError() error {
return nil
}
func (any *trueAny) ToBool() bool {
return true
}
func (any *trueAny) ToInt() int {
return 1
}
func (any *trueAny) ToInt32() int32 {
return 1
}
func (any *trueAny) ToInt64() int64 {
return 1
}
func (any *trueAny) ToUint() uint {
return 1
}
func (any *trueAny) ToUint32() uint32 {
return 1
}
func (any *trueAny) ToUint64() uint64 {
return 1
}
func (any *trueAny) ToFloat32() float32 {
return 1
}
func (any *trueAny) ToFloat64() float64 {
return 1
}
func (any *trueAny) ToString() string {
return "true"
}
func (any *trueAny) WriteTo(stream *Stream) {
stream.WriteTrue()
}
func (any *trueAny) Parse() *Iterator {
return nil
}
func (any *trueAny) GetInterface() interface{} {
return true
}
func (any *trueAny) ValueType() ValueType {
return BoolValue
}
func (any *trueAny) MustBeValid() Any {
return any
}
type falseAny struct {
baseAny
}
func (any *falseAny) LastError() error {
return nil
}
func (any *falseAny) ToBool() bool {
return false
}
func (any *falseAny) ToInt() int {
return 0
}
func (any *falseAny) ToInt32() int32 {
return 0
}
func (any *falseAny) ToInt64() int64 {
return 0
}
func (any *falseAny) ToUint() uint {
return 0
}
func (any *falseAny) ToUint32() uint32 {
return 0
}
func (any *falseAny) ToUint64() uint64 {
return 0
}
func (any *falseAny) ToFloat32() float32 {
return 0
}
func (any *falseAny) ToFloat64() float64 {
return 0
}
func (any *falseAny) ToString() string {
return "false"
}
func (any *falseAny) WriteTo(stream *Stream) {
stream.WriteFalse()
}
func (any *falseAny) Parse() *Iterator {
return nil
}
func (any *falseAny) GetInterface() interface{} {
return false
}
func (any *falseAny) ValueType() ValueType {
return BoolValue
}
func (any *falseAny) MustBeValid() Any {
return any
}

83
any_float.go Normal file
View File

@ -0,0 +1,83 @@
package jsoniter
import (
"strconv"
)
type floatAny struct {
baseAny
val float64
}
func (any *floatAny) Parse() *Iterator {
return nil
}
func (any *floatAny) ValueType() ValueType {
return NumberValue
}
func (any *floatAny) MustBeValid() Any {
return any
}
func (any *floatAny) LastError() error {
return nil
}
func (any *floatAny) ToBool() bool {
return any.ToFloat64() != 0
}
func (any *floatAny) ToInt() int {
return int(any.val)
}
func (any *floatAny) ToInt32() int32 {
return int32(any.val)
}
func (any *floatAny) ToInt64() int64 {
return int64(any.val)
}
func (any *floatAny) ToUint() uint {
if any.val > 0 {
return uint(any.val)
}
return 0
}
func (any *floatAny) ToUint32() uint32 {
if any.val > 0 {
return uint32(any.val)
}
return 0
}
func (any *floatAny) ToUint64() uint64 {
if any.val > 0 {
return uint64(any.val)
}
return 0
}
func (any *floatAny) ToFloat32() float32 {
return float32(any.val)
}
func (any *floatAny) ToFloat64() float64 {
return any.val
}
func (any *floatAny) ToString() string {
return strconv.FormatFloat(any.val, 'E', -1, 64)
}
func (any *floatAny) WriteTo(stream *Stream) {
stream.WriteFloat64(any.val)
}
func (any *floatAny) GetInterface() interface{} {
return any.val
}

74
any_int32.go Normal file
View File

@ -0,0 +1,74 @@
package jsoniter
import (
"strconv"
)
type int32Any struct {
baseAny
val int32
}
func (any *int32Any) LastError() error {
return nil
}
func (any *int32Any) ValueType() ValueType {
return NumberValue
}
func (any *int32Any) MustBeValid() Any {
return any
}
func (any *int32Any) ToBool() bool {
return any.val != 0
}
func (any *int32Any) ToInt() int {
return int(any.val)
}
func (any *int32Any) ToInt32() int32 {
return any.val
}
func (any *int32Any) ToInt64() int64 {
return int64(any.val)
}
func (any *int32Any) ToUint() uint {
return uint(any.val)
}
func (any *int32Any) ToUint32() uint32 {
return uint32(any.val)
}
func (any *int32Any) ToUint64() uint64 {
return uint64(any.val)
}
func (any *int32Any) ToFloat32() float32 {
return float32(any.val)
}
func (any *int32Any) ToFloat64() float64 {
return float64(any.val)
}
func (any *int32Any) ToString() string {
return strconv.FormatInt(int64(any.val), 10)
}
func (any *int32Any) WriteTo(stream *Stream) {
stream.WriteInt32(any.val)
}
func (any *int32Any) Parse() *Iterator {
return nil
}
func (any *int32Any) GetInterface() interface{} {
return any.val
}

74
any_int64.go Normal file
View File

@ -0,0 +1,74 @@
package jsoniter
import (
"strconv"
)
type int64Any struct {
baseAny
val int64
}
func (any *int64Any) LastError() error {
return nil
}
func (any *int64Any) ValueType() ValueType {
return NumberValue
}
func (any *int64Any) MustBeValid() Any {
return any
}
func (any *int64Any) ToBool() bool {
return any.val != 0
}
func (any *int64Any) ToInt() int {
return int(any.val)
}
func (any *int64Any) ToInt32() int32 {
return int32(any.val)
}
func (any *int64Any) ToInt64() int64 {
return any.val
}
func (any *int64Any) ToUint() uint {
return uint(any.val)
}
func (any *int64Any) ToUint32() uint32 {
return uint32(any.val)
}
func (any *int64Any) ToUint64() uint64 {
return uint64(any.val)
}
func (any *int64Any) ToFloat32() float32 {
return float32(any.val)
}
func (any *int64Any) ToFloat64() float64 {
return float64(any.val)
}
func (any *int64Any) ToString() string {
return strconv.FormatInt(any.val, 10)
}
func (any *int64Any) WriteTo(stream *Stream) {
stream.WriteInt64(any.val)
}
func (any *int64Any) Parse() *Iterator {
return nil
}
func (any *int64Any) GetInterface() interface{} {
return any.val
}

82
any_invalid.go Normal file
View File

@ -0,0 +1,82 @@
package jsoniter
import "fmt"
type invalidAny struct {
baseAny
err error
}
func newInvalidAny(path []interface{}) *invalidAny {
return &invalidAny{baseAny{}, fmt.Errorf("%v not found", path)}
}
func (any *invalidAny) LastError() error {
return any.err
}
func (any *invalidAny) ValueType() ValueType {
return InvalidValue
}
func (any *invalidAny) MustBeValid() Any {
panic(any.err)
}
func (any *invalidAny) ToBool() bool {
return false
}
func (any *invalidAny) ToInt() int {
return 0
}
func (any *invalidAny) ToInt32() int32 {
return 0
}
func (any *invalidAny) ToInt64() int64 {
return 0
}
func (any *invalidAny) ToUint() uint {
return 0
}
func (any *invalidAny) ToUint32() uint32 {
return 0
}
func (any *invalidAny) ToUint64() uint64 {
return 0
}
func (any *invalidAny) ToFloat32() float32 {
return 0
}
func (any *invalidAny) ToFloat64() float64 {
return 0
}
func (any *invalidAny) ToString() string {
return ""
}
func (any *invalidAny) WriteTo(stream *Stream) {
}
func (any *invalidAny) Get(path ...interface{}) Any {
if any.err == nil {
return &invalidAny{baseAny{}, fmt.Errorf("get %v from invalid", path)}
}
return &invalidAny{baseAny{}, fmt.Errorf("%v, get %v from invalid", any.err, path)}
}
func (any *invalidAny) Parse() *Iterator {
return nil
}
func (any *invalidAny) GetInterface() interface{} {
return nil
}

69
any_nil.go Normal file
View File

@ -0,0 +1,69 @@
package jsoniter
type nilAny struct {
baseAny
}
func (any *nilAny) LastError() error {
return nil
}
func (any *nilAny) ValueType() ValueType {
return NilValue
}
func (any *nilAny) MustBeValid() Any {
return any
}
func (any *nilAny) ToBool() bool {
return false
}
func (any *nilAny) ToInt() int {
return 0
}
func (any *nilAny) ToInt32() int32 {
return 0
}
func (any *nilAny) ToInt64() int64 {
return 0
}
func (any *nilAny) ToUint() uint {
return 0
}
func (any *nilAny) ToUint32() uint32 {
return 0
}
func (any *nilAny) ToUint64() uint64 {
return 0
}
func (any *nilAny) ToFloat32() float32 {
return 0
}
func (any *nilAny) ToFloat64() float64 {
return 0
}
func (any *nilAny) ToString() string {
return ""
}
func (any *nilAny) WriteTo(stream *Stream) {
stream.WriteNil()
}
func (any *nilAny) Parse() *Iterator {
return nil
}
func (any *nilAny) GetInterface() interface{} {
return nil
}

123
any_number.go Normal file
View File

@ -0,0 +1,123 @@
package jsoniter
import (
"io"
"unsafe"
)
type numberLazyAny struct {
baseAny
cfg *frozenConfig
buf []byte
err error
}
func (any *numberLazyAny) ValueType() ValueType {
return NumberValue
}
func (any *numberLazyAny) MustBeValid() Any {
return any
}
func (any *numberLazyAny) LastError() error {
return any.err
}
func (any *numberLazyAny) ToBool() bool {
return any.ToFloat64() != 0
}
func (any *numberLazyAny) ToInt() int {
iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter)
val := iter.ReadInt()
if iter.Error != nil && iter.Error != io.EOF {
any.err = iter.Error
}
return val
}
func (any *numberLazyAny) ToInt32() int32 {
iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter)
val := iter.ReadInt32()
if iter.Error != nil && iter.Error != io.EOF {
any.err = iter.Error
}
return val
}
func (any *numberLazyAny) ToInt64() int64 {
iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter)
val := iter.ReadInt64()
if iter.Error != nil && iter.Error != io.EOF {
any.err = iter.Error
}
return val
}
func (any *numberLazyAny) ToUint() uint {
iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter)
val := iter.ReadUint()
if iter.Error != nil && iter.Error != io.EOF {
any.err = iter.Error
}
return val
}
func (any *numberLazyAny) ToUint32() uint32 {
iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter)
val := iter.ReadUint32()
if iter.Error != nil && iter.Error != io.EOF {
any.err = iter.Error
}
return val
}
func (any *numberLazyAny) ToUint64() uint64 {
iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter)
val := iter.ReadUint64()
if iter.Error != nil && iter.Error != io.EOF {
any.err = iter.Error
}
return val
}
func (any *numberLazyAny) ToFloat32() float32 {
iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter)
val := iter.ReadFloat32()
if iter.Error != nil && iter.Error != io.EOF {
any.err = iter.Error
}
return val
}
func (any *numberLazyAny) ToFloat64() float64 {
iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter)
val := iter.ReadFloat64()
if iter.Error != nil && iter.Error != io.EOF {
any.err = iter.Error
}
return val
}
func (any *numberLazyAny) ToString() string {
return *(*string)(unsafe.Pointer(&any.buf))
}
func (any *numberLazyAny) WriteTo(stream *Stream) {
stream.Write(any.buf)
}
func (any *numberLazyAny) GetInterface() interface{} {
iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter)
return iter.Read()
}

374
any_object.go Normal file
View File

@ -0,0 +1,374 @@
package jsoniter
import (
"reflect"
"unsafe"
)
type objectLazyAny struct {
baseAny
cfg *frozenConfig
buf []byte
err error
}
func (any *objectLazyAny) ValueType() ValueType {
return ObjectValue
}
func (any *objectLazyAny) MustBeValid() Any {
return any
}
func (any *objectLazyAny) LastError() error {
return any.err
}
func (any *objectLazyAny) ToBool() bool {
return true
}
func (any *objectLazyAny) ToInt() int {
return 0
}
func (any *objectLazyAny) ToInt32() int32 {
return 0
}
func (any *objectLazyAny) ToInt64() int64 {
return 0
}
func (any *objectLazyAny) ToUint() uint {
return 0
}
func (any *objectLazyAny) ToUint32() uint32 {
return 0
}
func (any *objectLazyAny) ToUint64() uint64 {
return 0
}
func (any *objectLazyAny) ToFloat32() float32 {
return 0
}
func (any *objectLazyAny) ToFloat64() float64 {
return 0
}
func (any *objectLazyAny) ToString() string {
return *(*string)(unsafe.Pointer(&any.buf))
}
func (any *objectLazyAny) ToVal(obj interface{}) {
iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter)
iter.ReadVal(obj)
}
func (any *objectLazyAny) Get(path ...interface{}) Any {
if len(path) == 0 {
return any
}
switch firstPath := path[0].(type) {
case string:
iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter)
valueBytes := locateObjectField(iter, firstPath)
if valueBytes == nil {
return newInvalidAny(path)
}
iter.ResetBytes(valueBytes)
return locatePath(iter, path[1:])
case int32:
if '*' == firstPath {
mappedAll := map[string]Any{}
iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter)
iter.ReadMapCB(func(iter *Iterator, field string) bool {
mapped := locatePath(iter, path[1:])
if mapped.ValueType() != InvalidValue {
mappedAll[field] = mapped
}
return true
})
return wrapMap(mappedAll)
}
return newInvalidAny(path)
default:
return newInvalidAny(path)
}
}
func (any *objectLazyAny) Keys() []string {
keys := []string{}
iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter)
iter.ReadMapCB(func(iter *Iterator, field string) bool {
iter.Skip()
keys = append(keys, field)
return true
})
return keys
}
func (any *objectLazyAny) Size() int {
size := 0
iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter)
iter.ReadObjectCB(func(iter *Iterator, field string) bool {
iter.Skip()
size++
return true
})
return size
}
func (any *objectLazyAny) WriteTo(stream *Stream) {
stream.Write(any.buf)
}
func (any *objectLazyAny) GetInterface() interface{} {
iter := any.cfg.BorrowIterator(any.buf)
defer any.cfg.ReturnIterator(iter)
return iter.Read()
}
type objectAny struct {
baseAny
err error
val reflect.Value
}
func wrapStruct(val interface{}) *objectAny {
return &objectAny{baseAny{}, nil, reflect.ValueOf(val)}
}
func (any *objectAny) ValueType() ValueType {
return ObjectValue
}
func (any *objectAny) MustBeValid() Any {
return any
}
func (any *objectAny) Parse() *Iterator {
return nil
}
func (any *objectAny) LastError() error {
return any.err
}
func (any *objectAny) ToBool() bool {
return any.val.NumField() != 0
}
func (any *objectAny) ToInt() int {
return 0
}
func (any *objectAny) ToInt32() int32 {
return 0
}
func (any *objectAny) ToInt64() int64 {
return 0
}
func (any *objectAny) ToUint() uint {
return 0
}
func (any *objectAny) ToUint32() uint32 {
return 0
}
func (any *objectAny) ToUint64() uint64 {
return 0
}
func (any *objectAny) ToFloat32() float32 {
return 0
}
func (any *objectAny) ToFloat64() float64 {
return 0
}
func (any *objectAny) ToString() string {
str, err := MarshalToString(any.val.Interface())
any.err = err
return str
}
func (any *objectAny) Get(path ...interface{}) Any {
if len(path) == 0 {
return any
}
switch firstPath := path[0].(type) {
case string:
field := any.val.FieldByName(firstPath)
if !field.IsValid() {
return newInvalidAny(path)
}
return Wrap(field.Interface())
case int32:
if '*' == firstPath {
mappedAll := map[string]Any{}
for i := 0; i < any.val.NumField(); i++ {
field := any.val.Field(i)
if field.CanInterface() {
mapped := Wrap(field.Interface()).Get(path[1:]...)
if mapped.ValueType() != InvalidValue {
mappedAll[any.val.Type().Field(i).Name] = mapped
}
}
}
return wrapMap(mappedAll)
}
return newInvalidAny(path)
default:
return newInvalidAny(path)
}
}
func (any *objectAny) Keys() []string {
keys := make([]string, 0, any.val.NumField())
for i := 0; i < any.val.NumField(); i++ {
keys = append(keys, any.val.Type().Field(i).Name)
}
return keys
}
func (any *objectAny) Size() int {
return any.val.NumField()
}
func (any *objectAny) WriteTo(stream *Stream) {
stream.WriteVal(any.val)
}
func (any *objectAny) GetInterface() interface{} {
return any.val.Interface()
}
type mapAny struct {
baseAny
err error
val reflect.Value
}
func wrapMap(val interface{}) *mapAny {
return &mapAny{baseAny{}, nil, reflect.ValueOf(val)}
}
func (any *mapAny) ValueType() ValueType {
return ObjectValue
}
func (any *mapAny) MustBeValid() Any {
return any
}
func (any *mapAny) Parse() *Iterator {
return nil
}
func (any *mapAny) LastError() error {
return any.err
}
func (any *mapAny) ToBool() bool {
return true
}
func (any *mapAny) ToInt() int {
return 0
}
func (any *mapAny) ToInt32() int32 {
return 0
}
func (any *mapAny) ToInt64() int64 {
return 0
}
func (any *mapAny) ToUint() uint {
return 0
}
func (any *mapAny) ToUint32() uint32 {
return 0
}
func (any *mapAny) ToUint64() uint64 {
return 0
}
func (any *mapAny) ToFloat32() float32 {
return 0
}
func (any *mapAny) ToFloat64() float64 {
return 0
}
func (any *mapAny) ToString() string {
str, err := MarshalToString(any.val.Interface())
any.err = err
return str
}
func (any *mapAny) Get(path ...interface{}) Any {
if len(path) == 0 {
return any
}
switch firstPath := path[0].(type) {
case int32:
if '*' == firstPath {
mappedAll := map[string]Any{}
for _, key := range any.val.MapKeys() {
keyAsStr := key.String()
element := Wrap(any.val.MapIndex(key).Interface())
mapped := element.Get(path[1:]...)
if mapped.ValueType() != InvalidValue {
mappedAll[keyAsStr] = mapped
}
}
return wrapMap(mappedAll)
}
return newInvalidAny(path)
default:
value := any.val.MapIndex(reflect.ValueOf(firstPath))
if !value.IsValid() {
return newInvalidAny(path)
}
return Wrap(value.Interface())
}
}
func (any *mapAny) Keys() []string {
keys := make([]string, 0, any.val.Len())
for _, key := range any.val.MapKeys() {
keys = append(keys, key.String())
}
return keys
}
func (any *mapAny) Size() int {
return any.val.Len()
}
func (any *mapAny) WriteTo(stream *Stream) {
stream.WriteVal(any.val)
}
func (any *mapAny) GetInterface() interface{} {
return any.val.Interface()
}

166
any_str.go Normal file
View File

@ -0,0 +1,166 @@
package jsoniter
import (
"fmt"
"strconv"
)
type stringAny struct {
baseAny
val string
}
func (any *stringAny) Get(path ...interface{}) Any {
if len(path) == 0 {
return any
}
return &invalidAny{baseAny{}, fmt.Errorf("GetIndex %v from simple value", path)}
}
func (any *stringAny) Parse() *Iterator {
return nil
}
func (any *stringAny) ValueType() ValueType {
return StringValue
}
func (any *stringAny) MustBeValid() Any {
return any
}
func (any *stringAny) LastError() error {
return nil
}
func (any *stringAny) ToBool() bool {
str := any.ToString()
if str == "0" {
return false
}
for _, c := range str {
switch c {
case ' ', '\n', '\r', '\t':
default:
return true
}
}
return false
}
func (any *stringAny) ToInt() int {
return int(any.ToInt64())
}
func (any *stringAny) ToInt32() int32 {
return int32(any.ToInt64())
}
func (any *stringAny) ToInt64() int64 {
if any.val == "" {
return 0
}
flag := 1
startPos := 0
if any.val[0] == '+' || any.val[0] == '-' {
startPos = 1
}
if any.val[0] == '-' {
flag = -1
}
endPos := startPos
for i := startPos; i < len(any.val); i++ {
if any.val[i] >= '0' && any.val[i] <= '9' {
endPos = i + 1
} else {
break
}
}
parsed, _ := strconv.ParseInt(any.val[startPos:endPos], 10, 64)
return int64(flag) * parsed
}
func (any *stringAny) ToUint() uint {
return uint(any.ToUint64())
}
func (any *stringAny) ToUint32() uint32 {
return uint32(any.ToUint64())
}
func (any *stringAny) ToUint64() uint64 {
if any.val == "" {
return 0
}
startPos := 0
if any.val[0] == '-' {
return 0
}
if any.val[0] == '+' {
startPos = 1
}
endPos := startPos
for i := startPos; i < len(any.val); i++ {
if any.val[i] >= '0' && any.val[i] <= '9' {
endPos = i + 1
} else {
break
}
}
parsed, _ := strconv.ParseUint(any.val[startPos:endPos], 10, 64)
return parsed
}
func (any *stringAny) ToFloat32() float32 {
return float32(any.ToFloat64())
}
func (any *stringAny) ToFloat64() float64 {
if len(any.val) == 0 {
return 0
}
// first char invalid
if any.val[0] != '+' && any.val[0] != '-' && (any.val[0] > '9' || any.val[0] < '0') {
return 0
}
// extract valid num expression from string
// eg 123true => 123, -12.12xxa => -12.12
endPos := 1
for i := 1; i < len(any.val); i++ {
if any.val[i] == '.' || any.val[i] == 'e' || any.val[i] == 'E' || any.val[i] == '+' || any.val[i] == '-' {
endPos = i + 1
continue
}
// end position is the first char which is not digit
if any.val[i] >= '0' && any.val[i] <= '9' {
endPos = i + 1
} else {
endPos = i
break
}
}
parsed, _ := strconv.ParseFloat(any.val[:endPos], 64)
return parsed
}
func (any *stringAny) ToString() string {
return any.val
}
func (any *stringAny) WriteTo(stream *Stream) {
stream.WriteString(any.val)
}
func (any *stringAny) GetInterface() interface{} {
return any.val
}

View File

@ -1,72 +0,0 @@
package jsoniter
import (
"testing"
"fmt"
)
func Test_get_from_map(t *testing.T) {
any := Any{val: map[string]interface{}{
"hello": "world",
}}
if any.ToString("hello") != "world" {
t.FailNow()
}
}
func Test_get_from_array(t *testing.T) {
any := Any{val: []interface{}{
"hello", "world",
}}
if any.ToString(1) != "world" {
t.FailNow()
}
}
func Test_get_int(t *testing.T) {
any := Any{val: []interface{}{
1, 2, 3,
}}
if any.ToInt(1) != 2 {
t.FailNow()
}
}
func Test_is_null(t *testing.T) {
any := Any{val: []interface{}{
1, 2, 3,
}}
if any.IsNull() != false {
t.FailNow()
}
}
func Test_get_bool(t *testing.T) {
any := Any{val: []interface{}{
true, true, false,
}}
if any.ToBool(1) != true {
t.FailNow()
}
}
func Test_nested_read(t *testing.T) {
any := Any{val: []interface{}{
true, map[string]interface{}{
"hello": "world",
}, false,
}}
if any.ToString(1, "hello") != "world" {
fmt.Println(any.Error)
t.FailNow()
}
}
func Test_int_to_string(t *testing.T) {
any := Any{val: []interface{}{
true, 5, false,
}}
if any.ToString(1) != "5" {
t.FailNow()
}
}

View File

@ -0,0 +1,123 @@
package any_tests
import (
"testing"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
func Test_read_empty_array_as_any(t *testing.T) {
should := require.New(t)
any := jsoniter.Get([]byte("[]"))
should.Equal(jsoniter.ArrayValue, any.Get().ValueType())
should.Equal(jsoniter.InvalidValue, any.Get(0.3).ValueType())
should.Equal(0, any.Size())
should.Equal(jsoniter.ArrayValue, any.ValueType())
should.Nil(any.LastError())
should.Equal(0, any.ToInt())
should.Equal(int32(0), any.ToInt32())
should.Equal(int64(0), any.ToInt64())
should.Equal(uint(0), any.ToUint())
should.Equal(uint32(0), any.ToUint32())
should.Equal(uint64(0), any.ToUint64())
should.Equal(float32(0), any.ToFloat32())
should.Equal(float64(0), any.ToFloat64())
}
func Test_read_one_element_array_as_any(t *testing.T) {
should := require.New(t)
any := jsoniter.Get([]byte("[1]"))
should.Equal(1, any.Size())
}
func Test_read_two_element_array_as_any(t *testing.T) {
should := require.New(t)
any := jsoniter.Get([]byte("[1,2]"))
should.Equal(1, any.Get(0).ToInt())
should.Equal(2, any.Size())
should.True(any.ToBool())
should.Equal(1, any.ToInt())
should.Equal([]interface{}{float64(1), float64(2)}, any.GetInterface())
stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
any.WriteTo(stream)
should.Equal("[1,2]", string(stream.Buffer()))
arr := []int{}
any.ToVal(&arr)
should.Equal([]int{1, 2}, arr)
}
func Test_wrap_array_and_convert_to_any(t *testing.T) {
should := require.New(t)
any := jsoniter.Wrap([]int{1, 2, 3})
any2 := jsoniter.Wrap([]int{})
should.Equal("[1,2,3]", any.ToString())
should.True(any.ToBool())
should.False(any2.ToBool())
should.Equal(1, any.ToInt())
should.Equal(0, any2.ToInt())
should.Equal(int32(1), any.ToInt32())
should.Equal(int32(0), any2.ToInt32())
should.Equal(int64(1), any.ToInt64())
should.Equal(int64(0), any2.ToInt64())
should.Equal(uint(1), any.ToUint())
should.Equal(uint(0), any2.ToUint())
should.Equal(uint32(1), any.ToUint32())
should.Equal(uint32(0), any2.ToUint32())
should.Equal(uint64(1), any.ToUint64())
should.Equal(uint64(0), any2.ToUint64())
should.Equal(float32(1), any.ToFloat32())
should.Equal(float32(0), any2.ToFloat32())
should.Equal(float64(1), any.ToFloat64())
should.Equal(float64(0), any2.ToFloat64())
should.Equal(3, any.Size())
should.Equal(0, any2.Size())
var i interface{} = []int{1, 2, 3}
should.Equal(i, any.GetInterface())
}
func Test_array_lazy_any_get(t *testing.T) {
should := require.New(t)
any := jsoniter.Get([]byte("[1,[2,3],4]"))
should.Equal(3, any.Get(1, 1).ToInt())
should.Equal("[1,[2,3],4]", any.ToString())
}
func Test_array_lazy_any_get_all(t *testing.T) {
should := require.New(t)
any := jsoniter.Get([]byte("[[1],[2],[3,4]]"))
should.Equal("[1,2,3]", any.Get('*', 0).ToString())
any = jsoniter.Get([]byte("[[[1],[2],[3,4]]]"), 0, '*', 0)
should.Equal("[1,2,3]", any.ToString())
}
func Test_array_wrapper_any_get_all(t *testing.T) {
should := require.New(t)
any := jsoniter.Wrap([][]int{
{1, 2},
{3, 4},
{5, 6},
})
should.Equal("[1,3,5]", any.Get('*', 0).ToString())
should.Equal(jsoniter.ArrayValue, any.ValueType())
should.True(any.ToBool())
should.Equal(1, any.Get(0, 0).ToInt())
}
func Test_array_lazy_any_get_invalid(t *testing.T) {
should := require.New(t)
any := jsoniter.Get([]byte("[]"))
should.Equal(jsoniter.InvalidValue, any.Get(1, 1).ValueType())
should.NotNil(any.Get(1, 1).LastError())
should.Equal(jsoniter.InvalidValue, any.Get("1").ValueType())
should.NotNil(any.Get("1").LastError())
}
func Test_invalid_array(t *testing.T) {
should := require.New(t)
any := jsoniter.Get([]byte("["), 0)
should.Equal(jsoniter.InvalidValue, any.ValueType())
}

View File

@ -0,0 +1,65 @@
package any_tests
import (
"fmt"
"testing"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
var boolConvertMap = map[string]bool{
"null": false,
"true": true,
"false": false,
`"true"`: true,
`"false"`: true,
"123": true,
`"123"`: true,
"0": false,
`"0"`: false,
"-1": true,
`"-1"`: true,
"1.1": true,
"0.0": false,
"-1.1": true,
`""`: false,
"[1,2]": true,
"[]": false,
"{}": true,
`{"abc":1}`: true,
}
func Test_read_bool_as_any(t *testing.T) {
should := require.New(t)
var any jsoniter.Any
for k, v := range boolConvertMap {
any = jsoniter.Get([]byte(k))
if v {
should.True(any.ToBool(), fmt.Sprintf("origin val is %v", k))
} else {
should.False(any.ToBool(), fmt.Sprintf("origin val is %v", k))
}
}
}
func Test_write_bool_to_stream(t *testing.T) {
should := require.New(t)
any := jsoniter.Get([]byte("true"))
stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
any.WriteTo(stream)
should.Equal("true", string(stream.Buffer()))
should.Equal(any.ValueType(), jsoniter.BoolValue)
any = jsoniter.Get([]byte("false"))
stream = jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
any.WriteTo(stream)
should.Equal("false", string(stream.Buffer()))
should.Equal(any.ValueType(), jsoniter.BoolValue)
}

View File

@ -0,0 +1,101 @@
package any_tests
import (
"testing"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
var floatConvertMap = map[string]float64{
"null": 0,
"true": 1,
"false": 0,
`"true"`: 0,
`"false"`: 0,
"1e1": 10,
"1e+1": 10,
"1e-1": .1,
"1E1": 10,
"1E+1": 10,
"1E-1": .1,
"-1e1": -10,
"-1e+1": -10,
"-1e-1": -.1,
"-1E1": -10,
"-1E+1": -10,
"-1E-1": -.1,
`"1e1"`: 10,
`"1e+1"`: 10,
`"1e-1"`: .1,
`"1E1"`: 10,
`"1E+1"`: 10,
`"1E-1"`: .1,
`"-1e1"`: -10,
`"-1e+1"`: -10,
`"-1e-1"`: -.1,
`"-1E1"`: -10,
`"-1E+1"`: -10,
`"-1E-1"`: -.1,
"123": 123,
`"123true"`: 123,
`"+"`: 0,
`"-"`: 0,
`"-123true"`: -123,
`"-99.9true"`: -99.9,
"0": 0,
`"0"`: 0,
"-1": -1,
"1.1": 1.1,
"0.0": 0,
"-1.1": -1.1,
`"+1.1"`: 1.1,
`""`: 0,
"[1,2]": 1,
"[]": 0,
"{}": 0,
`{"abc":1}`: 0,
}
func Test_read_any_to_float(t *testing.T) {
should := require.New(t)
for k, v := range floatConvertMap {
any := jsoniter.Get([]byte(k))
should.Equal(float64(v), any.ToFloat64(), "the original val is "+k)
}
for k, v := range floatConvertMap {
any := jsoniter.Get([]byte(k))
should.Equal(float32(v), any.ToFloat32(), "the original val is "+k)
}
}
func Test_read_float_to_any(t *testing.T) {
should := require.New(t)
any := jsoniter.WrapFloat64(12.3)
anyFloat64 := float64(12.3)
any2 := jsoniter.WrapFloat64(-1.1)
should.Equal(float64(12.3), any.ToFloat64())
should.True(any.ToBool())
should.Equal(float32(anyFloat64), any.ToFloat32())
should.Equal(int(anyFloat64), any.ToInt())
should.Equal(int32(anyFloat64), any.ToInt32())
should.Equal(int64(anyFloat64), any.ToInt64())
should.Equal(uint(anyFloat64), any.ToUint())
should.Equal(uint32(anyFloat64), any.ToUint32())
should.Equal(uint64(anyFloat64), any.ToUint64())
should.Equal(uint(0), any2.ToUint())
should.Equal(uint32(0), any2.ToUint32())
should.Equal(uint64(0), any2.ToUint64())
should.Equal(any.ValueType(), jsoniter.NumberValue)
should.Equal("1.23E+01", any.ToString())
}

View File

@ -0,0 +1,198 @@
package any_tests
import (
"fmt"
"testing"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
var intConvertMap = map[string]int{
"null": 0,
"321.1": 321,
"-321.1": -321,
`"1.1"`: 1,
`"-321.1"`: -321,
"0.0": 0,
"0": 0,
`"0"`: 0,
`"0.0"`: 0,
"-1.1": -1,
"true": 1,
"false": 0,
`"true"`: 0,
`"false"`: 0,
`"true123"`: 0,
`"123true"`: 123,
`"-123true"`: -123,
`"1.2332e6"`: 1,
`""`: 0,
"+": 0,
"-": 0,
"[]": 0,
"[1,2]": 1,
`["1","2"]`: 1,
// object in php cannot convert to int
"{}": 0,
}
func Test_read_any_to_int(t *testing.T) {
should := require.New(t)
// int
for k, v := range intConvertMap {
any := jsoniter.Get([]byte(k))
should.Equal(v, any.ToInt(), fmt.Sprintf("origin val %v", k))
}
// int32
for k, v := range intConvertMap {
any := jsoniter.Get([]byte(k))
should.Equal(int32(v), any.ToInt32(), fmt.Sprintf("original val is %v", k))
}
// int64
for k, v := range intConvertMap {
any := jsoniter.Get([]byte(k))
should.Equal(int64(v), any.ToInt64(), fmt.Sprintf("original val is %v", k))
}
}
var uintConvertMap = map[string]int{
"null": 0,
"321.1": 321,
`"1.1"`: 1,
`"-123.1"`: 0,
"0.0": 0,
"0": 0,
`"0"`: 0,
`"0.0"`: 0,
`"00.0"`: 0,
"true": 1,
"false": 0,
`"true"`: 0,
`"false"`: 0,
`"true123"`: 0,
`"+1"`: 1,
`"123true"`: 123,
`"-123true"`: 0,
`"1.2332e6"`: 1,
`""`: 0,
"+": 0,
"-": 0,
".": 0,
"[]": 0,
"[1,2]": 1,
"{}": 0,
"{1,2}": 0,
"-1.1": 0,
"-321.1": 0,
}
func Test_read_any_to_uint(t *testing.T) {
should := require.New(t)
for k, v := range uintConvertMap {
any := jsoniter.Get([]byte(k))
should.Equal(uint64(v), any.ToUint64(), fmt.Sprintf("origin val %v", k))
}
for k, v := range uintConvertMap {
any := jsoniter.Get([]byte(k))
should.Equal(uint32(v), any.ToUint32(), fmt.Sprintf("origin val %v", k))
}
for k, v := range uintConvertMap {
any := jsoniter.Get([]byte(k))
should.Equal(uint(v), any.ToUint(), fmt.Sprintf("origin val %v", k))
}
}
func Test_read_int64_to_any(t *testing.T) {
should := require.New(t)
any := jsoniter.WrapInt64(12345)
should.Equal(12345, any.ToInt())
should.Equal(int32(12345), any.ToInt32())
should.Equal(int64(12345), any.ToInt64())
should.Equal(uint(12345), any.ToUint())
should.Equal(uint32(12345), any.ToUint32())
should.Equal(uint64(12345), any.ToUint64())
should.Equal(float32(12345), any.ToFloat32())
should.Equal(float64(12345), any.ToFloat64())
should.Equal("12345", any.ToString())
should.Equal(true, any.ToBool())
should.Equal(any.ValueType(), jsoniter.NumberValue)
stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
any.WriteTo(stream)
should.Equal("12345", string(stream.Buffer()))
}
func Test_read_int32_to_any(t *testing.T) {
should := require.New(t)
any := jsoniter.WrapInt32(12345)
should.Equal(12345, any.ToInt())
should.Equal(int32(12345), any.ToInt32())
should.Equal(int64(12345), any.ToInt64())
should.Equal(uint(12345), any.ToUint())
should.Equal(uint32(12345), any.ToUint32())
should.Equal(uint64(12345), any.ToUint64())
should.Equal(float32(12345), any.ToFloat32())
should.Equal(float64(12345), any.ToFloat64())
should.Equal("12345", any.ToString())
should.Equal(true, any.ToBool())
should.Equal(any.ValueType(), jsoniter.NumberValue)
stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
any.WriteTo(stream)
should.Equal("12345", string(stream.Buffer()))
}
func Test_read_uint32_to_any(t *testing.T) {
should := require.New(t)
any := jsoniter.WrapUint32(12345)
should.Equal(12345, any.ToInt())
should.Equal(int32(12345), any.ToInt32())
should.Equal(int64(12345), any.ToInt64())
should.Equal(uint(12345), any.ToUint())
should.Equal(uint32(12345), any.ToUint32())
should.Equal(uint64(12345), any.ToUint64())
should.Equal(float32(12345), any.ToFloat32())
should.Equal(float64(12345), any.ToFloat64())
should.Equal("12345", any.ToString())
should.Equal(true, any.ToBool())
should.Equal(any.ValueType(), jsoniter.NumberValue)
stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
any.WriteTo(stream)
should.Equal("12345", string(stream.Buffer()))
}
func Test_read_uint64_to_any(t *testing.T) {
should := require.New(t)
any := jsoniter.WrapUint64(12345)
should.Equal(12345, any.ToInt())
should.Equal(int32(12345), any.ToInt32())
should.Equal(int64(12345), any.ToInt64())
should.Equal(uint(12345), any.ToUint())
should.Equal(uint32(12345), any.ToUint32())
should.Equal(uint64(12345), any.ToUint64())
should.Equal(float32(12345), any.ToFloat32())
should.Equal(float64(12345), any.ToFloat64())
should.Equal("12345", any.ToString())
should.Equal(true, any.ToBool())
should.Equal(any.ValueType(), jsoniter.NumberValue)
stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
any.WriteTo(stream)
should.Equal("12345", string(stream.Buffer()))
stream = jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
stream.WriteUint(uint(123))
should.Equal("123", string(stream.Buffer()))
}
func Test_int_lazy_any_get(t *testing.T) {
should := require.New(t)
any := jsoniter.Get([]byte("1234"))
// panic!!
//should.Equal(any.LastError(), io.EOF)
should.Equal(jsoniter.InvalidValue, any.Get(1, "2").ValueType())
}

View File

@ -0,0 +1,28 @@
package any_tests
import (
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"testing"
)
func Test_wrap_map(t *testing.T) {
should := require.New(t)
any := jsoniter.Wrap(map[string]string{"Field1": "hello"})
should.Equal("hello", any.Get("Field1").ToString())
any = jsoniter.Wrap(map[string]string{"Field1": "hello"})
should.Equal(1, any.Size())
}
func Test_map_wrapper_any_get_all(t *testing.T) {
should := require.New(t)
any := jsoniter.Wrap(map[string][]int{"Field1": {1, 2}})
should.Equal(`{"Field1":1}`, any.Get('*', 0).ToString())
should.Contains(any.Keys(), "Field1")
// map write to
stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 0)
any.WriteTo(stream)
// TODO cannot pass
//should.Equal(string(stream.buf), "")
}

View File

@ -0,0 +1,16 @@
package any_tests
import (
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"testing"
)
func Test_read_null_as_any(t *testing.T) {
should := require.New(t)
any := jsoniter.Get([]byte(`null`))
should.Equal(0, any.ToInt())
should.Equal(float64(0), any.ToFloat64())
should.Equal("", any.ToString())
should.False(any.ToBool())
}

View File

@ -0,0 +1,121 @@
package any_tests
import (
"testing"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
func Test_read_object_as_any(t *testing.T) {
should := require.New(t)
any := jsoniter.Get([]byte(`{"a":"stream","c":"d"}`))
should.Equal(`{"a":"stream","c":"d"}`, any.ToString())
// partial parse
should.Equal("stream", any.Get("a").ToString())
should.Equal("d", any.Get("c").ToString())
should.Equal(2, len(any.Keys()))
any = jsoniter.Get([]byte(`{"a":"stream","c":"d"}`))
// full parse
should.Equal(2, len(any.Keys()))
should.Equal(2, any.Size())
should.True(any.ToBool())
should.Equal(0, any.ToInt())
should.Equal(jsoniter.ObjectValue, any.ValueType())
should.Nil(any.LastError())
obj := struct {
A string
}{}
any.ToVal(&obj)
should.Equal("stream", obj.A)
}
func Test_object_lazy_any_get(t *testing.T) {
should := require.New(t)
any := jsoniter.Get([]byte(`{"a":{"stream":{"c":"d"}}}`))
should.Equal("d", any.Get("a", "stream", "c").ToString())
}
func Test_object_lazy_any_get_all(t *testing.T) {
should := require.New(t)
any := jsoniter.Get([]byte(`{"a":[0],"stream":[1]}`))
should.Contains(any.Get('*', 0).ToString(), `"a":0`)
}
func Test_object_lazy_any_get_invalid(t *testing.T) {
should := require.New(t)
any := jsoniter.Get([]byte(`{}`))
should.Equal(jsoniter.InvalidValue, any.Get("a", "stream", "c").ValueType())
should.Equal(jsoniter.InvalidValue, any.Get(1).ValueType())
}
func Test_wrap_map_and_convert_to_any(t *testing.T) {
should := require.New(t)
any := jsoniter.Wrap(map[string]interface{}{"a": 1})
should.True(any.ToBool())
should.Equal(0, any.ToInt())
should.Equal(int32(0), any.ToInt32())
should.Equal(int64(0), any.ToInt64())
should.Equal(float32(0), any.ToFloat32())
should.Equal(float64(0), any.ToFloat64())
should.Equal(uint(0), any.ToUint())
should.Equal(uint32(0), any.ToUint32())
should.Equal(uint64(0), any.ToUint64())
}
func Test_wrap_object_and_convert_to_any(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
field2 string
}
any := jsoniter.Wrap(TestObject{"hello", "world"})
should.Equal("hello", any.Get("Field1").ToString())
any = jsoniter.Wrap(TestObject{"hello", "world"})
should.Equal(2, any.Size())
should.Equal(`{"Field1":"hello"}`, any.Get('*').ToString())
should.Equal(0, any.ToInt())
should.Equal(int32(0), any.ToInt32())
should.Equal(int64(0), any.ToInt64())
should.Equal(float32(0), any.ToFloat32())
should.Equal(float64(0), any.ToFloat64())
should.Equal(uint(0), any.ToUint())
should.Equal(uint32(0), any.ToUint32())
should.Equal(uint64(0), any.ToUint64())
should.True(any.ToBool())
should.Equal(`{"Field1":"hello"}`, any.ToString())
// cannot pass!
//stream := NewStream(ConfigDefault, nil, 32)
//any.WriteTo(stream)
//should.Equal(`{"Field1":"hello"}`, string(stream.Buffer()))
// cannot pass!
}
func Test_any_within_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 jsoniter.Any
Field2 jsoniter.Any
}
obj := TestObject{}
err := jsoniter.UnmarshalFromString(`{"Field1": "hello", "Field2": [1,2,3]}`, &obj)
should.Nil(err)
should.Equal("hello", obj.Field1.ToString())
should.Equal("[1,2,3]", obj.Field2.ToString())
}
func Test_object_wrapper_any_get_all(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 []int
Field2 []int
}
any := jsoniter.Wrap(TestObject{[]int{1, 2}, []int{3, 4}})
should.Contains(any.Get('*', 0).ToString(), `"Field2":3`)
should.Contains(any.Keys(), "Field1")
should.Contains(any.Keys(), "Field2")
should.NotContains(any.Keys(), "Field3")
}

View File

@ -0,0 +1,58 @@
package any_tests
import (
"testing"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
var stringConvertMap = map[string]string{
"null": "",
"321.1": "321.1",
`"1.1"`: "1.1",
`"-123.1"`: "-123.1",
"0.0": "0.0",
"0": "0",
`"0"`: "0",
`"0.0"`: "0.0",
`"00.0"`: "00.0",
"true": "true",
"false": "false",
`"true"`: "true",
`"false"`: "false",
`"true123"`: "true123",
`"+1"`: "+1",
"[]": "[]",
"[1,2]": "[1,2]",
"{}": "{}",
`{"a":1, "stream":true}`: `{"a":1, "stream":true}`,
}
func Test_read_any_to_string(t *testing.T) {
should := require.New(t)
for k, v := range stringConvertMap {
any := jsoniter.Get([]byte(k))
should.Equal(v, any.ToString(), "original val "+k)
}
}
func Test_read_string_as_any(t *testing.T) {
should := require.New(t)
any := jsoniter.Get([]byte(`"hello"`))
should.Equal("hello", any.ToString())
should.True(any.ToBool())
any = jsoniter.Get([]byte(`" "`))
should.False(any.ToBool())
any = jsoniter.Get([]byte(`"false"`))
should.True(any.ToBool())
any = jsoniter.Get([]byte(`"123"`))
should.Equal(123, any.ToInt())
}
func Test_wrap_string(t *testing.T) {
should := require.New(t)
any := jsoniter.Get([]byte("-32000")).MustBeValid()
should.Equal(-32000, any.ToInt())
should.NoError(any.LastError())
}

View File

@ -0,0 +1,72 @@
package any_tests
import (
"testing"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
// if must be valid is useless, just drop this test
func Test_must_be_valid(t *testing.T) {
should := require.New(t)
any := jsoniter.Get([]byte("123"))
should.Equal(any.MustBeValid().ToInt(), 123)
any = jsoniter.Wrap(int8(10))
should.Equal(any.MustBeValid().ToInt(), 10)
any = jsoniter.Wrap(int16(10))
should.Equal(any.MustBeValid().ToInt(), 10)
any = jsoniter.Wrap(int32(10))
should.Equal(any.MustBeValid().ToInt(), 10)
any = jsoniter.Wrap(int64(10))
should.Equal(any.MustBeValid().ToInt(), 10)
any = jsoniter.Wrap(uint(10))
should.Equal(any.MustBeValid().ToInt(), 10)
any = jsoniter.Wrap(uint8(10))
should.Equal(any.MustBeValid().ToInt(), 10)
any = jsoniter.Wrap(uint16(10))
should.Equal(any.MustBeValid().ToInt(), 10)
any = jsoniter.Wrap(uint32(10))
should.Equal(any.MustBeValid().ToInt(), 10)
any = jsoniter.Wrap(uint64(10))
should.Equal(any.MustBeValid().ToInt(), 10)
any = jsoniter.Wrap(float32(10))
should.Equal(any.MustBeValid().ToFloat64(), float64(10))
any = jsoniter.Wrap(float64(10))
should.Equal(any.MustBeValid().ToFloat64(), float64(10))
any = jsoniter.Wrap(true)
should.Equal(any.MustBeValid().ToFloat64(), float64(1))
any = jsoniter.Wrap(false)
should.Equal(any.MustBeValid().ToFloat64(), float64(0))
any = jsoniter.Wrap(nil)
should.Equal(any.MustBeValid().ToFloat64(), float64(0))
any = jsoniter.Wrap(struct{ age int }{age: 1})
should.Equal(any.MustBeValid().ToFloat64(), float64(0))
any = jsoniter.Wrap(map[string]interface{}{"abc": 1})
should.Equal(any.MustBeValid().ToFloat64(), float64(0))
any = jsoniter.Wrap("abc")
should.Equal(any.MustBeValid().ToFloat64(), float64(0))
any = jsoniter.Wrap([]int{})
should.Equal(any.MustBeValid().ToFloat64(), float64(0))
any = jsoniter.Wrap([]int{1, 2})
should.Equal(any.MustBeValid().ToFloat64(), float64(1))
}

View File

@ -0,0 +1,119 @@
package any_tests
import (
"testing"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
func Test_wrap_and_valuetype_everything(t *testing.T) {
should := require.New(t)
var i interface{}
any := jsoniter.Get([]byte("123"))
// default of number type is float64
i = float64(123)
should.Equal(i, any.GetInterface())
any = jsoniter.Wrap(int8(10))
should.Equal(any.ValueType(), jsoniter.NumberValue)
should.Equal(any.LastError(), nil)
// get interface is not int8 interface
// i = int8(10)
// should.Equal(i, any.GetInterface())
any = jsoniter.Wrap(int16(10))
should.Equal(any.ValueType(), jsoniter.NumberValue)
should.Equal(any.LastError(), nil)
//i = int16(10)
//should.Equal(i, any.GetInterface())
any = jsoniter.Wrap(int32(10))
should.Equal(any.ValueType(), jsoniter.NumberValue)
should.Equal(any.LastError(), nil)
i = int32(10)
should.Equal(i, any.GetInterface())
any = jsoniter.Wrap(int64(10))
should.Equal(any.ValueType(), jsoniter.NumberValue)
should.Equal(any.LastError(), nil)
i = int64(10)
should.Equal(i, any.GetInterface())
any = jsoniter.Wrap(uint(10))
should.Equal(any.ValueType(), jsoniter.NumberValue)
should.Equal(any.LastError(), nil)
// not equal
//i = uint(10)
//should.Equal(i, any.GetInterface())
any = jsoniter.Wrap(uint8(10))
should.Equal(any.ValueType(), jsoniter.NumberValue)
should.Equal(any.LastError(), nil)
// not equal
// i = uint8(10)
// should.Equal(i, any.GetInterface())
any = jsoniter.Wrap(uint16(10))
should.Equal(any.ValueType(), jsoniter.NumberValue)
should.Equal(any.LastError(), nil)
any = jsoniter.Wrap(uint32(10))
should.Equal(any.ValueType(), jsoniter.NumberValue)
should.Equal(any.LastError(), nil)
i = uint32(10)
should.Equal(i, any.GetInterface())
any = jsoniter.Wrap(uint64(10))
should.Equal(any.ValueType(), jsoniter.NumberValue)
should.Equal(any.LastError(), nil)
i = uint64(10)
should.Equal(i, any.GetInterface())
any = jsoniter.Wrap(float32(10))
should.Equal(any.ValueType(), jsoniter.NumberValue)
should.Equal(any.LastError(), nil)
// not equal
//i = float32(10)
//should.Equal(i, any.GetInterface())
any = jsoniter.Wrap(float64(10))
should.Equal(any.ValueType(), jsoniter.NumberValue)
should.Equal(any.LastError(), nil)
i = float64(10)
should.Equal(i, any.GetInterface())
any = jsoniter.Wrap(true)
should.Equal(any.ValueType(), jsoniter.BoolValue)
should.Equal(any.LastError(), nil)
i = true
should.Equal(i, any.GetInterface())
any = jsoniter.Wrap(false)
should.Equal(any.ValueType(), jsoniter.BoolValue)
should.Equal(any.LastError(), nil)
i = false
should.Equal(i, any.GetInterface())
any = jsoniter.Wrap(nil)
should.Equal(any.ValueType(), jsoniter.NilValue)
should.Equal(any.LastError(), nil)
i = nil
should.Equal(i, any.GetInterface())
stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 32)
any.WriteTo(stream)
should.Equal("null", string(stream.Buffer()))
should.Equal(any.LastError(), nil)
any = jsoniter.Wrap(struct{ age int }{age: 1})
should.Equal(any.ValueType(), jsoniter.ObjectValue)
should.Equal(any.LastError(), nil)
i = struct{ age int }{age: 1}
should.Equal(i, any.GetInterface())
any = jsoniter.Wrap(map[string]interface{}{"abc": 1})
should.Equal(any.ValueType(), jsoniter.ObjectValue)
should.Equal(any.LastError(), nil)
i = map[string]interface{}{"abc": 1}
should.Equal(i, any.GetInterface())
any = jsoniter.Wrap("abc")
i = "abc"
should.Equal(i, any.GetInterface())
should.Equal(nil, any.LastError())
}

74
any_uint32.go Normal file
View File

@ -0,0 +1,74 @@
package jsoniter
import (
"strconv"
)
type uint32Any struct {
baseAny
val uint32
}
func (any *uint32Any) LastError() error {
return nil
}
func (any *uint32Any) ValueType() ValueType {
return NumberValue
}
func (any *uint32Any) MustBeValid() Any {
return any
}
func (any *uint32Any) ToBool() bool {
return any.val != 0
}
func (any *uint32Any) ToInt() int {
return int(any.val)
}
func (any *uint32Any) ToInt32() int32 {
return int32(any.val)
}
func (any *uint32Any) ToInt64() int64 {
return int64(any.val)
}
func (any *uint32Any) ToUint() uint {
return uint(any.val)
}
func (any *uint32Any) ToUint32() uint32 {
return any.val
}
func (any *uint32Any) ToUint64() uint64 {
return uint64(any.val)
}
func (any *uint32Any) ToFloat32() float32 {
return float32(any.val)
}
func (any *uint32Any) ToFloat64() float64 {
return float64(any.val)
}
func (any *uint32Any) ToString() string {
return strconv.FormatInt(int64(any.val), 10)
}
func (any *uint32Any) WriteTo(stream *Stream) {
stream.WriteUint32(any.val)
}
func (any *uint32Any) Parse() *Iterator {
return nil
}
func (any *uint32Any) GetInterface() interface{} {
return any.val
}

74
any_uint64.go Normal file
View File

@ -0,0 +1,74 @@
package jsoniter
import (
"strconv"
)
type uint64Any struct {
baseAny
val uint64
}
func (any *uint64Any) LastError() error {
return nil
}
func (any *uint64Any) ValueType() ValueType {
return NumberValue
}
func (any *uint64Any) MustBeValid() Any {
return any
}
func (any *uint64Any) ToBool() bool {
return any.val != 0
}
func (any *uint64Any) ToInt() int {
return int(any.val)
}
func (any *uint64Any) ToInt32() int32 {
return int32(any.val)
}
func (any *uint64Any) ToInt64() int64 {
return int64(any.val)
}
func (any *uint64Any) ToUint() uint {
return uint(any.val)
}
func (any *uint64Any) ToUint32() uint32 {
return uint32(any.val)
}
func (any *uint64Any) ToUint64() uint64 {
return any.val
}
func (any *uint64Any) ToFloat32() float32 {
return float32(any.val)
}
func (any *uint64Any) ToFloat64() float64 {
return float64(any.val)
}
func (any *uint64Any) ToString() string {
return strconv.FormatUint(any.val, 10)
}
func (any *uint64Any) WriteTo(stream *Stream) {
stream.WriteUint64(any.val)
}
func (any *uint64Any) Parse() *Iterator {
return nil
}
func (any *uint64Any) GetInterface() interface{} {
return any.val
}

229
api_tests/config_test.go Normal file
View File

@ -0,0 +1,229 @@
package test
import (
"encoding/json"
"testing"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
func Test_use_number_for_unmarshal(t *testing.T) {
should := require.New(t)
api := jsoniter.Config{UseNumber: true}.Froze()
var obj interface{}
should.Nil(api.UnmarshalFromString("123", &obj))
should.Equal(json.Number("123"), obj)
}
func Test_customize_float_marshal(t *testing.T) {
should := require.New(t)
json := jsoniter.Config{MarshalFloatWith6Digits: true}.Froze()
str, err := json.MarshalToString(float32(1.23456789))
should.Nil(err)
should.Equal("1.234568", str)
}
func Test_customize_tag_key(t *testing.T) {
type TestObject struct {
Field string `orm:"field"`
}
should := require.New(t)
json := jsoniter.Config{TagKey: "orm"}.Froze()
str, err := json.MarshalToString(TestObject{"hello"})
should.Nil(err)
should.Equal(`{"field":"hello"}`, str)
}
func Test_read_large_number_as_interface(t *testing.T) {
should := require.New(t)
var val interface{}
err := jsoniter.Config{UseNumber: true}.Froze().UnmarshalFromString(`123456789123456789123456789`, &val)
should.Nil(err)
output, err := jsoniter.MarshalToString(val)
should.Nil(err)
should.Equal(`123456789123456789123456789`, output)
}
type caseSensitiveStruct struct {
A string `json:"a"`
B string `json:"b,omitempty"`
C *C `json:"C,omitempty"`
}
type C struct {
D int64 `json:"D,omitempty"`
E *E `json:"e,omitempty"`
}
type E struct {
F string `json:"F,omitempty"`
}
func Test_CaseSensitive(t *testing.T) {
should := require.New(t)
testCases := []struct {
input string
expectedOutput string
caseSensitive bool
}{
{
input: `{"A":"foo","B":"bar"}`,
expectedOutput: `{"a":"foo","b":"bar"}`,
caseSensitive: false,
},
{
input: `{"a":"foo","b":"bar"}`,
expectedOutput: `{"a":"foo","b":"bar"}`,
caseSensitive: true,
},
{
input: `{"a":"foo","b":"bar","C":{"D":10}}`,
expectedOutput: `{"a":"foo","b":"bar","C":{"D":10}}`,
caseSensitive: true,
},
{
input: `{"a":"foo","B":"bar","c":{"d":10}}`,
expectedOutput: `{"a":"foo"}`,
caseSensitive: true,
},
{
input: `{"a":"foo","C":{"d":10}}`,
expectedOutput: `{"a":"foo","C":{}}`,
caseSensitive: true,
},
{
input: `{"a":"foo","C":{"D":10,"e":{"f":"baz"}}}`,
expectedOutput: `{"a":"foo","C":{"D":10,"e":{}}}`,
caseSensitive: true,
},
{
input: `{"a":"foo","C":{"D":10,"e":{"F":"baz"}}}`,
expectedOutput: `{"a":"foo","C":{"D":10,"e":{"F":"baz"}}}`,
caseSensitive: true,
},
{
input: `{"A":"foo","c":{"d":10,"E":{"f":"baz"}}}`,
expectedOutput: `{"a":"foo","C":{"D":10,"e":{"F":"baz"}}}`,
caseSensitive: false,
},
}
for _, tc := range testCases {
val := caseSensitiveStruct{}
err := jsoniter.Config{CaseSensitive: tc.caseSensitive}.Froze().UnmarshalFromString(tc.input, &val)
should.Nil(err)
output, err := jsoniter.MarshalToString(val)
should.Nil(err)
should.Equal(tc.expectedOutput, output)
}
}
type structWithElevenFields struct {
A string `json:"A,omitempty"`
B string `json:"B,omitempty"`
C string `json:"C,omitempty"`
D string `json:"d,omitempty"`
E string `json:"e,omitempty"`
F string `json:"f,omitempty"`
G string `json:"g,omitempty"`
H string `json:"h,omitempty"`
I string `json:"i,omitempty"`
J string `json:"j,omitempty"`
K string `json:"k,omitempty"`
}
func Test_CaseSensitive_MoreThanTenFields(t *testing.T) {
should := require.New(t)
testCases := []struct {
input string
expectedOutput string
caseSensitive bool
}{
{
input: `{"A":"1","B":"2","C":"3","d":"4","e":"5","f":"6","g":"7","h":"8","i":"9","j":"10","k":"11"}`,
expectedOutput: `{"A":"1","B":"2","C":"3","d":"4","e":"5","f":"6","g":"7","h":"8","i":"9","j":"10","k":"11"}`,
caseSensitive: true,
},
{
input: `{"a":"1","b":"2","c":"3","D":"4","E":"5","F":"6"}`,
expectedOutput: `{"A":"1","B":"2","C":"3","d":"4","e":"5","f":"6"}`,
caseSensitive: false,
},
{
input: `{"A":"1","b":"2","d":"4","E":"5"}`,
expectedOutput: `{"A":"1","d":"4"}`,
caseSensitive: true,
},
}
for _, tc := range testCases {
val := structWithElevenFields{}
err := jsoniter.Config{CaseSensitive: tc.caseSensitive}.Froze().UnmarshalFromString(tc.input, &val)
should.Nil(err)
output, err := jsoniter.MarshalToString(val)
should.Nil(err)
should.Equal(tc.expectedOutput, output)
}
}
type onlyTaggedFieldStruct struct {
A string `json:"a"`
B string
FSimpl F `json:"f_simpl"`
ISimpl I
FPtr *F `json:"f_ptr"`
IPtr *I
F
*I
}
type F struct {
G string `json:"g"`
H string
}
type I struct {
J string `json:"j"`
K string
}
func Test_OnlyTaggedField(t *testing.T) {
should := require.New(t)
obj := onlyTaggedFieldStruct{
A: "a",
B: "b",
FSimpl: F{G: "g", H: "h"},
ISimpl: I{J: "j", K: "k"},
FPtr: &F{G: "g", H: "h"},
IPtr: &I{J: "j", K: "k"},
F: F{G: "g", H: "h"},
I: &I{J: "j", K: "k"},
}
output, err := jsoniter.Config{OnlyTaggedField: true}.Froze().Marshal(obj)
should.Nil(err)
m := make(map[string]interface{})
err = jsoniter.Unmarshal(output, &m)
should.Nil(err)
should.Equal(map[string]interface{}{
"a": "a",
"f_simpl": map[string]interface{}{
"g": "g",
},
"f_ptr": map[string]interface{}{
"g": "g",
},
"g": "g",
"j": "j",
}, m)
}

64
api_tests/decoder_test.go Normal file
View File

@ -0,0 +1,64 @@
package test
import (
"bytes"
"encoding/json"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"io/ioutil"
"testing"
)
func Test_disallowUnknownFields(t *testing.T) {
should := require.New(t)
type TestObject struct{}
var obj TestObject
decoder := jsoniter.NewDecoder(bytes.NewBufferString(`{"field1":100}`))
decoder.DisallowUnknownFields()
should.Error(decoder.Decode(&obj))
}
func Test_new_decoder(t *testing.T) {
should := require.New(t)
decoder1 := json.NewDecoder(bytes.NewBufferString(`[1][2]`))
decoder2 := jsoniter.NewDecoder(bytes.NewBufferString(`[1][2]`))
arr1 := []int{}
should.Nil(decoder1.Decode(&arr1))
should.Equal([]int{1}, arr1)
arr2 := []int{}
should.True(decoder1.More())
buffered, _ := ioutil.ReadAll(decoder1.Buffered())
should.Equal("[2]", string(buffered))
should.Nil(decoder2.Decode(&arr2))
should.Equal([]int{1}, arr2)
should.True(decoder2.More())
buffered, _ = ioutil.ReadAll(decoder2.Buffered())
should.Equal("[2]", string(buffered))
should.Nil(decoder1.Decode(&arr1))
should.Equal([]int{2}, arr1)
should.False(decoder1.More())
should.Nil(decoder2.Decode(&arr2))
should.Equal([]int{2}, arr2)
should.False(decoder2.More())
}
func Test_use_number(t *testing.T) {
should := require.New(t)
decoder1 := json.NewDecoder(bytes.NewBufferString(`123`))
decoder1.UseNumber()
decoder2 := jsoniter.NewDecoder(bytes.NewBufferString(`123`))
decoder2.UseNumber()
var obj1 interface{}
should.Nil(decoder1.Decode(&obj1))
should.Equal(json.Number("123"), obj1)
var obj2 interface{}
should.Nil(decoder2.Decode(&obj2))
should.Equal(json.Number("123"), obj2)
}
func Test_decoder_more(t *testing.T) {
should := require.New(t)
decoder := jsoniter.NewDecoder(bytes.NewBufferString("abcde"))
should.True(decoder.More())
}

View File

@ -0,0 +1,46 @@
//+build go1.8
package test
import (
"bytes"
"encoding/json"
"testing"
"unicode/utf8"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
func Test_new_encoder(t *testing.T) {
should := require.New(t)
buf1 := &bytes.Buffer{}
encoder1 := json.NewEncoder(buf1)
encoder1.SetEscapeHTML(false)
encoder1.Encode([]int{1})
should.Equal("[1]\n", buf1.String())
buf2 := &bytes.Buffer{}
encoder2 := jsoniter.NewEncoder(buf2)
encoder2.SetEscapeHTML(false)
encoder2.Encode([]int{1})
should.Equal("[1]\n", buf2.String())
}
func Test_string_encode_with_std_without_html_escape(t *testing.T) {
api := jsoniter.Config{EscapeHTML: false}.Froze()
should := require.New(t)
for i := 0; i < utf8.RuneSelf; i++ {
input := string([]byte{byte(i)})
buf := &bytes.Buffer{}
encoder := json.NewEncoder(buf)
encoder.SetEscapeHTML(false)
err := encoder.Encode(input)
should.Nil(err)
stdOutput := buf.String()
stdOutput = stdOutput[:len(stdOutput)-1]
jsoniterOutputBytes, err := api.Marshal(input)
should.Nil(err)
jsoniterOutput := string(jsoniterOutputBytes)
should.Equal(stdOutput, jsoniterOutput)
}
}

20
api_tests/encoder_test.go Normal file
View File

@ -0,0 +1,20 @@
package test
import (
"bytes"
"encoding/json"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"testing"
)
// Standard Encoder has trailing newline.
func TestEncoderHasTrailingNewline(t *testing.T) {
should := require.New(t)
var buf, stdbuf bytes.Buffer
enc := jsoniter.ConfigCompatibleWithStandardLibrary.NewEncoder(&buf)
enc.Encode(1)
stdenc := json.NewEncoder(&stdbuf)
stdenc.Encode(1)
should.Equal(stdbuf.Bytes(), buf.Bytes())
}

View File

@ -0,0 +1,36 @@
package test
import (
"encoding/json"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"testing"
)
func Test_marshal_indent(t *testing.T) {
should := require.New(t)
obj := struct {
F1 int
F2 []int
}{1, []int{2, 3, 4}}
output, err := json.MarshalIndent(obj, "", " ")
should.Nil(err)
should.Equal("{\n \"F1\": 1,\n \"F2\": [\n 2,\n 3,\n 4\n ]\n}", string(output))
output, err = jsoniter.MarshalIndent(obj, "", " ")
should.Nil(err)
should.Equal("{\n \"F1\": 1,\n \"F2\": [\n 2,\n 3,\n 4\n ]\n}", string(output))
}
func Test_marshal_indent_map(t *testing.T) {
should := require.New(t)
obj := map[int]int{1: 2}
output, err := json.MarshalIndent(obj, "", " ")
should.Nil(err)
should.Equal("{\n \"1\": 2\n}", string(output))
output, err = jsoniter.MarshalIndent(obj, "", " ")
should.Nil(err)
should.Equal("{\n \"1\": 2\n}", string(output))
output, err = jsoniter.ConfigCompatibleWithStandardLibrary.MarshalIndent(obj, "", " ")
should.Nil(err)
should.Equal("{\n \"1\": 2\n}", string(output))
}

View File

@ -0,0 +1,47 @@
package test
import (
"bytes"
"encoding/json"
"testing"
jsoniter "github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
var marshalConfig = jsoniter.Config{
EscapeHTML: false,
SortMapKeys: true,
ValidateJsonRawMessage: true,
}.Froze()
type Container struct {
Bar interface{}
}
func (c *Container) MarshalJSON() ([]byte, error) {
return marshalConfig.Marshal(&c.Bar)
}
func TestEncodeEscape(t *testing.T) {
should := require.New(t)
container := &Container{
Bar: []string{"123<ab>", "ooo"},
}
out, err := marshalConfig.Marshal(container)
should.Nil(err)
bufout := string(out)
var stdbuf bytes.Buffer
stdenc := json.NewEncoder(&stdbuf)
stdenc.SetEscapeHTML(false)
err = stdenc.Encode(container)
should.Nil(err)
stdout := string(stdbuf.Bytes())
if stdout[len(stdout)-1:] == "\n" {
stdout = stdout[:len(stdout)-1]
}
should.Equal(stdout, bufout)
}

View File

@ -0,0 +1,36 @@
package test
import (
"bytes"
"encoding/json"
"github.com/json-iterator/go"
"testing"
"github.com/stretchr/testify/require"
)
type Foo struct {
Bar interface{}
}
func (f Foo) MarshalJSON() ([]byte, error) {
var buf bytes.Buffer
err := json.NewEncoder(&buf).Encode(f.Bar)
return buf.Bytes(), err
}
// Standard Encoder has trailing newline.
func TestEncodeMarshalJSON(t *testing.T) {
foo := Foo {
Bar: 123,
}
should := require.New(t)
var buf, stdbuf bytes.Buffer
enc := jsoniter.ConfigCompatibleWithStandardLibrary.NewEncoder(&buf)
enc.Encode(foo)
stdenc := json.NewEncoder(&stdbuf)
stdenc.Encode(foo)
should.Equal(stdbuf.Bytes(), buf.Bytes())
}

View File

@ -0,0 +1,25 @@
package test
import (
"bytes"
"github.com/json-iterator/go"
"testing"
)
func Benchmark_encode_string_with_SetEscapeHTML(b *testing.B) {
type V struct {
S string
B bool
I int
}
var json = jsoniter.ConfigCompatibleWithStandardLibrary
b.ReportAllocs()
for i := 0; i < b.N; i++ {
buf := &bytes.Buffer{}
enc := json.NewEncoder(buf)
enc.SetEscapeHTML(true)
if err := enc.Encode(V{S: "s", B: true, I: 233}); err != nil {
b.Fatal(err)
}
}
}

View File

@ -0,0 +1,158 @@
package test
import (
"encoding/json"
"github.com/json-iterator/go"
"io/ioutil"
"os"
"testing"
)
//func Test_large_file(t *testing.T) {
// file, err := os.Open("/tmp/large-file.json")
// if err != nil {
// t.Fatal(err)
// }
// iter := Parse(file, 4096)
// count := 0
// for iter.ReadArray() {
// iter.Skip()
// count++
// }
// if count != 11351 {
// t.Fatal(count)
// }
//}
func init() {
ioutil.WriteFile("/tmp/large-file.json", []byte(`[{
"person": {
"id": "d50887ca-a6ce-4e59-b89f-14f0b5d03b03",
"name": {
"fullName": "Leonid Bugaev",
"givenName": "Leonid",
"familyName": "Bugaev"
},
"email": "leonsbox@gmail.com",
"gender": "male",
"location": "Saint Petersburg, Saint Petersburg, RU",
"geo": {
"city": "Saint Petersburg",
"state": "Saint Petersburg",
"country": "Russia",
"lat": 59.9342802,
"lng": 30.3350986
},
"bio": "Senior engineer at Granify.com",
"site": "http://flickfaver.com",
"avatar": "https://d1ts43dypk8bqh.cloudfront.net/v1/avatars/d50887ca-a6ce-4e59-b89f-14f0b5d03b03",
"employment": {
"name": "www.latera.ru",
"title": "Software Engineer",
"domain": "gmail.com"
},
"facebook": {
"handle": "leonid.bugaev"
},
"github": {
"handle": "buger",
"id": 14009,
"avatar": "https://avatars.githubusercontent.com/u/14009?v=3",
"company": "Granify",
"blog": "http://leonsbox.com",
"followers": 95,
"following": 10
},
"twitter": {
"handle": "flickfaver",
"id": 77004410,
"bio": null,
"followers": 2,
"following": 1,
"statuses": 5,
"favorites": 0,
"location": "",
"site": "http://flickfaver.com",
"avatar": null
},
"linkedin": {
"handle": "in/leonidbugaev"
},
"googleplus": {
"handle": null
},
"angellist": {
"handle": "leonid-bugaev",
"id": 61541,
"bio": "Senior engineer at Granify.com",
"blog": "http://buger.github.com",
"site": "http://buger.github.com",
"followers": 41,
"avatar": "https://d1qb2nb5cznatu.cloudfront.net/users/61541-medium_jpg?1405474390"
},
"klout": {
"handle": null,
"score": null
},
"foursquare": {
"handle": null
},
"aboutme": {
"handle": "leonid.bugaev",
"bio": null,
"avatar": null
},
"gravatar": {
"handle": "buger",
"urls": [
],
"avatar": "http://1.gravatar.com/avatar/f7c8edd577d13b8930d5522f28123510",
"avatars": [
{
"url": "http://1.gravatar.com/avatar/f7c8edd577d13b8930d5522f28123510",
"type": "thumbnail"
}
]
},
"fuzzy": false
},
"company": "hello"
}]`), 0666)
}
/*
200000 8886 ns/op 4336 B/op 6 allocs/op
50000 34244 ns/op 6744 B/op 14 allocs/op
*/
func Benchmark_jsoniter_large_file(b *testing.B) {
b.ReportAllocs()
for n := 0; n < b.N; n++ {
file, _ := os.Open("/tmp/large-file.json")
iter := jsoniter.Parse(jsoniter.ConfigDefault, file, 4096)
count := 0
iter.ReadArrayCB(func(iter *jsoniter.Iterator) bool {
// Skip() is strict by default, use --tags jsoniter-sloppy to skip without validation
iter.Skip()
count++
return true
})
file.Close()
if iter.Error != nil {
b.Error(iter.Error)
}
}
}
func Benchmark_json_large_file(b *testing.B) {
b.ReportAllocs()
for n := 0; n < b.N; n++ {
file, _ := os.Open("/tmp/large-file.json")
bytes, _ := ioutil.ReadAll(file)
file.Close()
result := []struct{}{}
err := json.Unmarshal(bytes, &result)
if err != nil {
b.Error(err)
}
}
}

128
benchmarks/stream_test.go Normal file
View File

@ -0,0 +1,128 @@
package test
import (
"bytes"
"strconv"
"testing"
jsoniter "github.com/json-iterator/go"
)
func Benchmark_stream_encode_big_object(b *testing.B) {
var buf bytes.Buffer
var stream = jsoniter.NewStream(jsoniter.ConfigDefault, &buf, 100)
for i := 0; i < b.N; i++ {
buf.Reset()
stream.Reset(&buf)
encodeObject(stream)
if stream.Error != nil {
b.Errorf("error: %+v", stream.Error)
}
}
}
func TestEncodeObject(t *testing.T) {
var stream = jsoniter.NewStream(jsoniter.ConfigDefault, nil, 100)
encodeObject(stream)
if stream.Error != nil {
t.Errorf("error encoding a test object: %+v", stream.Error)
return
}
var m = make(map[string]interface{})
if err := jsoniter.Unmarshal(stream.Buffer(), &m); err != nil {
t.Errorf("error unmarshaling a test object: %+v", err)
return
}
}
func encodeObject(stream *jsoniter.Stream) {
stream.WriteObjectStart()
stream.WriteObjectField("objectId")
stream.WriteUint64(8838243212)
stream.WriteMore()
stream.WriteObjectField("name")
stream.WriteString("Jane Doe")
stream.WriteMore()
stream.WriteObjectField("address")
stream.WriteObjectStart()
for i, field := range addressFields {
if i != 0 {
stream.WriteMore()
}
stream.WriteObjectField(field.key)
stream.WriteString(field.val)
}
stream.WriteMore()
stream.WriteObjectField("geo")
{
stream.WriteObjectStart()
stream.WriteObjectField("latitude")
stream.WriteFloat64(-154.550817)
stream.WriteMore()
stream.WriteObjectField("longitude")
stream.WriteFloat64(-84.176159)
stream.WriteObjectEnd()
}
stream.WriteObjectEnd()
stream.WriteMore()
stream.WriteObjectField("specialties")
stream.WriteArrayStart()
for i, s := range specialties {
if i != 0 {
stream.WriteMore()
}
stream.WriteString(s)
}
stream.WriteArrayEnd()
stream.WriteMore()
for i, text := range longText {
if i != 0 {
stream.WriteMore()
}
stream.WriteObjectField("longText" + strconv.Itoa(i))
stream.WriteString(text)
}
for i := 0; i < 25; i++ {
num := i * 18328
stream.WriteMore()
stream.WriteObjectField("integerField" + strconv.Itoa(i))
stream.WriteInt64(int64(num))
}
stream.WriteObjectEnd()
}
type field struct{ key, val string }
var (
addressFields = []field{
{"address1", "123 Example St"},
{"address2", "Apartment 5D, Suite 3"},
{"city", "Miami"},
{"state", "FL"},
{"postalCode", "33133"},
{"country", "US"},
}
specialties = []string{
"Web Design",
"Go Programming",
"Tennis",
"Cycling",
"Mixed martial arts",
}
longText = []string{
`Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`,
`Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?`,
`But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure?`,
`At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat.`,
`On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection: he rejects pleasures to secure other greater pleasures, or else he endures pains to avoid worse pains.`,
}
)

12
build.sh Executable file
View File

@ -0,0 +1,12 @@
#!/bin/bash
set -e
set -x
if [ ! -d /tmp/build-golang/src/github.com/json-iterator ]; then
mkdir -p /tmp/build-golang/src/github.com/json-iterator
ln -s $PWD /tmp/build-golang/src/github.com/json-iterator/go
fi
export GOPATH=/tmp/build-golang
go get -u github.com/golang/dep/cmd/dep
cd /tmp/build-golang/src/github.com/json-iterator/go
exec $GOPATH/bin/dep ensure -update

375
config.go Normal file
View File

@ -0,0 +1,375 @@
package jsoniter
import (
"encoding/json"
"io"
"reflect"
"sync"
"unsafe"
"github.com/modern-go/concurrent"
"github.com/modern-go/reflect2"
)
// Config customize how the API should behave.
// The API is created from Config by Froze.
type Config struct {
IndentionStep int
MarshalFloatWith6Digits bool
EscapeHTML bool
SortMapKeys bool
UseNumber bool
DisallowUnknownFields bool
TagKey string
OnlyTaggedField bool
ValidateJsonRawMessage bool
ObjectFieldMustBeSimpleString bool
CaseSensitive bool
}
// API the public interface of this package.
// Primary Marshal and Unmarshal.
type API interface {
IteratorPool
StreamPool
MarshalToString(v interface{}) (string, error)
Marshal(v interface{}) ([]byte, error)
MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
UnmarshalFromString(str string, v interface{}) error
Unmarshal(data []byte, v interface{}) error
Get(data []byte, path ...interface{}) Any
NewEncoder(writer io.Writer) *Encoder
NewDecoder(reader io.Reader) *Decoder
Valid(data []byte) bool
RegisterExtension(extension Extension)
DecoderOf(typ reflect2.Type) ValDecoder
EncoderOf(typ reflect2.Type) ValEncoder
}
// ConfigDefault the default API
var ConfigDefault = Config{
EscapeHTML: true,
}.Froze()
// ConfigCompatibleWithStandardLibrary tries to be 100% compatible with standard library behavior
var ConfigCompatibleWithStandardLibrary = Config{
EscapeHTML: true,
SortMapKeys: true,
ValidateJsonRawMessage: true,
}.Froze()
// ConfigFastest marshals float with only 6 digits precision
var ConfigFastest = Config{
EscapeHTML: false,
MarshalFloatWith6Digits: true, // will lose precession
ObjectFieldMustBeSimpleString: true, // do not unescape object field
}.Froze()
type frozenConfig struct {
configBeforeFrozen Config
sortMapKeys bool
indentionStep int
objectFieldMustBeSimpleString bool
onlyTaggedField bool
disallowUnknownFields bool
decoderCache *concurrent.Map
encoderCache *concurrent.Map
encoderExtension Extension
decoderExtension Extension
extraExtensions []Extension
streamPool *sync.Pool
iteratorPool *sync.Pool
caseSensitive bool
}
func (cfg *frozenConfig) initCache() {
cfg.decoderCache = concurrent.NewMap()
cfg.encoderCache = concurrent.NewMap()
}
func (cfg *frozenConfig) addDecoderToCache(cacheKey uintptr, decoder ValDecoder) {
cfg.decoderCache.Store(cacheKey, decoder)
}
func (cfg *frozenConfig) addEncoderToCache(cacheKey uintptr, encoder ValEncoder) {
cfg.encoderCache.Store(cacheKey, encoder)
}
func (cfg *frozenConfig) getDecoderFromCache(cacheKey uintptr) ValDecoder {
decoder, found := cfg.decoderCache.Load(cacheKey)
if found {
return decoder.(ValDecoder)
}
return nil
}
func (cfg *frozenConfig) getEncoderFromCache(cacheKey uintptr) ValEncoder {
encoder, found := cfg.encoderCache.Load(cacheKey)
if found {
return encoder.(ValEncoder)
}
return nil
}
var cfgCache = concurrent.NewMap()
func getFrozenConfigFromCache(cfg Config) *frozenConfig {
obj, found := cfgCache.Load(cfg)
if found {
return obj.(*frozenConfig)
}
return nil
}
func addFrozenConfigToCache(cfg Config, frozenConfig *frozenConfig) {
cfgCache.Store(cfg, frozenConfig)
}
// Froze forge API from config
func (cfg Config) Froze() API {
api := &frozenConfig{
sortMapKeys: cfg.SortMapKeys,
indentionStep: cfg.IndentionStep,
objectFieldMustBeSimpleString: cfg.ObjectFieldMustBeSimpleString,
onlyTaggedField: cfg.OnlyTaggedField,
disallowUnknownFields: cfg.DisallowUnknownFields,
caseSensitive: cfg.CaseSensitive,
}
api.streamPool = &sync.Pool{
New: func() interface{} {
return NewStream(api, nil, 512)
},
}
api.iteratorPool = &sync.Pool{
New: func() interface{} {
return NewIterator(api)
},
}
api.initCache()
encoderExtension := EncoderExtension{}
decoderExtension := DecoderExtension{}
if cfg.MarshalFloatWith6Digits {
api.marshalFloatWith6Digits(encoderExtension)
}
if cfg.EscapeHTML {
api.escapeHTML(encoderExtension)
}
if cfg.UseNumber {
api.useNumber(decoderExtension)
}
if cfg.ValidateJsonRawMessage {
api.validateJsonRawMessage(encoderExtension)
}
api.encoderExtension = encoderExtension
api.decoderExtension = decoderExtension
api.configBeforeFrozen = cfg
return api
}
func (cfg Config) frozeWithCacheReuse(extraExtensions []Extension) *frozenConfig {
api := getFrozenConfigFromCache(cfg)
if api != nil {
return api
}
api = cfg.Froze().(*frozenConfig)
for _, extension := range extraExtensions {
api.RegisterExtension(extension)
}
addFrozenConfigToCache(cfg, api)
return api
}
func (cfg *frozenConfig) validateJsonRawMessage(extension EncoderExtension) {
encoder := &funcEncoder{func(ptr unsafe.Pointer, stream *Stream) {
rawMessage := *(*json.RawMessage)(ptr)
iter := cfg.BorrowIterator([]byte(rawMessage))
defer cfg.ReturnIterator(iter)
iter.Read()
if iter.Error != nil && iter.Error != io.EOF {
stream.WriteRaw("null")
} else {
stream.WriteRaw(string(rawMessage))
}
}, func(ptr unsafe.Pointer) bool {
return len(*((*json.RawMessage)(ptr))) == 0
}}
extension[reflect2.TypeOfPtr((*json.RawMessage)(nil)).Elem()] = encoder
extension[reflect2.TypeOfPtr((*RawMessage)(nil)).Elem()] = encoder
}
func (cfg *frozenConfig) useNumber(extension DecoderExtension) {
extension[reflect2.TypeOfPtr((*interface{})(nil)).Elem()] = &funcDecoder{func(ptr unsafe.Pointer, iter *Iterator) {
exitingValue := *((*interface{})(ptr))
if exitingValue != nil && reflect.TypeOf(exitingValue).Kind() == reflect.Ptr {
iter.ReadVal(exitingValue)
return
}
if iter.WhatIsNext() == NumberValue {
*((*interface{})(ptr)) = json.Number(iter.readNumberAsString())
} else {
*((*interface{})(ptr)) = iter.Read()
}
}}
}
func (cfg *frozenConfig) getTagKey() string {
tagKey := cfg.configBeforeFrozen.TagKey
if tagKey == "" {
return "json"
}
return tagKey
}
func (cfg *frozenConfig) RegisterExtension(extension Extension) {
cfg.extraExtensions = append(cfg.extraExtensions, extension)
copied := cfg.configBeforeFrozen
cfg.configBeforeFrozen = copied
}
type lossyFloat32Encoder struct {
}
func (encoder *lossyFloat32Encoder) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteFloat32Lossy(*((*float32)(ptr)))
}
func (encoder *lossyFloat32Encoder) IsEmpty(ptr unsafe.Pointer) bool {
return *((*float32)(ptr)) == 0
}
type lossyFloat64Encoder struct {
}
func (encoder *lossyFloat64Encoder) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteFloat64Lossy(*((*float64)(ptr)))
}
func (encoder *lossyFloat64Encoder) IsEmpty(ptr unsafe.Pointer) bool {
return *((*float64)(ptr)) == 0
}
// EnableLossyFloatMarshalling keeps 10**(-6) precision
// for float variables for better performance.
func (cfg *frozenConfig) marshalFloatWith6Digits(extension EncoderExtension) {
// for better performance
extension[reflect2.TypeOfPtr((*float32)(nil)).Elem()] = &lossyFloat32Encoder{}
extension[reflect2.TypeOfPtr((*float64)(nil)).Elem()] = &lossyFloat64Encoder{}
}
type htmlEscapedStringEncoder struct {
}
func (encoder *htmlEscapedStringEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
str := *((*string)(ptr))
stream.WriteStringWithHTMLEscaped(str)
}
func (encoder *htmlEscapedStringEncoder) IsEmpty(ptr unsafe.Pointer) bool {
return *((*string)(ptr)) == ""
}
func (cfg *frozenConfig) escapeHTML(encoderExtension EncoderExtension) {
encoderExtension[reflect2.TypeOfPtr((*string)(nil)).Elem()] = &htmlEscapedStringEncoder{}
}
func (cfg *frozenConfig) cleanDecoders() {
typeDecoders = map[string]ValDecoder{}
fieldDecoders = map[string]ValDecoder{}
*cfg = *(cfg.configBeforeFrozen.Froze().(*frozenConfig))
}
func (cfg *frozenConfig) cleanEncoders() {
typeEncoders = map[string]ValEncoder{}
fieldEncoders = map[string]ValEncoder{}
*cfg = *(cfg.configBeforeFrozen.Froze().(*frozenConfig))
}
func (cfg *frozenConfig) MarshalToString(v interface{}) (string, error) {
stream := cfg.BorrowStream(nil)
defer cfg.ReturnStream(stream)
stream.WriteVal(v)
if stream.Error != nil {
return "", stream.Error
}
return string(stream.Buffer()), nil
}
func (cfg *frozenConfig) Marshal(v interface{}) ([]byte, error) {
stream := cfg.BorrowStream(nil)
defer cfg.ReturnStream(stream)
stream.WriteVal(v)
if stream.Error != nil {
return nil, stream.Error
}
result := stream.Buffer()
copied := make([]byte, len(result))
copy(copied, result)
return copied, nil
}
func (cfg *frozenConfig) MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
if prefix != "" {
panic("prefix is not supported")
}
for _, r := range indent {
if r != ' ' {
panic("indent can only be space")
}
}
newCfg := cfg.configBeforeFrozen
newCfg.IndentionStep = len(indent)
return newCfg.frozeWithCacheReuse(cfg.extraExtensions).Marshal(v)
}
func (cfg *frozenConfig) UnmarshalFromString(str string, v interface{}) error {
data := []byte(str)
iter := cfg.BorrowIterator(data)
defer cfg.ReturnIterator(iter)
iter.ReadVal(v)
c := iter.nextToken()
if c == 0 {
if iter.Error == io.EOF {
return nil
}
return iter.Error
}
iter.ReportError("Unmarshal", "there are bytes left after unmarshal")
return iter.Error
}
func (cfg *frozenConfig) Get(data []byte, path ...interface{}) Any {
iter := cfg.BorrowIterator(data)
defer cfg.ReturnIterator(iter)
return locatePath(iter, path)
}
func (cfg *frozenConfig) Unmarshal(data []byte, v interface{}) error {
iter := cfg.BorrowIterator(data)
defer cfg.ReturnIterator(iter)
iter.ReadVal(v)
c := iter.nextToken()
if c == 0 {
if iter.Error == io.EOF {
return nil
}
return iter.Error
}
iter.ReportError("Unmarshal", "there are bytes left after unmarshal")
return iter.Error
}
func (cfg *frozenConfig) NewEncoder(writer io.Writer) *Encoder {
stream := NewStream(cfg, writer, 512)
return &Encoder{stream}
}
func (cfg *frozenConfig) NewDecoder(reader io.Reader) *Decoder {
iter := Parse(cfg, reader, 512)
return &Decoder{iter}
}
func (cfg *frozenConfig) Valid(data []byte) bool {
iter := cfg.BorrowIterator(data)
defer cfg.ReturnIterator(iter)
iter.Skip()
return iter.Error == nil
}

121
example_test.go Normal file
View File

@ -0,0 +1,121 @@
package jsoniter
import (
"fmt"
"os"
"strings"
)
func ExampleMarshal() {
type ColorGroup struct {
ID int
Name string
Colors []string
}
group := ColorGroup{
ID: 1,
Name: "Reds",
Colors: []string{"Crimson", "Red", "Ruby", "Maroon"},
}
b, err := Marshal(group)
if err != nil {
fmt.Println("error:", err)
}
os.Stdout.Write(b)
// Output:
// {"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]}
}
func ExampleUnmarshal() {
var jsonBlob = []byte(`[
{"Name": "Platypus", "Order": "Monotremata"},
{"Name": "Quoll", "Order": "Dasyuromorphia"}
]`)
type Animal struct {
Name string
Order string
}
var animals []Animal
err := Unmarshal(jsonBlob, &animals)
if err != nil {
fmt.Println("error:", err)
}
fmt.Printf("%+v", animals)
// Output:
// [{Name:Platypus Order:Monotremata} {Name:Quoll Order:Dasyuromorphia}]
}
func ExampleConfigFastest_Marshal() {
type ColorGroup struct {
ID int
Name string
Colors []string
}
group := ColorGroup{
ID: 1,
Name: "Reds",
Colors: []string{"Crimson", "Red", "Ruby", "Maroon"},
}
stream := ConfigFastest.BorrowStream(nil)
defer ConfigFastest.ReturnStream(stream)
stream.WriteVal(group)
if stream.Error != nil {
fmt.Println("error:", stream.Error)
}
os.Stdout.Write(stream.Buffer())
// Output:
// {"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]}
}
func ExampleConfigFastest_Unmarshal() {
var jsonBlob = []byte(`[
{"Name": "Platypus", "Order": "Monotremata"},
{"Name": "Quoll", "Order": "Dasyuromorphia"}
]`)
type Animal struct {
Name string
Order string
}
var animals []Animal
iter := ConfigFastest.BorrowIterator(jsonBlob)
defer ConfigFastest.ReturnIterator(iter)
iter.ReadVal(&animals)
if iter.Error != nil {
fmt.Println("error:", iter.Error)
}
fmt.Printf("%+v", animals)
// Output:
// [{Name:Platypus Order:Monotremata} {Name:Quoll Order:Dasyuromorphia}]
}
func ExampleGet() {
val := []byte(`{"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]}`)
fmt.Printf(Get(val, "Colors", 0).ToString())
// Output:
// Crimson
}
func ExampleMyKey() {
hello := MyKey("hello")
output, _ := Marshal(map[*MyKey]string{&hello: "world"})
fmt.Println(string(output))
obj := map[*MyKey]string{}
Unmarshal(output, &obj)
for k, v := range obj {
fmt.Println(*k, v)
}
// Output:
// {"Hello":"world"}
// Hel world
}
type MyKey string
func (m *MyKey) MarshalText() ([]byte, error) {
return []byte(strings.Replace(string(*m), "h", "H", -1)), nil
}
func (m *MyKey) UnmarshalText(text []byte) error {
*m = MyKey(text[:3])
return nil
}

View File

@ -0,0 +1,223 @@
package test
import (
"bytes"
"fmt"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"strconv"
"testing"
"time"
"unsafe"
)
func Test_customize_type_decoder(t *testing.T) {
t.Skip()
jsoniter.RegisterTypeDecoderFunc("time.Time", func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
t, err := time.ParseInLocation("2006-01-02 15:04:05", iter.ReadString(), time.UTC)
if err != nil {
iter.Error = err
return
}
*((*time.Time)(ptr)) = t
})
//defer jsoniter.ConfigDefault.(*frozenConfig).cleanDecoders()
val := time.Time{}
err := jsoniter.Unmarshal([]byte(`"2016-12-05 08:43:28"`), &val)
if err != nil {
t.Fatal(err)
}
year, month, day := val.Date()
if year != 2016 || month != 12 || day != 5 {
t.Fatal(val)
}
}
func Test_customize_byte_array_encoder(t *testing.T) {
t.Skip()
//jsoniter.ConfigDefault.(*frozenConfig).cleanEncoders()
should := require.New(t)
jsoniter.RegisterTypeEncoderFunc("[]uint8", func(ptr unsafe.Pointer, stream *jsoniter.Stream) {
t := *((*[]byte)(ptr))
stream.WriteString(string(t))
}, nil)
//defer jsoniter.ConfigDefault.(*frozenConfig).cleanEncoders()
val := []byte("abc")
str, err := jsoniter.MarshalToString(val)
should.Nil(err)
should.Equal(`"abc"`, str)
}
type CustomEncoderAttachmentTestStruct struct {
Value int32 `json:"value"`
}
type CustomEncoderAttachmentTestStructEncoder struct {}
func (c *CustomEncoderAttachmentTestStructEncoder) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
attachVal, ok := stream.Attachment.(int)
stream.WriteRaw(`"`)
stream.WriteRaw(fmt.Sprintf("%t %d", ok, attachVal))
stream.WriteRaw(`"`)
}
func (c *CustomEncoderAttachmentTestStructEncoder) IsEmpty(ptr unsafe.Pointer) bool {
return false
}
func Test_custom_encoder_attachment(t *testing.T) {
jsoniter.RegisterTypeEncoder("test.CustomEncoderAttachmentTestStruct", &CustomEncoderAttachmentTestStructEncoder{})
expectedValue := 17
should := require.New(t)
buf := &bytes.Buffer{}
stream := jsoniter.NewStream(jsoniter.Config{SortMapKeys: true}.Froze(), buf, 4096)
stream.Attachment = expectedValue
val := map[string]CustomEncoderAttachmentTestStruct{"a": {}}
stream.WriteVal(val)
stream.Flush()
should.Nil(stream.Error)
should.Equal("{\"a\":\"true 17\"}", buf.String())
}
func Test_customize_field_decoder(t *testing.T) {
type Tom struct {
field1 string
}
jsoniter.RegisterFieldDecoderFunc("jsoniter.Tom", "field1", func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
*((*string)(ptr)) = strconv.Itoa(iter.ReadInt())
})
//defer jsoniter.ConfigDefault.(*frozenConfig).cleanDecoders()
tom := Tom{}
err := jsoniter.Unmarshal([]byte(`{"field1": 100}`), &tom)
if err != nil {
t.Fatal(err)
}
}
func Test_recursive_empty_interface_customization(t *testing.T) {
t.Skip()
var obj interface{}
jsoniter.RegisterTypeDecoderFunc("interface {}", func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
switch iter.WhatIsNext() {
case jsoniter.NumberValue:
*(*interface{})(ptr) = iter.ReadInt64()
default:
*(*interface{})(ptr) = iter.Read()
}
})
should := require.New(t)
jsoniter.Unmarshal([]byte("[100]"), &obj)
should.Equal([]interface{}{int64(100)}, obj)
}
type MyInterface interface {
Hello() string
}
type MyString string
func (ms MyString) Hello() string {
return string(ms)
}
func Test_read_custom_interface(t *testing.T) {
t.Skip()
should := require.New(t)
var val MyInterface
jsoniter.RegisterTypeDecoderFunc("jsoniter.MyInterface", func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
*((*MyInterface)(ptr)) = MyString(iter.ReadString())
})
err := jsoniter.UnmarshalFromString(`"hello"`, &val)
should.Nil(err)
should.Equal("hello", val.Hello())
}
const flow1 = `
{"A":"hello"}
{"A":"hello"}
{"A":"hello"}
{"A":"hello"}
{"A":"hello"}`
const flow2 = `
{"A":"hello"}
{"A":"hello"}
{"A":"hello"}
{"A":"hello"}
{"A":"hello"}
`
type (
Type1 struct {
A string
}
Type2 struct {
A string
}
)
func (t *Type2) UnmarshalJSON(data []byte) error {
return nil
}
func (t *Type2) MarshalJSON() ([]byte, error) {
return nil, nil
}
func TestType1NoFinalLF(t *testing.T) {
reader := bytes.NewReader([]byte(flow1))
dec := jsoniter.NewDecoder(reader)
i := 0
for dec.More() {
data := &Type1{}
if err := dec.Decode(data); err != nil {
t.Errorf("at %v got %v", i, err)
}
i++
}
}
func TestType1FinalLF(t *testing.T) {
reader := bytes.NewReader([]byte(flow2))
dec := jsoniter.NewDecoder(reader)
i := 0
for dec.More() {
data := &Type1{}
if err := dec.Decode(data); err != nil {
t.Errorf("at %v got %v", i, err)
}
i++
}
}
func TestType2NoFinalLF(t *testing.T) {
reader := bytes.NewReader([]byte(flow1))
dec := jsoniter.NewDecoder(reader)
i := 0
for dec.More() {
data := &Type2{}
if err := dec.Decode(data); err != nil {
t.Errorf("at %v got %v", i, err)
}
i++
}
}
func TestType2FinalLF(t *testing.T) {
reader := bytes.NewReader([]byte(flow2))
dec := jsoniter.NewDecoder(reader)
i := 0
for dec.More() {
data := &Type2{}
if err := dec.Decode(data); err != nil {
t.Errorf("at %v got %v", i, err)
}
i++
}
}

View File

@ -0,0 +1,120 @@
package test
import (
"github.com/json-iterator/go"
"github.com/modern-go/reflect2"
"github.com/stretchr/testify/require"
"reflect"
"strconv"
"testing"
"unsafe"
)
type TestObject1 struct {
Field1 string
}
type testExtension struct {
jsoniter.DummyExtension
}
func (extension *testExtension) UpdateStructDescriptor(structDescriptor *jsoniter.StructDescriptor) {
if structDescriptor.Type.String() != "test.TestObject1" {
return
}
binding := structDescriptor.GetField("Field1")
binding.Encoder = &funcEncoder{fun: func(ptr unsafe.Pointer, stream *jsoniter.Stream) {
str := *((*string)(ptr))
val, _ := strconv.Atoi(str)
stream.WriteInt(val)
}}
binding.Decoder = &funcDecoder{func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
*((*string)(ptr)) = strconv.Itoa(iter.ReadInt())
}}
binding.ToNames = []string{"field-1"}
binding.FromNames = []string{"field-1"}
}
func Test_customize_field_by_extension(t *testing.T) {
should := require.New(t)
cfg := jsoniter.Config{}.Froze()
cfg.RegisterExtension(&testExtension{})
obj := TestObject1{}
err := cfg.UnmarshalFromString(`{"field-1": 100}`, &obj)
should.Nil(err)
should.Equal("100", obj.Field1)
str, err := cfg.MarshalToString(obj)
should.Nil(err)
should.Equal(`{"field-1":100}`, str)
}
func Test_customize_map_key_encoder(t *testing.T) {
should := require.New(t)
cfg := jsoniter.Config{}.Froze()
cfg.RegisterExtension(&testMapKeyExtension{})
m := map[int]int{1: 2}
output, err := cfg.MarshalToString(m)
should.NoError(err)
should.Equal(`{"2":2}`, output)
m = map[int]int{}
should.NoError(cfg.UnmarshalFromString(output, &m))
should.Equal(map[int]int{1: 2}, m)
}
type testMapKeyExtension struct {
jsoniter.DummyExtension
}
func (extension *testMapKeyExtension) CreateMapKeyEncoder(typ reflect2.Type) jsoniter.ValEncoder {
if typ.Kind() == reflect.Int {
return &funcEncoder{
fun: func(ptr unsafe.Pointer, stream *jsoniter.Stream) {
stream.WriteRaw(`"`)
stream.WriteInt(*(*int)(ptr) + 1)
stream.WriteRaw(`"`)
},
}
}
return nil
}
func (extension *testMapKeyExtension) CreateMapKeyDecoder(typ reflect2.Type) jsoniter.ValDecoder {
if typ.Kind() == reflect.Int {
return &funcDecoder{
fun: func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
i, err := strconv.Atoi(iter.ReadString())
if err != nil {
iter.ReportError("read map key", err.Error())
return
}
i--
*(*int)(ptr) = i
},
}
}
return nil
}
type funcDecoder struct {
fun jsoniter.DecoderFunc
}
func (decoder *funcDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
decoder.fun(ptr, iter)
}
type funcEncoder struct {
fun jsoniter.EncoderFunc
isEmptyFunc func(ptr unsafe.Pointer) bool
}
func (encoder *funcEncoder) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
encoder.fun(ptr, stream)
}
func (encoder *funcEncoder) IsEmpty(ptr unsafe.Pointer) bool {
if encoder.isEmptyFunc == nil {
return false
}
return encoder.isEmptyFunc(ptr)
}

View File

@ -0,0 +1,238 @@
package extra
import (
"github.com/json-iterator/go"
"github.com/modern-go/reflect2"
"unicode/utf8"
"unsafe"
)
// safeSet holds the value true if the ASCII character with the given array
// position can be represented inside a JSON string without any further
// escaping.
//
// All values are true except for the ASCII control characters (0-31), the
// double quote ("), and the backslash character ("\").
var safeSet = [utf8.RuneSelf]bool{
' ': true,
'!': true,
'"': false,
'#': true,
'$': true,
'%': true,
'&': true,
'\'': true,
'(': true,
')': true,
'*': true,
'+': true,
',': true,
'-': true,
'.': true,
'/': true,
'0': true,
'1': true,
'2': true,
'3': true,
'4': true,
'5': true,
'6': true,
'7': true,
'8': true,
'9': true,
':': true,
';': true,
'<': true,
'=': true,
'>': true,
'?': true,
'@': true,
'A': true,
'B': true,
'C': true,
'D': true,
'E': true,
'F': true,
'G': true,
'H': true,
'I': true,
'J': true,
'K': true,
'L': true,
'M': true,
'N': true,
'O': true,
'P': true,
'Q': true,
'R': true,
'S': true,
'T': true,
'U': true,
'V': true,
'W': true,
'X': true,
'Y': true,
'Z': true,
'[': true,
'\\': false,
']': true,
'^': true,
'_': true,
'`': true,
'a': true,
'b': true,
'c': true,
'd': true,
'e': true,
'f': true,
'g': true,
'h': true,
'i': true,
'j': true,
'k': true,
'l': true,
'm': true,
'n': true,
'o': true,
'p': true,
'q': true,
'r': true,
's': true,
't': true,
'u': true,
'v': true,
'w': true,
'x': true,
'y': true,
'z': true,
'{': true,
'|': true,
'}': true,
'~': true,
'\u007f': true,
}
var binaryType = reflect2.TypeOfPtr((*[]byte)(nil)).Elem()
type BinaryAsStringExtension struct {
jsoniter.DummyExtension
}
func (extension *BinaryAsStringExtension) CreateEncoder(typ reflect2.Type) jsoniter.ValEncoder {
if typ == binaryType {
return &binaryAsStringCodec{}
}
return nil
}
func (extension *BinaryAsStringExtension) CreateDecoder(typ reflect2.Type) jsoniter.ValDecoder {
if typ == binaryType {
return &binaryAsStringCodec{}
}
return nil
}
type binaryAsStringCodec struct {
}
func (codec *binaryAsStringCodec) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
rawBytes := iter.ReadStringAsSlice()
bytes := make([]byte, 0, len(rawBytes))
for i := 0; i < len(rawBytes); i++ {
b := rawBytes[i]
if b == '\\' {
b2 := rawBytes[i+1]
if b2 != '\\' {
iter.ReportError("decode binary as string", `\\x is only supported escape`)
return
}
b3 := rawBytes[i+2]
if b3 != 'x' {
iter.ReportError("decode binary as string", `\\x is only supported escape`)
return
}
b4 := rawBytes[i+3]
b5 := rawBytes[i+4]
i += 4
b = readHex(iter, b4, b5)
}
bytes = append(bytes, b)
}
*(*[]byte)(ptr) = bytes
}
func (codec *binaryAsStringCodec) IsEmpty(ptr unsafe.Pointer) bool {
return len(*((*[]byte)(ptr))) == 0
}
func (codec *binaryAsStringCodec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
newBuffer := writeBytes(stream.Buffer(), *(*[]byte)(ptr))
stream.SetBuffer(newBuffer)
}
func readHex(iter *jsoniter.Iterator, b1, b2 byte) byte {
var ret byte
if b1 >= '0' && b1 <= '9' {
ret = b1 - '0'
} else if b1 >= 'a' && b1 <= 'f' {
ret = b1 - 'a' + 10
} else {
iter.ReportError("read hex", "expects 0~9 or a~f, but found "+string([]byte{b1}))
return 0
}
ret *= 16
if b2 >= '0' && b2 <= '9' {
ret = b2 - '0'
} else if b2 >= 'a' && b2 <= 'f' {
ret = b2 - 'a' + 10
} else {
iter.ReportError("read hex", "expects 0~9 or a~f, but found "+string([]byte{b2}))
return 0
}
return ret
}
var hex = "0123456789abcdef"
func writeBytes(space []byte, s []byte) []byte {
space = append(space, '"')
// write string, the fast path, without utf8 and escape support
var i int
var c byte
for i, c = range s {
if c < utf8.RuneSelf && safeSet[c] {
space = append(space, c)
} else {
break
}
}
if i == len(s)-1 {
space = append(space, '"')
return space
}
return writeBytesSlowPath(space, s[i:])
}
func writeBytesSlowPath(space []byte, s []byte) []byte {
start := 0
// for the remaining parts, we process them char by char
var i int
var b byte
for i, b = range s {
if b >= utf8.RuneSelf {
space = append(space, '\\', '\\', 'x', hex[b>>4], hex[b&0xF])
start = i + 1
continue
}
if safeSet[b] {
continue
}
if start < i {
space = append(space, s[start:i]...)
}
space = append(space, '\\', '\\', 'x', hex[b>>4], hex[b&0xF])
start = i + 1
}
if start < len(s) {
space = append(space, s[start:]...)
}
return append(space, '"')
}

View File

@ -0,0 +1,32 @@
package extra
import (
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"testing"
)
func init() {
jsoniter.RegisterExtension(&BinaryAsStringExtension{})
}
func TestBinaryAsStringCodec(t *testing.T) {
t.Run("safe set", func(t *testing.T) {
should := require.New(t)
output, err := jsoniter.Marshal([]byte("hello"))
should.NoError(err)
should.Equal(`"hello"`, string(output))
var val []byte
should.NoError(jsoniter.Unmarshal(output, &val))
should.Equal(`hello`, string(val))
})
t.Run("non safe set", func(t *testing.T) {
should := require.New(t)
output, err := jsoniter.Marshal([]byte{1, 2, 3, 15})
should.NoError(err)
should.Equal(`"\\x01\\x02\\x03\\x0f"`, string(output))
var val []byte
should.NoError(jsoniter.Unmarshal(output, &val))
should.Equal([]byte{1, 2, 3, 15}, val)
})
}

294
extra/fuzzy_decoder.go Normal file
View File

@ -0,0 +1,294 @@
package extra
import (
"encoding/json"
"io"
"math"
"reflect"
"strings"
"unsafe"
"github.com/json-iterator/go"
"github.com/modern-go/reflect2"
)
const maxUint = ^uint(0)
const maxInt = int(maxUint >> 1)
const minInt = -maxInt - 1
// RegisterFuzzyDecoders decode input from PHP with tolerance.
// It will handle string/number auto conversation, and treat empty [] as empty struct.
func RegisterFuzzyDecoders() {
jsoniter.RegisterExtension(&tolerateEmptyArrayExtension{})
jsoniter.RegisterTypeDecoder("string", &fuzzyStringDecoder{})
jsoniter.RegisterTypeDecoder("float32", &fuzzyFloat32Decoder{})
jsoniter.RegisterTypeDecoder("float64", &fuzzyFloat64Decoder{})
jsoniter.RegisterTypeDecoder("int", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
if isFloat {
val := iter.ReadFloat64()
if val > float64(maxInt) || val < float64(minInt) {
iter.ReportError("fuzzy decode int", "exceed range")
return
}
*((*int)(ptr)) = int(val)
} else {
*((*int)(ptr)) = iter.ReadInt()
}
}})
jsoniter.RegisterTypeDecoder("uint", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
if isFloat {
val := iter.ReadFloat64()
if val > float64(maxUint) || val < 0 {
iter.ReportError("fuzzy decode uint", "exceed range")
return
}
*((*uint)(ptr)) = uint(val)
} else {
*((*uint)(ptr)) = iter.ReadUint()
}
}})
jsoniter.RegisterTypeDecoder("int8", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
if isFloat {
val := iter.ReadFloat64()
if val > float64(math.MaxInt8) || val < float64(math.MinInt8) {
iter.ReportError("fuzzy decode int8", "exceed range")
return
}
*((*int8)(ptr)) = int8(val)
} else {
*((*int8)(ptr)) = iter.ReadInt8()
}
}})
jsoniter.RegisterTypeDecoder("uint8", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
if isFloat {
val := iter.ReadFloat64()
if val > float64(math.MaxUint8) || val < 0 {
iter.ReportError("fuzzy decode uint8", "exceed range")
return
}
*((*uint8)(ptr)) = uint8(val)
} else {
*((*uint8)(ptr)) = iter.ReadUint8()
}
}})
jsoniter.RegisterTypeDecoder("int16", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
if isFloat {
val := iter.ReadFloat64()
if val > float64(math.MaxInt16) || val < float64(math.MinInt16) {
iter.ReportError("fuzzy decode int16", "exceed range")
return
}
*((*int16)(ptr)) = int16(val)
} else {
*((*int16)(ptr)) = iter.ReadInt16()
}
}})
jsoniter.RegisterTypeDecoder("uint16", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
if isFloat {
val := iter.ReadFloat64()
if val > float64(math.MaxUint16) || val < 0 {
iter.ReportError("fuzzy decode uint16", "exceed range")
return
}
*((*uint16)(ptr)) = uint16(val)
} else {
*((*uint16)(ptr)) = iter.ReadUint16()
}
}})
jsoniter.RegisterTypeDecoder("int32", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
if isFloat {
val := iter.ReadFloat64()
if val > float64(math.MaxInt32) || val < float64(math.MinInt32) {
iter.ReportError("fuzzy decode int32", "exceed range")
return
}
*((*int32)(ptr)) = int32(val)
} else {
*((*int32)(ptr)) = iter.ReadInt32()
}
}})
jsoniter.RegisterTypeDecoder("uint32", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
if isFloat {
val := iter.ReadFloat64()
if val > float64(math.MaxUint32) || val < 0 {
iter.ReportError("fuzzy decode uint32", "exceed range")
return
}
*((*uint32)(ptr)) = uint32(val)
} else {
*((*uint32)(ptr)) = iter.ReadUint32()
}
}})
jsoniter.RegisterTypeDecoder("int64", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
if isFloat {
val := iter.ReadFloat64()
if val > float64(math.MaxInt64) || val < float64(math.MinInt64) {
iter.ReportError("fuzzy decode int64", "exceed range")
return
}
*((*int64)(ptr)) = int64(val)
} else {
*((*int64)(ptr)) = iter.ReadInt64()
}
}})
jsoniter.RegisterTypeDecoder("uint64", &fuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) {
if isFloat {
val := iter.ReadFloat64()
if val > float64(math.MaxUint64) || val < 0 {
iter.ReportError("fuzzy decode uint64", "exceed range")
return
}
*((*uint64)(ptr)) = uint64(val)
} else {
*((*uint64)(ptr)) = iter.ReadUint64()
}
}})
}
type tolerateEmptyArrayExtension struct {
jsoniter.DummyExtension
}
func (extension *tolerateEmptyArrayExtension) DecorateDecoder(typ reflect2.Type, decoder jsoniter.ValDecoder) jsoniter.ValDecoder {
if typ.Kind() == reflect.Struct || typ.Kind() == reflect.Map {
return &tolerateEmptyArrayDecoder{decoder}
}
return decoder
}
type tolerateEmptyArrayDecoder struct {
valDecoder jsoniter.ValDecoder
}
func (decoder *tolerateEmptyArrayDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
if iter.WhatIsNext() == jsoniter.ArrayValue {
iter.Skip()
newIter := iter.Pool().BorrowIterator([]byte("{}"))
defer iter.Pool().ReturnIterator(newIter)
decoder.valDecoder.Decode(ptr, newIter)
} else {
decoder.valDecoder.Decode(ptr, iter)
}
}
type fuzzyStringDecoder struct {
}
func (decoder *fuzzyStringDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
valueType := iter.WhatIsNext()
switch valueType {
case jsoniter.NumberValue:
var number json.Number
iter.ReadVal(&number)
*((*string)(ptr)) = string(number)
case jsoniter.StringValue:
*((*string)(ptr)) = iter.ReadString()
case jsoniter.NilValue:
iter.Skip()
*((*string)(ptr)) = ""
default:
iter.ReportError("fuzzyStringDecoder", "not number or string")
}
}
type fuzzyIntegerDecoder struct {
fun func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator)
}
func (decoder *fuzzyIntegerDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
valueType := iter.WhatIsNext()
var str string
switch valueType {
case jsoniter.NumberValue:
var number json.Number
iter.ReadVal(&number)
str = string(number)
case jsoniter.StringValue:
str = iter.ReadString()
case jsoniter.BoolValue:
if iter.ReadBool() {
str = "1"
} else {
str = "0"
}
case jsoniter.NilValue:
iter.Skip()
str = "0"
default:
iter.ReportError("fuzzyIntegerDecoder", "not number or string")
}
if len(str) == 0 {
str = "0"
}
newIter := iter.Pool().BorrowIterator([]byte(str))
defer iter.Pool().ReturnIterator(newIter)
isFloat := strings.IndexByte(str, '.') != -1
decoder.fun(isFloat, ptr, newIter)
if newIter.Error != nil && newIter.Error != io.EOF {
iter.Error = newIter.Error
}
}
type fuzzyFloat32Decoder struct {
}
func (decoder *fuzzyFloat32Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
valueType := iter.WhatIsNext()
var str string
switch valueType {
case jsoniter.NumberValue:
*((*float32)(ptr)) = iter.ReadFloat32()
case jsoniter.StringValue:
str = iter.ReadString()
newIter := iter.Pool().BorrowIterator([]byte(str))
defer iter.Pool().ReturnIterator(newIter)
*((*float32)(ptr)) = newIter.ReadFloat32()
if newIter.Error != nil && newIter.Error != io.EOF {
iter.Error = newIter.Error
}
case jsoniter.BoolValue:
// support bool to float32
if iter.ReadBool() {
*((*float32)(ptr)) = 1
} else {
*((*float32)(ptr)) = 0
}
case jsoniter.NilValue:
iter.Skip()
*((*float32)(ptr)) = 0
default:
iter.ReportError("fuzzyFloat32Decoder", "not number or string")
}
}
type fuzzyFloat64Decoder struct {
}
func (decoder *fuzzyFloat64Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
valueType := iter.WhatIsNext()
var str string
switch valueType {
case jsoniter.NumberValue:
*((*float64)(ptr)) = iter.ReadFloat64()
case jsoniter.StringValue:
str = iter.ReadString()
newIter := iter.Pool().BorrowIterator([]byte(str))
defer iter.Pool().ReturnIterator(newIter)
*((*float64)(ptr)) = newIter.ReadFloat64()
if newIter.Error != nil && newIter.Error != io.EOF {
iter.Error = newIter.Error
}
case jsoniter.BoolValue:
// support bool to float64
if iter.ReadBool() {
*((*float64)(ptr)) = 1
} else {
*((*float64)(ptr)) = 0
}
case jsoniter.NilValue:
iter.Skip()
*((*float64)(ptr)) = 0
default:
iter.ReportError("fuzzyFloat64Decoder", "not number or string")
}
}

393
extra/fuzzy_decoder_test.go Normal file
View File

@ -0,0 +1,393 @@
package extra
import (
"testing"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
func init() {
RegisterFuzzyDecoders()
}
func Test_any_to_string(t *testing.T) {
should := require.New(t)
var val string
should.Nil(jsoniter.UnmarshalFromString(`"100"`, &val))
should.Equal("100", val)
should.Nil(jsoniter.UnmarshalFromString("10", &val))
should.Equal("10", val)
should.Nil(jsoniter.UnmarshalFromString("10.1", &val))
should.Equal("10.1", val)
should.Nil(jsoniter.UnmarshalFromString(`"10.1"`, &val))
should.Equal("10.1", val)
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
}
func Test_any_to_int64(t *testing.T) {
should := require.New(t)
var val int64
should.Nil(jsoniter.UnmarshalFromString(`"100"`, &val))
should.Equal(int64(100), val)
should.Nil(jsoniter.UnmarshalFromString(`"10.1"`, &val))
should.Equal(int64(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10.1`, &val))
should.Equal(int64(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(int64(10), val)
should.Nil(jsoniter.UnmarshalFromString(`""`, &val))
should.Equal(int64(0), val)
// bool part
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
should.Equal(int64(0), val)
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
should.Equal(int64(1), val)
should.Nil(jsoniter.UnmarshalFromString(`-10`, &val))
should.Equal(int64(-10), val)
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
// large float to int
should.NotNil(jsoniter.UnmarshalFromString(`1234512345123451234512345.0`, &val))
}
func Test_any_to_int(t *testing.T) {
should := require.New(t)
var val int
should.Nil(jsoniter.UnmarshalFromString(`"100"`, &val))
should.Equal(100, val)
should.Nil(jsoniter.UnmarshalFromString(`"10.1"`, &val))
should.Equal(10, val)
should.Nil(jsoniter.UnmarshalFromString(`10.1`, &val))
should.Equal(10, val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(10, val)
// bool part
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
should.Equal(0, val)
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
should.Equal(1, val)
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
// large float to int
should.NotNil(jsoniter.UnmarshalFromString(`1234512345123451234512345.0`, &val))
}
func Test_any_to_int16(t *testing.T) {
should := require.New(t)
var val int16
should.Nil(jsoniter.UnmarshalFromString(`"100"`, &val))
should.Equal(int16(100), val)
should.Nil(jsoniter.UnmarshalFromString(`"10.1"`, &val))
should.Equal(int16(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10.1`, &val))
should.Equal(int16(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(int16(10), val)
// bool part
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
should.Equal(int16(0), val)
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
should.Equal(int16(1), val)
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
// large float to int
should.NotNil(jsoniter.UnmarshalFromString(`1234512345123451234512345.0`, &val))
}
func Test_any_to_int32(t *testing.T) {
should := require.New(t)
var val int32
should.Nil(jsoniter.UnmarshalFromString(`"100"`, &val))
should.Equal(int32(100), val)
should.Nil(jsoniter.UnmarshalFromString(`"10.1"`, &val))
should.Equal(int32(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10.1`, &val))
should.Equal(int32(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(int32(10), val)
// bool part
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
should.Equal(int32(0), val)
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
should.Equal(int32(1), val)
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
// large float to int
should.NotNil(jsoniter.UnmarshalFromString(`1234512345123451234512345.0`, &val))
}
func Test_any_to_int8(t *testing.T) {
should := require.New(t)
var val int8
should.Nil(jsoniter.UnmarshalFromString(`"100"`, &val))
should.Equal(int8(100), val)
should.Nil(jsoniter.UnmarshalFromString(`"10.1"`, &val))
should.Equal(int8(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10.1`, &val))
should.Equal(int8(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(int8(10), val)
// bool part
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
should.Equal(int8(0), val)
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
should.Equal(int8(1), val)
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
// large float to int
should.NotNil(jsoniter.UnmarshalFromString(`1234512345123451234512345.0`, &val))
}
func Test_any_to_uint8(t *testing.T) {
should := require.New(t)
var val uint8
should.Nil(jsoniter.UnmarshalFromString(`"100"`, &val))
should.Equal(uint8(100), val)
should.Nil(jsoniter.UnmarshalFromString(`"10.1"`, &val))
should.Equal(uint8(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10.1`, &val))
should.Equal(uint8(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(uint8(10), val)
// bool part
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
should.Equal(uint8(0), val)
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
should.Equal(uint8(1), val)
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
// large float to int
should.NotNil(jsoniter.UnmarshalFromString(`1234512345123451234512345.0`, &val))
}
func Test_any_to_uint64(t *testing.T) {
should := require.New(t)
var val uint64
should.Nil(jsoniter.UnmarshalFromString(`"100"`, &val))
should.Equal(uint64(100), val)
should.Nil(jsoniter.UnmarshalFromString(`"10.1"`, &val))
should.Equal(uint64(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10.1`, &val))
should.Equal(uint64(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(uint64(10), val)
// bool part
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
should.Equal(uint64(0), val)
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
should.Equal(uint64(1), val)
// TODO fix?
should.NotNil(jsoniter.UnmarshalFromString(`-10`, &val))
should.Equal(uint64(0), val)
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
// large float to int
should.NotNil(jsoniter.UnmarshalFromString(`1234512345123451234512345.0`, &val))
}
func Test_any_to_uint32(t *testing.T) {
should := require.New(t)
var val uint32
should.Nil(jsoniter.UnmarshalFromString(`"100"`, &val))
should.Equal(uint32(100), val)
should.Nil(jsoniter.UnmarshalFromString(`"10.1"`, &val))
should.Equal(uint32(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10.1`, &val))
should.Equal(uint32(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(uint32(10), val)
// bool part
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
should.Equal(uint32(0), val)
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
should.Equal(uint32(1), val)
// TODO fix?
should.NotNil(jsoniter.UnmarshalFromString(`-10`, &val))
should.Equal(uint32(0), val)
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
// large float to int
should.NotNil(jsoniter.UnmarshalFromString(`1234512345123451234512345.0`, &val))
}
func Test_any_to_uint16(t *testing.T) {
should := require.New(t)
var val uint16
should.Nil(jsoniter.UnmarshalFromString(`"100"`, &val))
should.Equal(uint16(100), val)
should.Nil(jsoniter.UnmarshalFromString(`"10.1"`, &val))
should.Equal(uint16(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10.1`, &val))
should.Equal(uint16(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(uint16(10), val)
// bool part
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
should.Equal(uint16(0), val)
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
should.Equal(uint16(1), val)
// TODO fix?
should.NotNil(jsoniter.UnmarshalFromString(`-10`, &val))
should.Equal(uint16(0), val)
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
// large float to int
should.NotNil(jsoniter.UnmarshalFromString(`1234512345123451234512345.0`, &val))
}
func Test_any_to_uint(t *testing.T) {
should := require.New(t)
var val uint
should.Nil(jsoniter.UnmarshalFromString(`"100"`, &val))
should.Equal(uint(100), val)
should.Nil(jsoniter.UnmarshalFromString(`"10.1"`, &val))
should.Equal(uint(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10.1`, &val))
should.Equal(uint(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(uint(10), val)
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
should.Equal(uint(0), val)
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
should.Equal(uint(1), val)
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
// large float to int
should.NotNil(jsoniter.UnmarshalFromString(`1234512345123451234512345.0`, &val))
}
func Test_any_to_float32(t *testing.T) {
should := require.New(t)
var val float32
should.Nil(jsoniter.UnmarshalFromString(`"100"`, &val))
should.Equal(float32(100), val)
should.Nil(jsoniter.UnmarshalFromString(`"10.1"`, &val))
should.Equal(float32(10.1), val)
should.Nil(jsoniter.UnmarshalFromString(`10.1`, &val))
should.Equal(float32(10.1), val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(float32(10), val)
// bool part
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
should.Equal(float32(0), val)
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
should.Equal(float32(1), val)
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
}
func Test_any_to_float64(t *testing.T) {
should := require.New(t)
var val float64
should.Nil(jsoniter.UnmarshalFromString(`"100"`, &val))
should.Equal(float64(100), val)
should.Nil(jsoniter.UnmarshalFromString(`"10.1"`, &val))
should.Equal(float64(10.1), val)
should.Nil(jsoniter.UnmarshalFromString(`10.1`, &val))
should.Equal(float64(10.1), val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(float64(10), val)
// bool part
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
should.Equal(float64(0), val)
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
should.Equal(float64(1), val)
should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
}
func Test_empty_array_as_map(t *testing.T) {
should := require.New(t)
var val map[string]interface{}
should.Nil(jsoniter.UnmarshalFromString(`[]`, &val))
should.Equal(map[string]interface{}{}, val)
}
func Test_empty_array_as_object(t *testing.T) {
should := require.New(t)
var val struct{}
should.Nil(jsoniter.UnmarshalFromString(`[]`, &val))
should.Equal(struct{}{}, val)
}
func Test_bad_case(t *testing.T) {
var jsonstr = `
{
"extra_type": 181760,
"combo_type": 0,
"trigger_time_ms": 1498800398000,
"_create_time": "2017-06-16 11:21:39",
"_msg_type": 41000
}
`
type OrderEventRequestParams struct {
ExtraType uint64 `json:"extra_type"`
}
var a OrderEventRequestParams
err := jsoniter.UnmarshalFromString(jsonstr, &a)
should := require.New(t)
should.Nil(err)
}
func Test_null_to_string(t *testing.T) {
should := require.New(t)
body := []byte(`null`)
var message string
err := jsoniter.Unmarshal(body, &message)
should.NoError(err)
}
func Test_null_to_int(t *testing.T) {
should := require.New(t)
body := []byte(`null`)
var message int
err := jsoniter.Unmarshal(body, &message)
should.NoError(err)
}
func Test_null_to_float32(t *testing.T) {
should := require.New(t)
body := []byte(`null`)
var message float32
err := jsoniter.Unmarshal(body, &message)
should.NoError(err)
}
func Test_null_to_float64(t *testing.T) {
should := require.New(t)
body := []byte(`null`)
var message float64
err := jsoniter.Unmarshal(body, &message)
should.NoError(err)
}

55
extra/naming_strategy.go Normal file
View File

@ -0,0 +1,55 @@
package extra
import (
"github.com/json-iterator/go"
"strings"
"unicode"
)
// SetNamingStrategy rename struct fields uniformly
func SetNamingStrategy(translate func(string) string) {
jsoniter.RegisterExtension(&namingStrategyExtension{jsoniter.DummyExtension{}, translate})
}
type namingStrategyExtension struct {
jsoniter.DummyExtension
translate func(string) string
}
func (extension *namingStrategyExtension) UpdateStructDescriptor(structDescriptor *jsoniter.StructDescriptor) {
for _, binding := range structDescriptor.Fields {
if unicode.IsLower(rune(binding.Field.Name()[0])) || binding.Field.Name()[0] == '_'{
continue
}
tag, hastag := binding.Field.Tag().Lookup("json")
if hastag {
tagParts := strings.Split(tag, ",")
if tagParts[0] == "-" {
continue // hidden field
}
if tagParts[0] != "" {
continue // field explicitly named
}
}
binding.ToNames = []string{extension.translate(binding.Field.Name())}
binding.FromNames = []string{extension.translate(binding.Field.Name())}
}
}
// LowerCaseWithUnderscores one strategy to SetNamingStrategy for. It will change HelloWorld to hello_world.
func LowerCaseWithUnderscores(name string) string {
newName := []rune{}
for i, c := range name {
if i == 0 {
newName = append(newName, unicode.ToLower(c))
} else {
if unicode.IsUpper(c) {
newName = append(newName, '_')
newName = append(newName, unicode.ToLower(c))
} else {
newName = append(newName, c)
}
}
}
return string(newName)
}

View File

@ -0,0 +1,66 @@
package extra
import (
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"testing"
)
func Test_lower_case_with_underscores(t *testing.T) {
should := require.New(t)
should.Equal("hello_world", LowerCaseWithUnderscores("helloWorld"))
should.Equal("hello_world", LowerCaseWithUnderscores("HelloWorld"))
SetNamingStrategy(LowerCaseWithUnderscores)
output, err := jsoniter.Marshal(struct {
UserName string
FirstLanguage string
}{
UserName: "taowen",
FirstLanguage: "Chinese",
})
should.Nil(err)
should.Equal(`{"user_name":"taowen","first_language":"Chinese"}`, string(output))
}
func Test_set_naming_strategy_with_overrides(t *testing.T) {
should := require.New(t)
SetNamingStrategy(LowerCaseWithUnderscores)
output, err := jsoniter.Marshal(struct {
UserName string `json:"UserName"`
FirstLanguage string
}{
UserName: "taowen",
FirstLanguage: "Chinese",
})
should.Nil(err)
should.Equal(`{"UserName":"taowen","first_language":"Chinese"}`, string(output))
}
func Test_set_naming_strategy_with_omitempty(t *testing.T) {
should := require.New(t)
SetNamingStrategy(LowerCaseWithUnderscores)
output, err := jsoniter.Marshal(struct {
UserName string
FirstLanguage string `json:",omitempty"`
}{
UserName: "taowen",
})
should.Nil(err)
should.Equal(`{"user_name":"taowen"}`, string(output))
}
func Test_set_naming_strategy_with_private_field(t *testing.T) {
should := require.New(t)
SetNamingStrategy(LowerCaseWithUnderscores)
output, err := jsoniter.Marshal(struct {
UserName string
userId int
_UserAge int
}{
UserName: "allen",
userId: 100,
_UserAge: 30,
})
should.Nil(err)
should.Equal(`{"user_name":"allen"}`, string(output))
}

54
extra/privat_fields.go Normal file
View File

@ -0,0 +1,54 @@
package extra
import (
"github.com/json-iterator/go"
"strings"
"unicode"
)
// SupportPrivateFields include private fields when encoding/decoding
func SupportPrivateFields() {
jsoniter.RegisterExtension(&privateFieldsExtension{})
}
type privateFieldsExtension struct {
jsoniter.DummyExtension
}
func (extension *privateFieldsExtension) UpdateStructDescriptor(structDescriptor *jsoniter.StructDescriptor) {
for _, binding := range structDescriptor.Fields {
isPrivate := unicode.IsLower(rune(binding.Field.Name()[0]))
if isPrivate {
tag, hastag := binding.Field.Tag().Lookup("json")
if !hastag {
binding.FromNames = []string{binding.Field.Name()}
binding.ToNames = []string{binding.Field.Name()}
continue
}
tagParts := strings.Split(tag, ",")
names := calcFieldNames(binding.Field.Name(), tagParts[0], tag)
binding.FromNames = names
binding.ToNames = names
}
}
}
func calcFieldNames(originalFieldName string, tagProvidedFieldName string, wholeTag string) []string {
// ignore?
if wholeTag == "-" {
return []string{}
}
// rename?
var fieldNames []string
if tagProvidedFieldName == "" {
fieldNames = []string{originalFieldName}
} else {
fieldNames = []string{tagProvidedFieldName}
}
// private?
isNotExported := unicode.IsLower(rune(originalFieldName[0]))
if isNotExported {
fieldNames = []string{}
}
return fieldNames
}

View File

@ -0,0 +1,18 @@
package extra
import (
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"testing"
)
func Test_private_fields(t *testing.T) {
type TestObject struct {
field1 string
}
SupportPrivateFields()
should := require.New(t)
obj := TestObject{}
should.Nil(jsoniter.UnmarshalFromString(`{"field1":"Hello"}`, &obj))
should.Equal("Hello", obj.field1)
}

View File

@ -0,0 +1,31 @@
package extra
import (
"github.com/json-iterator/go"
"time"
"unsafe"
)
// RegisterTimeAsInt64Codec encode/decode time since number of unit since epoch. the precision is the unit.
func RegisterTimeAsInt64Codec(precision time.Duration) {
jsoniter.RegisterTypeEncoder("time.Time", &timeAsInt64Codec{precision})
jsoniter.RegisterTypeDecoder("time.Time", &timeAsInt64Codec{precision})
}
type timeAsInt64Codec struct {
precision time.Duration
}
func (codec *timeAsInt64Codec) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
nanoseconds := iter.ReadInt64() * codec.precision.Nanoseconds()
*((*time.Time)(ptr)) = time.Unix(0, nanoseconds)
}
func (codec *timeAsInt64Codec) IsEmpty(ptr unsafe.Pointer) bool {
ts := *((*time.Time)(ptr))
return ts.UnixNano() == 0
}
func (codec *timeAsInt64Codec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
ts := *((*time.Time)(ptr))
stream.WriteInt64(ts.UnixNano() / codec.precision.Nanoseconds())
}

View File

@ -0,0 +1,31 @@
package extra
import (
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"testing"
"time"
)
func Test_time_as_int64(t *testing.T) {
should := require.New(t)
RegisterTimeAsInt64Codec(time.Nanosecond)
output, err := jsoniter.Marshal(time.Unix(1497952257, 1002))
should.Nil(err)
should.Equal("1497952257000001002", string(output))
var val time.Time
should.Nil(jsoniter.Unmarshal(output, &val))
should.Equal(int64(1497952257000001002), val.UnixNano())
}
func Test_time_as_int64_keep_microsecond(t *testing.T) {
t.Skip("conflict")
should := require.New(t)
RegisterTimeAsInt64Codec(time.Microsecond)
output, err := jsoniter.Marshal(time.Unix(1, 1002))
should.Nil(err)
should.Equal("1000001", string(output))
var val time.Time
should.Nil(jsoniter.Unmarshal(output, &val))
should.Equal(int64(1000001000), val.UnixNano())
}

View File

@ -0,0 +1,7 @@
| json type \ dest type | bool | int | uint | float |string|
| --- | --- | --- | --- |--|--|
| number | positive => true <br/> negative => true <br/> zero => false| 23.2 => 23 <br/> -32.1 => -32| 12.1 => 12 <br/> -12.1 => 0|as normal|same as origin|
| string | empty string => false <br/> string "0" => false <br/> other strings => true | "123.32" => 123 <br/> "-123.4" => -123 <br/> "123.23xxxw" => 123 <br/> "abcde12" => 0 <br/> "-32.1" => -32| 13.2 => 13 <br/> -1.1 => 0 |12.1 => 12.1 <br/> -12.3 => -12.3<br/> 12.4xxa => 12.4 <br/> +1.1e2 =>110 |same as origin|
| bool | true => true <br/> false => false| true => 1 <br/> false => 0 | true => 1 <br/> false => 0 |true => 1 <br/>false => 0|true => "true" <br/> false => "false"|
| object | true | 0 | 0 |0|originnal json|
| array | empty array => false <br/> nonempty array => true| [] => 0 <br/> [1,2] => 1 | [] => 0 <br/> [1,2] => 1 |[] => 0<br/>[1,2] => 1|original json|

11
go.mod Normal file
View File

@ -0,0 +1,11 @@
module github.com/json-iterator/go
go 1.12
require (
github.com/davecgh/go-spew v1.1.1
github.com/google/gofuzz v1.0.0
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742
github.com/stretchr/testify v1.3.0
)

15
go.sum Normal file
View File

@ -0,0 +1,15 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=

349
iter.go Normal file
View File

@ -0,0 +1,349 @@
package jsoniter
import (
"encoding/json"
"fmt"
"io"
)
// ValueType the type for JSON element
type ValueType int
const (
// InvalidValue invalid JSON element
InvalidValue ValueType = iota
// StringValue JSON element "string"
StringValue
// NumberValue JSON element 100 or 0.10
NumberValue
// NilValue JSON element null
NilValue
// BoolValue JSON element true or false
BoolValue
// ArrayValue JSON element []
ArrayValue
// ObjectValue JSON element {}
ObjectValue
)
var hexDigits []byte
var valueTypes []ValueType
func init() {
hexDigits = make([]byte, 256)
for i := 0; i < len(hexDigits); i++ {
hexDigits[i] = 255
}
for i := '0'; i <= '9'; i++ {
hexDigits[i] = byte(i - '0')
}
for i := 'a'; i <= 'f'; i++ {
hexDigits[i] = byte((i - 'a') + 10)
}
for i := 'A'; i <= 'F'; i++ {
hexDigits[i] = byte((i - 'A') + 10)
}
valueTypes = make([]ValueType, 256)
for i := 0; i < len(valueTypes); i++ {
valueTypes[i] = InvalidValue
}
valueTypes['"'] = StringValue
valueTypes['-'] = NumberValue
valueTypes['0'] = NumberValue
valueTypes['1'] = NumberValue
valueTypes['2'] = NumberValue
valueTypes['3'] = NumberValue
valueTypes['4'] = NumberValue
valueTypes['5'] = NumberValue
valueTypes['6'] = NumberValue
valueTypes['7'] = NumberValue
valueTypes['8'] = NumberValue
valueTypes['9'] = NumberValue
valueTypes['t'] = BoolValue
valueTypes['f'] = BoolValue
valueTypes['n'] = NilValue
valueTypes['['] = ArrayValue
valueTypes['{'] = ObjectValue
}
// Iterator is a io.Reader like object, with JSON specific read functions.
// Error is not returned as return value, but stored as Error member on this iterator instance.
type Iterator struct {
cfg *frozenConfig
reader io.Reader
buf []byte
head int
tail int
depth int
captureStartedAt int
captured []byte
Error error
Attachment interface{} // open for customized decoder
}
// NewIterator creates an empty Iterator instance
func NewIterator(cfg API) *Iterator {
return &Iterator{
cfg: cfg.(*frozenConfig),
reader: nil,
buf: nil,
head: 0,
tail: 0,
depth: 0,
}
}
// Parse creates an Iterator instance from io.Reader
func Parse(cfg API, reader io.Reader, bufSize int) *Iterator {
return &Iterator{
cfg: cfg.(*frozenConfig),
reader: reader,
buf: make([]byte, bufSize),
head: 0,
tail: 0,
depth: 0,
}
}
// ParseBytes creates an Iterator instance from byte array
func ParseBytes(cfg API, input []byte) *Iterator {
return &Iterator{
cfg: cfg.(*frozenConfig),
reader: nil,
buf: input,
head: 0,
tail: len(input),
depth: 0,
}
}
// ParseString creates an Iterator instance from string
func ParseString(cfg API, input string) *Iterator {
return ParseBytes(cfg, []byte(input))
}
// Pool returns a pool can provide more iterator with same configuration
func (iter *Iterator) Pool() IteratorPool {
return iter.cfg
}
// Reset reuse iterator instance by specifying another reader
func (iter *Iterator) Reset(reader io.Reader) *Iterator {
iter.reader = reader
iter.head = 0
iter.tail = 0
iter.depth = 0
return iter
}
// ResetBytes reuse iterator instance by specifying another byte array as input
func (iter *Iterator) ResetBytes(input []byte) *Iterator {
iter.reader = nil
iter.buf = input
iter.head = 0
iter.tail = len(input)
iter.depth = 0
return iter
}
// WhatIsNext gets ValueType of relatively next json element
func (iter *Iterator) WhatIsNext() ValueType {
valueType := valueTypes[iter.nextToken()]
iter.unreadByte()
return valueType
}
func (iter *Iterator) skipWhitespacesWithoutLoadMore() bool {
for i := iter.head; i < iter.tail; i++ {
c := iter.buf[i]
switch c {
case ' ', '\n', '\t', '\r':
continue
}
iter.head = i
return false
}
return true
}
func (iter *Iterator) isObjectEnd() bool {
c := iter.nextToken()
if c == ',' {
return false
}
if c == '}' {
return true
}
iter.ReportError("isObjectEnd", "object ended prematurely, unexpected char "+string([]byte{c}))
return true
}
func (iter *Iterator) nextToken() byte {
// a variation of skip whitespaces, returning the next non-whitespace token
for {
for i := iter.head; i < iter.tail; i++ {
c := iter.buf[i]
switch c {
case ' ', '\n', '\t', '\r':
continue
}
iter.head = i + 1
return c
}
if !iter.loadMore() {
return 0
}
}
}
// ReportError record a error in iterator instance with current position.
func (iter *Iterator) ReportError(operation string, msg string) {
if iter.Error != nil {
if iter.Error != io.EOF {
return
}
}
peekStart := iter.head - 10
if peekStart < 0 {
peekStart = 0
}
peekEnd := iter.head + 10
if peekEnd > iter.tail {
peekEnd = iter.tail
}
parsing := string(iter.buf[peekStart:peekEnd])
contextStart := iter.head - 50
if contextStart < 0 {
contextStart = 0
}
contextEnd := iter.head + 50
if contextEnd > iter.tail {
contextEnd = iter.tail
}
context := string(iter.buf[contextStart:contextEnd])
iter.Error = fmt.Errorf("%s: %s, error found in #%v byte of ...|%s|..., bigger context ...|%s|...",
operation, msg, iter.head-peekStart, parsing, context)
}
// CurrentBuffer gets current buffer as string for debugging purpose
func (iter *Iterator) CurrentBuffer() string {
peekStart := iter.head - 10
if peekStart < 0 {
peekStart = 0
}
return fmt.Sprintf("parsing #%v byte, around ...|%s|..., whole buffer ...|%s|...", iter.head,
string(iter.buf[peekStart:iter.head]), string(iter.buf[0:iter.tail]))
}
func (iter *Iterator) readByte() (ret byte) {
if iter.head == iter.tail {
if iter.loadMore() {
ret = iter.buf[iter.head]
iter.head++
return ret
}
return 0
}
ret = iter.buf[iter.head]
iter.head++
return ret
}
func (iter *Iterator) loadMore() bool {
if iter.reader == nil {
if iter.Error == nil {
iter.head = iter.tail
iter.Error = io.EOF
}
return false
}
if iter.captured != nil {
iter.captured = append(iter.captured,
iter.buf[iter.captureStartedAt:iter.tail]...)
iter.captureStartedAt = 0
}
for {
n, err := iter.reader.Read(iter.buf)
if n == 0 {
if err != nil {
if iter.Error == nil {
iter.Error = err
}
return false
}
} else {
iter.head = 0
iter.tail = n
return true
}
}
}
func (iter *Iterator) unreadByte() {
if iter.Error != nil {
return
}
iter.head--
return
}
// Read read the next JSON element as generic interface{}.
func (iter *Iterator) Read() interface{} {
valueType := iter.WhatIsNext()
switch valueType {
case StringValue:
return iter.ReadString()
case NumberValue:
if iter.cfg.configBeforeFrozen.UseNumber {
return json.Number(iter.readNumberAsString())
}
return iter.ReadFloat64()
case NilValue:
iter.skipFourBytes('n', 'u', 'l', 'l')
return nil
case BoolValue:
return iter.ReadBool()
case ArrayValue:
arr := []interface{}{}
iter.ReadArrayCB(func(iter *Iterator) bool {
var elem interface{}
iter.ReadVal(&elem)
arr = append(arr, elem)
return true
})
return arr
case ObjectValue:
obj := map[string]interface{}{}
iter.ReadMapCB(func(Iter *Iterator, field string) bool {
var elem interface{}
iter.ReadVal(&elem)
obj[field] = elem
return true
})
return obj
default:
iter.ReportError("Read", fmt.Sprintf("unexpected value type: %v", valueType))
return nil
}
}
// limit maximum depth of nesting, as allowed by https://tools.ietf.org/html/rfc7159#section-9
const maxDepth = 10000
func (iter *Iterator) incrementDepth() (success bool) {
iter.depth++
if iter.depth <= maxDepth {
return true
}
iter.ReportError("incrementDepth", "exceeded max depth")
return false
}
func (iter *Iterator) decrementDepth() (success bool) {
iter.depth--
if iter.depth >= 0 {
return true
}
iter.ReportError("decrementDepth", "unexpected negative nesting")
return false
}

64
iter_array.go Normal file
View File

@ -0,0 +1,64 @@
package jsoniter
// ReadArray read array element, tells if the array has more element to read.
func (iter *Iterator) ReadArray() (ret bool) {
c := iter.nextToken()
switch c {
case 'n':
iter.skipThreeBytes('u', 'l', 'l')
return false // null
case '[':
c = iter.nextToken()
if c != ']' {
iter.unreadByte()
return true
}
return false
case ']':
return false
case ',':
return true
default:
iter.ReportError("ReadArray", "expect [ or , or ] or n, but found "+string([]byte{c}))
return
}
}
// ReadArrayCB read array with callback
func (iter *Iterator) ReadArrayCB(callback func(*Iterator) bool) (ret bool) {
c := iter.nextToken()
if c == '[' {
if !iter.incrementDepth() {
return false
}
c = iter.nextToken()
if c != ']' {
iter.unreadByte()
if !callback(iter) {
iter.decrementDepth()
return false
}
c = iter.nextToken()
for c == ',' {
if !callback(iter) {
iter.decrementDepth()
return false
}
c = iter.nextToken()
}
if c != ']' {
iter.ReportError("ReadArrayCB", "expect ] in the end, but found "+string([]byte{c}))
iter.decrementDepth()
return false
}
return iter.decrementDepth()
}
return iter.decrementDepth()
}
if c == 'n' {
iter.skipThreeBytes('u', 'l', 'l')
return true // null
}
iter.ReportError("ReadArrayCB", "expect [ or n, but found "+string([]byte{c}))
return false
}

342
iter_float.go Normal file
View File

@ -0,0 +1,342 @@
package jsoniter
import (
"encoding/json"
"io"
"math/big"
"strconv"
"strings"
"unsafe"
)
var floatDigits []int8
const invalidCharForNumber = int8(-1)
const endOfNumber = int8(-2)
const dotInNumber = int8(-3)
func init() {
floatDigits = make([]int8, 256)
for i := 0; i < len(floatDigits); i++ {
floatDigits[i] = invalidCharForNumber
}
for i := int8('0'); i <= int8('9'); i++ {
floatDigits[i] = i - int8('0')
}
floatDigits[','] = endOfNumber
floatDigits[']'] = endOfNumber
floatDigits['}'] = endOfNumber
floatDigits[' '] = endOfNumber
floatDigits['\t'] = endOfNumber
floatDigits['\n'] = endOfNumber
floatDigits['.'] = dotInNumber
}
// ReadBigFloat read big.Float
func (iter *Iterator) ReadBigFloat() (ret *big.Float) {
str := iter.readNumberAsString()
if iter.Error != nil && iter.Error != io.EOF {
return nil
}
prec := 64
if len(str) > prec {
prec = len(str)
}
val, _, err := big.ParseFloat(str, 10, uint(prec), big.ToZero)
if err != nil {
iter.Error = err
return nil
}
return val
}
// ReadBigInt read big.Int
func (iter *Iterator) ReadBigInt() (ret *big.Int) {
str := iter.readNumberAsString()
if iter.Error != nil && iter.Error != io.EOF {
return nil
}
ret = big.NewInt(0)
var success bool
ret, success = ret.SetString(str, 10)
if !success {
iter.ReportError("ReadBigInt", "invalid big int")
return nil
}
return ret
}
//ReadFloat32 read float32
func (iter *Iterator) ReadFloat32() (ret float32) {
c := iter.nextToken()
if c == '-' {
return -iter.readPositiveFloat32()
}
iter.unreadByte()
return iter.readPositiveFloat32()
}
func (iter *Iterator) readPositiveFloat32() (ret float32) {
i := iter.head
// first char
if i == iter.tail {
return iter.readFloat32SlowPath()
}
c := iter.buf[i]
i++
ind := floatDigits[c]
switch ind {
case invalidCharForNumber:
return iter.readFloat32SlowPath()
case endOfNumber:
iter.ReportError("readFloat32", "empty number")
return
case dotInNumber:
iter.ReportError("readFloat32", "leading dot is invalid")
return
case 0:
if i == iter.tail {
return iter.readFloat32SlowPath()
}
c = iter.buf[i]
switch c {
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
iter.ReportError("readFloat32", "leading zero is invalid")
return
}
}
value := uint64(ind)
// chars before dot
non_decimal_loop:
for ; i < iter.tail; i++ {
c = iter.buf[i]
ind := floatDigits[c]
switch ind {
case invalidCharForNumber:
return iter.readFloat32SlowPath()
case endOfNumber:
iter.head = i
return float32(value)
case dotInNumber:
break non_decimal_loop
}
if value > uint64SafeToMultiple10 {
return iter.readFloat32SlowPath()
}
value = (value << 3) + (value << 1) + uint64(ind) // value = value * 10 + ind;
}
// chars after dot
if c == '.' {
i++
decimalPlaces := 0
if i == iter.tail {
return iter.readFloat32SlowPath()
}
for ; i < iter.tail; i++ {
c = iter.buf[i]
ind := floatDigits[c]
switch ind {
case endOfNumber:
if decimalPlaces > 0 && decimalPlaces < len(pow10) {
iter.head = i
return float32(float64(value) / float64(pow10[decimalPlaces]))
}
// too many decimal places
return iter.readFloat32SlowPath()
case invalidCharForNumber, dotInNumber:
return iter.readFloat32SlowPath()
}
decimalPlaces++
if value > uint64SafeToMultiple10 {
return iter.readFloat32SlowPath()
}
value = (value << 3) + (value << 1) + uint64(ind)
}
}
return iter.readFloat32SlowPath()
}
func (iter *Iterator) readNumberAsString() (ret string) {
strBuf := [16]byte{}
str := strBuf[0:0]
load_loop:
for {
for i := iter.head; i < iter.tail; i++ {
c := iter.buf[i]
switch c {
case '+', '-', '.', 'e', 'E', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
str = append(str, c)
continue
default:
iter.head = i
break load_loop
}
}
if !iter.loadMore() {
break
}
}
if iter.Error != nil && iter.Error != io.EOF {
return
}
if len(str) == 0 {
iter.ReportError("readNumberAsString", "invalid number")
}
return *(*string)(unsafe.Pointer(&str))
}
func (iter *Iterator) readFloat32SlowPath() (ret float32) {
str := iter.readNumberAsString()
if iter.Error != nil && iter.Error != io.EOF {
return
}
errMsg := validateFloat(str)
if errMsg != "" {
iter.ReportError("readFloat32SlowPath", errMsg)
return
}
val, err := strconv.ParseFloat(str, 32)
if err != nil {
iter.Error = err
return
}
return float32(val)
}
// ReadFloat64 read float64
func (iter *Iterator) ReadFloat64() (ret float64) {
c := iter.nextToken()
if c == '-' {
return -iter.readPositiveFloat64()
}
iter.unreadByte()
return iter.readPositiveFloat64()
}
func (iter *Iterator) readPositiveFloat64() (ret float64) {
i := iter.head
// first char
if i == iter.tail {
return iter.readFloat64SlowPath()
}
c := iter.buf[i]
i++
ind := floatDigits[c]
switch ind {
case invalidCharForNumber:
return iter.readFloat64SlowPath()
case endOfNumber:
iter.ReportError("readFloat64", "empty number")
return
case dotInNumber:
iter.ReportError("readFloat64", "leading dot is invalid")
return
case 0:
if i == iter.tail {
return iter.readFloat64SlowPath()
}
c = iter.buf[i]
switch c {
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
iter.ReportError("readFloat64", "leading zero is invalid")
return
}
}
value := uint64(ind)
// chars before dot
non_decimal_loop:
for ; i < iter.tail; i++ {
c = iter.buf[i]
ind := floatDigits[c]
switch ind {
case invalidCharForNumber:
return iter.readFloat64SlowPath()
case endOfNumber:
iter.head = i
return float64(value)
case dotInNumber:
break non_decimal_loop
}
if value > uint64SafeToMultiple10 {
return iter.readFloat64SlowPath()
}
value = (value << 3) + (value << 1) + uint64(ind) // value = value * 10 + ind;
}
// chars after dot
if c == '.' {
i++
decimalPlaces := 0
if i == iter.tail {
return iter.readFloat64SlowPath()
}
for ; i < iter.tail; i++ {
c = iter.buf[i]
ind := floatDigits[c]
switch ind {
case endOfNumber:
if decimalPlaces > 0 && decimalPlaces < len(pow10) {
iter.head = i
return float64(value) / float64(pow10[decimalPlaces])
}
// too many decimal places
return iter.readFloat64SlowPath()
case invalidCharForNumber, dotInNumber:
return iter.readFloat64SlowPath()
}
decimalPlaces++
if value > uint64SafeToMultiple10 {
return iter.readFloat64SlowPath()
}
value = (value << 3) + (value << 1) + uint64(ind)
if value > maxFloat64 {
return iter.readFloat64SlowPath()
}
}
}
return iter.readFloat64SlowPath()
}
func (iter *Iterator) readFloat64SlowPath() (ret float64) {
str := iter.readNumberAsString()
if iter.Error != nil && iter.Error != io.EOF {
return
}
errMsg := validateFloat(str)
if errMsg != "" {
iter.ReportError("readFloat64SlowPath", errMsg)
return
}
val, err := strconv.ParseFloat(str, 64)
if err != nil {
iter.Error = err
return
}
return val
}
func validateFloat(str string) string {
// strconv.ParseFloat is not validating `1.` or `1.e1`
if len(str) == 0 {
return "empty number"
}
if str[0] == '-' {
return "-- is not valid"
}
dotPos := strings.IndexByte(str, '.')
if dotPos != -1 {
if dotPos == len(str)-1 {
return "dot can not be last character"
}
switch str[dotPos+1] {
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
default:
return "missing digit after dot"
}
}
return ""
}
// ReadNumber read json.Number
func (iter *Iterator) ReadNumber() (ret json.Number) {
return json.Number(iter.readNumberAsString())
}

346
iter_int.go Normal file
View File

@ -0,0 +1,346 @@
package jsoniter
import (
"math"
"strconv"
)
var intDigits []int8
const uint32SafeToMultiply10 = uint32(0xffffffff)/10 - 1
const uint64SafeToMultiple10 = uint64(0xffffffffffffffff)/10 - 1
const maxFloat64 = 1<<53 - 1
func init() {
intDigits = make([]int8, 256)
for i := 0; i < len(intDigits); i++ {
intDigits[i] = invalidCharForNumber
}
for i := int8('0'); i <= int8('9'); i++ {
intDigits[i] = i - int8('0')
}
}
// ReadUint read uint
func (iter *Iterator) ReadUint() uint {
if strconv.IntSize == 32 {
return uint(iter.ReadUint32())
}
return uint(iter.ReadUint64())
}
// ReadInt read int
func (iter *Iterator) ReadInt() int {
if strconv.IntSize == 32 {
return int(iter.ReadInt32())
}
return int(iter.ReadInt64())
}
// ReadInt8 read int8
func (iter *Iterator) ReadInt8() (ret int8) {
c := iter.nextToken()
if c == '-' {
val := iter.readUint32(iter.readByte())
if val > math.MaxInt8+1 {
iter.ReportError("ReadInt8", "overflow: "+strconv.FormatInt(int64(val), 10))
return
}
return -int8(val)
}
val := iter.readUint32(c)
if val > math.MaxInt8 {
iter.ReportError("ReadInt8", "overflow: "+strconv.FormatInt(int64(val), 10))
return
}
return int8(val)
}
// ReadUint8 read uint8
func (iter *Iterator) ReadUint8() (ret uint8) {
val := iter.readUint32(iter.nextToken())
if val > math.MaxUint8 {
iter.ReportError("ReadUint8", "overflow: "+strconv.FormatInt(int64(val), 10))
return
}
return uint8(val)
}
// ReadInt16 read int16
func (iter *Iterator) ReadInt16() (ret int16) {
c := iter.nextToken()
if c == '-' {
val := iter.readUint32(iter.readByte())
if val > math.MaxInt16+1 {
iter.ReportError("ReadInt16", "overflow: "+strconv.FormatInt(int64(val), 10))
return
}
return -int16(val)
}
val := iter.readUint32(c)
if val > math.MaxInt16 {
iter.ReportError("ReadInt16", "overflow: "+strconv.FormatInt(int64(val), 10))
return
}
return int16(val)
}
// ReadUint16 read uint16
func (iter *Iterator) ReadUint16() (ret uint16) {
val := iter.readUint32(iter.nextToken())
if val > math.MaxUint16 {
iter.ReportError("ReadUint16", "overflow: "+strconv.FormatInt(int64(val), 10))
return
}
return uint16(val)
}
// ReadInt32 read int32
func (iter *Iterator) ReadInt32() (ret int32) {
c := iter.nextToken()
if c == '-' {
val := iter.readUint32(iter.readByte())
if val > math.MaxInt32+1 {
iter.ReportError("ReadInt32", "overflow: "+strconv.FormatInt(int64(val), 10))
return
}
return -int32(val)
}
val := iter.readUint32(c)
if val > math.MaxInt32 {
iter.ReportError("ReadInt32", "overflow: "+strconv.FormatInt(int64(val), 10))
return
}
return int32(val)
}
// ReadUint32 read uint32
func (iter *Iterator) ReadUint32() (ret uint32) {
return iter.readUint32(iter.nextToken())
}
func (iter *Iterator) readUint32(c byte) (ret uint32) {
ind := intDigits[c]
if ind == 0 {
iter.assertInteger()
return 0 // single zero
}
if ind == invalidCharForNumber {
iter.ReportError("readUint32", "unexpected character: "+string([]byte{byte(ind)}))
return
}
value := uint32(ind)
if iter.tail-iter.head > 10 {
i := iter.head
ind2 := intDigits[iter.buf[i]]
if ind2 == invalidCharForNumber {
iter.head = i
iter.assertInteger()
return value
}
i++
ind3 := intDigits[iter.buf[i]]
if ind3 == invalidCharForNumber {
iter.head = i
iter.assertInteger()
return value*10 + uint32(ind2)
}
//iter.head = i + 1
//value = value * 100 + uint32(ind2) * 10 + uint32(ind3)
i++
ind4 := intDigits[iter.buf[i]]
if ind4 == invalidCharForNumber {
iter.head = i
iter.assertInteger()
return value*100 + uint32(ind2)*10 + uint32(ind3)
}
i++
ind5 := intDigits[iter.buf[i]]
if ind5 == invalidCharForNumber {
iter.head = i
iter.assertInteger()
return value*1000 + uint32(ind2)*100 + uint32(ind3)*10 + uint32(ind4)
}
i++
ind6 := intDigits[iter.buf[i]]
if ind6 == invalidCharForNumber {
iter.head = i
iter.assertInteger()
return value*10000 + uint32(ind2)*1000 + uint32(ind3)*100 + uint32(ind4)*10 + uint32(ind5)
}
i++
ind7 := intDigits[iter.buf[i]]
if ind7 == invalidCharForNumber {
iter.head = i
iter.assertInteger()
return value*100000 + uint32(ind2)*10000 + uint32(ind3)*1000 + uint32(ind4)*100 + uint32(ind5)*10 + uint32(ind6)
}
i++
ind8 := intDigits[iter.buf[i]]
if ind8 == invalidCharForNumber {
iter.head = i
iter.assertInteger()
return value*1000000 + uint32(ind2)*100000 + uint32(ind3)*10000 + uint32(ind4)*1000 + uint32(ind5)*100 + uint32(ind6)*10 + uint32(ind7)
}
i++
ind9 := intDigits[iter.buf[i]]
value = value*10000000 + uint32(ind2)*1000000 + uint32(ind3)*100000 + uint32(ind4)*10000 + uint32(ind5)*1000 + uint32(ind6)*100 + uint32(ind7)*10 + uint32(ind8)
iter.head = i
if ind9 == invalidCharForNumber {
iter.assertInteger()
return value
}
}
for {
for i := iter.head; i < iter.tail; i++ {
ind = intDigits[iter.buf[i]]
if ind == invalidCharForNumber {
iter.head = i
iter.assertInteger()
return value
}
if value > uint32SafeToMultiply10 {
value2 := (value << 3) + (value << 1) + uint32(ind)
if value2 < value {
iter.ReportError("readUint32", "overflow")
return
}
value = value2
continue
}
value = (value << 3) + (value << 1) + uint32(ind)
}
if !iter.loadMore() {
iter.assertInteger()
return value
}
}
}
// ReadInt64 read int64
func (iter *Iterator) ReadInt64() (ret int64) {
c := iter.nextToken()
if c == '-' {
val := iter.readUint64(iter.readByte())
if val > math.MaxInt64+1 {
iter.ReportError("ReadInt64", "overflow: "+strconv.FormatUint(uint64(val), 10))
return
}
return -int64(val)
}
val := iter.readUint64(c)
if val > math.MaxInt64 {
iter.ReportError("ReadInt64", "overflow: "+strconv.FormatUint(uint64(val), 10))
return
}
return int64(val)
}
// ReadUint64 read uint64
func (iter *Iterator) ReadUint64() uint64 {
return iter.readUint64(iter.nextToken())
}
func (iter *Iterator) readUint64(c byte) (ret uint64) {
ind := intDigits[c]
if ind == 0 {
iter.assertInteger()
return 0 // single zero
}
if ind == invalidCharForNumber {
iter.ReportError("readUint64", "unexpected character: "+string([]byte{byte(ind)}))
return
}
value := uint64(ind)
if iter.tail-iter.head > 10 {
i := iter.head
ind2 := intDigits[iter.buf[i]]
if ind2 == invalidCharForNumber {
iter.head = i
iter.assertInteger()
return value
}
i++
ind3 := intDigits[iter.buf[i]]
if ind3 == invalidCharForNumber {
iter.head = i
iter.assertInteger()
return value*10 + uint64(ind2)
}
//iter.head = i + 1
//value = value * 100 + uint32(ind2) * 10 + uint32(ind3)
i++
ind4 := intDigits[iter.buf[i]]
if ind4 == invalidCharForNumber {
iter.head = i
iter.assertInteger()
return value*100 + uint64(ind2)*10 + uint64(ind3)
}
i++
ind5 := intDigits[iter.buf[i]]
if ind5 == invalidCharForNumber {
iter.head = i
iter.assertInteger()
return value*1000 + uint64(ind2)*100 + uint64(ind3)*10 + uint64(ind4)
}
i++
ind6 := intDigits[iter.buf[i]]
if ind6 == invalidCharForNumber {
iter.head = i
iter.assertInteger()
return value*10000 + uint64(ind2)*1000 + uint64(ind3)*100 + uint64(ind4)*10 + uint64(ind5)
}
i++
ind7 := intDigits[iter.buf[i]]
if ind7 == invalidCharForNumber {
iter.head = i
iter.assertInteger()
return value*100000 + uint64(ind2)*10000 + uint64(ind3)*1000 + uint64(ind4)*100 + uint64(ind5)*10 + uint64(ind6)
}
i++
ind8 := intDigits[iter.buf[i]]
if ind8 == invalidCharForNumber {
iter.head = i
iter.assertInteger()
return value*1000000 + uint64(ind2)*100000 + uint64(ind3)*10000 + uint64(ind4)*1000 + uint64(ind5)*100 + uint64(ind6)*10 + uint64(ind7)
}
i++
ind9 := intDigits[iter.buf[i]]
value = value*10000000 + uint64(ind2)*1000000 + uint64(ind3)*100000 + uint64(ind4)*10000 + uint64(ind5)*1000 + uint64(ind6)*100 + uint64(ind7)*10 + uint64(ind8)
iter.head = i
if ind9 == invalidCharForNumber {
iter.assertInteger()
return value
}
}
for {
for i := iter.head; i < iter.tail; i++ {
ind = intDigits[iter.buf[i]]
if ind == invalidCharForNumber {
iter.head = i
iter.assertInteger()
return value
}
if value > uint64SafeToMultiple10 {
value2 := (value << 3) + (value << 1) + uint64(ind)
if value2 < value {
iter.ReportError("readUint64", "overflow")
return
}
value = value2
continue
}
value = (value << 3) + (value << 1) + uint64(ind)
}
if !iter.loadMore() {
iter.assertInteger()
return value
}
}
}
func (iter *Iterator) assertInteger() {
if iter.head < iter.tail && iter.buf[iter.head] == '.' {
iter.ReportError("assertInteger", "can not decode float as int")
}
}

267
iter_object.go Normal file
View File

@ -0,0 +1,267 @@
package jsoniter
import (
"fmt"
"strings"
)
// ReadObject read one field from object.
// If object ended, returns empty string.
// Otherwise, returns the field name.
func (iter *Iterator) ReadObject() (ret string) {
c := iter.nextToken()
switch c {
case 'n':
iter.skipThreeBytes('u', 'l', 'l')
return "" // null
case '{':
c = iter.nextToken()
if c == '"' {
iter.unreadByte()
field := iter.ReadString()
c = iter.nextToken()
if c != ':' {
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
}
return field
}
if c == '}' {
return "" // end of object
}
iter.ReportError("ReadObject", `expect " after {, but found `+string([]byte{c}))
return
case ',':
field := iter.ReadString()
c = iter.nextToken()
if c != ':' {
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
}
return field
case '}':
return "" // end of object
default:
iter.ReportError("ReadObject", fmt.Sprintf(`expect { or , or } or n, but found %s`, string([]byte{c})))
return
}
}
// CaseInsensitive
func (iter *Iterator) readFieldHash() int64 {
hash := int64(0x811c9dc5)
c := iter.nextToken()
if c != '"' {
iter.ReportError("readFieldHash", `expect ", but found `+string([]byte{c}))
return 0
}
for {
for i := iter.head; i < iter.tail; i++ {
// require ascii string and no escape
b := iter.buf[i]
if b == '\\' {
iter.head = i
for _, b := range iter.readStringSlowPath() {
if 'A' <= b && b <= 'Z' && !iter.cfg.caseSensitive {
b += 'a' - 'A'
}
hash ^= int64(b)
hash *= 0x1000193
}
c = iter.nextToken()
if c != ':' {
iter.ReportError("readFieldHash", `expect :, but found `+string([]byte{c}))
return 0
}
return hash
}
if b == '"' {
iter.head = i + 1
c = iter.nextToken()
if c != ':' {
iter.ReportError("readFieldHash", `expect :, but found `+string([]byte{c}))
return 0
}
return hash
}
if 'A' <= b && b <= 'Z' && !iter.cfg.caseSensitive {
b += 'a' - 'A'
}
hash ^= int64(b)
hash *= 0x1000193
}
if !iter.loadMore() {
iter.ReportError("readFieldHash", `incomplete field name`)
return 0
}
}
}
func calcHash(str string, caseSensitive bool) int64 {
if !caseSensitive {
str = strings.ToLower(str)
}
hash := int64(0x811c9dc5)
for _, b := range []byte(str) {
hash ^= int64(b)
hash *= 0x1000193
}
return int64(hash)
}
// ReadObjectCB read object with callback, the key is ascii only and field name not copied
func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool {
c := iter.nextToken()
var field string
if c == '{' {
if !iter.incrementDepth() {
return false
}
c = iter.nextToken()
if c == '"' {
iter.unreadByte()
field = iter.ReadString()
c = iter.nextToken()
if c != ':' {
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
}
if !callback(iter, field) {
iter.decrementDepth()
return false
}
c = iter.nextToken()
for c == ',' {
field = iter.ReadString()
c = iter.nextToken()
if c != ':' {
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
}
if !callback(iter, field) {
iter.decrementDepth()
return false
}
c = iter.nextToken()
}
if c != '}' {
iter.ReportError("ReadObjectCB", `object not ended with }`)
iter.decrementDepth()
return false
}
return iter.decrementDepth()
}
if c == '}' {
return iter.decrementDepth()
}
iter.ReportError("ReadObjectCB", `expect " after {, but found `+string([]byte{c}))
iter.decrementDepth()
return false
}
if c == 'n' {
iter.skipThreeBytes('u', 'l', 'l')
return true // null
}
iter.ReportError("ReadObjectCB", `expect { or n, but found `+string([]byte{c}))
return false
}
// ReadMapCB read map with callback, the key can be any string
func (iter *Iterator) ReadMapCB(callback func(*Iterator, string) bool) bool {
c := iter.nextToken()
if c == '{' {
if !iter.incrementDepth() {
return false
}
c = iter.nextToken()
if c == '"' {
iter.unreadByte()
field := iter.ReadString()
if iter.nextToken() != ':' {
iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c}))
iter.decrementDepth()
return false
}
if !callback(iter, field) {
iter.decrementDepth()
return false
}
c = iter.nextToken()
for c == ',' {
field = iter.ReadString()
if iter.nextToken() != ':' {
iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c}))
iter.decrementDepth()
return false
}
if !callback(iter, field) {
iter.decrementDepth()
return false
}
c = iter.nextToken()
}
if c != '}' {
iter.ReportError("ReadMapCB", `object not ended with }`)
iter.decrementDepth()
return false
}
return iter.decrementDepth()
}
if c == '}' {
return iter.decrementDepth()
}
iter.ReportError("ReadMapCB", `expect " after {, but found `+string([]byte{c}))
iter.decrementDepth()
return false
}
if c == 'n' {
iter.skipThreeBytes('u', 'l', 'l')
return true // null
}
iter.ReportError("ReadMapCB", `expect { or n, but found `+string([]byte{c}))
return false
}
func (iter *Iterator) readObjectStart() bool {
c := iter.nextToken()
if c == '{' {
c = iter.nextToken()
if c == '}' {
return false
}
iter.unreadByte()
return true
} else if c == 'n' {
iter.skipThreeBytes('u', 'l', 'l')
return false
}
iter.ReportError("readObjectStart", "expect { or n, but found "+string([]byte{c}))
return false
}
func (iter *Iterator) readObjectFieldAsBytes() (ret []byte) {
str := iter.ReadStringAsSlice()
if iter.skipWhitespacesWithoutLoadMore() {
if ret == nil {
ret = make([]byte, len(str))
copy(ret, str)
}
if !iter.loadMore() {
return
}
}
if iter.buf[iter.head] != ':' {
iter.ReportError("readObjectFieldAsBytes", "expect : after object field, but found "+string([]byte{iter.buf[iter.head]}))
return
}
iter.head++
if iter.skipWhitespacesWithoutLoadMore() {
if ret == nil {
ret = make([]byte, len(str))
copy(ret, str)
}
if !iter.loadMore() {
return
}
}
if ret == nil {
return str
}
return ret
}

130
iter_skip.go Normal file
View File

@ -0,0 +1,130 @@
package jsoniter
import "fmt"
// ReadNil reads a json object as nil and
// returns whether it's a nil or not
func (iter *Iterator) ReadNil() (ret bool) {
c := iter.nextToken()
if c == 'n' {
iter.skipThreeBytes('u', 'l', 'l') // null
return true
}
iter.unreadByte()
return false
}
// ReadBool reads a json object as BoolValue
func (iter *Iterator) ReadBool() (ret bool) {
c := iter.nextToken()
if c == 't' {
iter.skipThreeBytes('r', 'u', 'e')
return true
}
if c == 'f' {
iter.skipFourBytes('a', 'l', 's', 'e')
return false
}
iter.ReportError("ReadBool", "expect t or f, but found "+string([]byte{c}))
return
}
// SkipAndReturnBytes skip next JSON element, and return its content as []byte.
// The []byte can be kept, it is a copy of data.
func (iter *Iterator) SkipAndReturnBytes() []byte {
iter.startCapture(iter.head)
iter.Skip()
return iter.stopCapture()
}
// SkipAndAppendBytes skips next JSON element and appends its content to
// buffer, returning the result.
func (iter *Iterator) SkipAndAppendBytes(buf []byte) []byte {
iter.startCaptureTo(buf, iter.head)
iter.Skip()
return iter.stopCapture()
}
func (iter *Iterator) startCaptureTo(buf []byte, captureStartedAt int) {
if iter.captured != nil {
panic("already in capture mode")
}
iter.captureStartedAt = captureStartedAt
iter.captured = buf
}
func (iter *Iterator) startCapture(captureStartedAt int) {
iter.startCaptureTo(make([]byte, 0, 32), captureStartedAt)
}
func (iter *Iterator) stopCapture() []byte {
if iter.captured == nil {
panic("not in capture mode")
}
captured := iter.captured
remaining := iter.buf[iter.captureStartedAt:iter.head]
iter.captureStartedAt = -1
iter.captured = nil
return append(captured, remaining...)
}
// Skip skips a json object and positions to relatively the next json object
func (iter *Iterator) Skip() {
c := iter.nextToken()
switch c {
case '"':
iter.skipString()
case 'n':
iter.skipThreeBytes('u', 'l', 'l') // null
case 't':
iter.skipThreeBytes('r', 'u', 'e') // true
case 'f':
iter.skipFourBytes('a', 'l', 's', 'e') // false
case '0':
iter.unreadByte()
iter.ReadFloat32()
case '-', '1', '2', '3', '4', '5', '6', '7', '8', '9':
iter.skipNumber()
case '[':
iter.skipArray()
case '{':
iter.skipObject()
default:
iter.ReportError("Skip", fmt.Sprintf("do not know how to skip: %v", c))
return
}
}
func (iter *Iterator) skipFourBytes(b1, b2, b3, b4 byte) {
if iter.readByte() != b1 {
iter.ReportError("skipFourBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3, b4})))
return
}
if iter.readByte() != b2 {
iter.ReportError("skipFourBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3, b4})))
return
}
if iter.readByte() != b3 {
iter.ReportError("skipFourBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3, b4})))
return
}
if iter.readByte() != b4 {
iter.ReportError("skipFourBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3, b4})))
return
}
}
func (iter *Iterator) skipThreeBytes(b1, b2, b3 byte) {
if iter.readByte() != b1 {
iter.ReportError("skipThreeBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3})))
return
}
if iter.readByte() != b2 {
iter.ReportError("skipThreeBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3})))
return
}
if iter.readByte() != b3 {
iter.ReportError("skipThreeBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3})))
return
}
}

163
iter_skip_sloppy.go Normal file
View File

@ -0,0 +1,163 @@
//+build jsoniter_sloppy
package jsoniter
// sloppy but faster implementation, do not validate the input json
func (iter *Iterator) skipNumber() {
for {
for i := iter.head; i < iter.tail; i++ {
c := iter.buf[i]
switch c {
case ' ', '\n', '\r', '\t', ',', '}', ']':
iter.head = i
return
}
}
if !iter.loadMore() {
return
}
}
}
func (iter *Iterator) skipArray() {
level := 1
if !iter.incrementDepth() {
return
}
for {
for i := iter.head; i < iter.tail; i++ {
switch iter.buf[i] {
case '"': // If inside string, skip it
iter.head = i + 1
iter.skipString()
i = iter.head - 1 // it will be i++ soon
case '[': // If open symbol, increase level
level++
if !iter.incrementDepth() {
return
}
case ']': // If close symbol, increase level
level--
if !iter.decrementDepth() {
return
}
// If we have returned to the original level, we're done
if level == 0 {
iter.head = i + 1
return
}
}
}
if !iter.loadMore() {
iter.ReportError("skipObject", "incomplete array")
return
}
}
}
func (iter *Iterator) skipObject() {
level := 1
if !iter.incrementDepth() {
return
}
for {
for i := iter.head; i < iter.tail; i++ {
switch iter.buf[i] {
case '"': // If inside string, skip it
iter.head = i + 1
iter.skipString()
i = iter.head - 1 // it will be i++ soon
case '{': // If open symbol, increase level
level++
if !iter.incrementDepth() {
return
}
case '}': // If close symbol, increase level
level--
if !iter.decrementDepth() {
return
}
// If we have returned to the original level, we're done
if level == 0 {
iter.head = i + 1
return
}
}
}
if !iter.loadMore() {
iter.ReportError("skipObject", "incomplete object")
return
}
}
}
func (iter *Iterator) skipString() {
for {
end, escaped := iter.findStringEnd()
if end == -1 {
if !iter.loadMore() {
iter.ReportError("skipString", "incomplete string")
return
}
if escaped {
iter.head = 1 // skip the first char as last char read is \
}
} else {
iter.head = end
return
}
}
}
// adapted from: https://github.com/buger/jsonparser/blob/master/parser.go
// Tries to find the end of string
// Support if string contains escaped quote symbols.
func (iter *Iterator) findStringEnd() (int, bool) {
escaped := false
for i := iter.head; i < iter.tail; i++ {
c := iter.buf[i]
if c == '"' {
if !escaped {
return i + 1, false
}
j := i - 1
for {
if j < iter.head || iter.buf[j] != '\\' {
// even number of backslashes
// either end of buffer, or " found
return i + 1, true
}
j--
if j < iter.head || iter.buf[j] != '\\' {
// odd number of backslashes
// it is \" or \\\"
break
}
j--
}
} else if c == '\\' {
escaped = true
}
}
j := iter.tail - 1
for {
if j < iter.head || iter.buf[j] != '\\' {
// even number of backslashes
// either end of buffer, or " found
return -1, false // do not end with \
}
j--
if j < iter.head || iter.buf[j] != '\\' {
// odd number of backslashes
// it is \" or \\\"
break
}
j--
}
return -1, true // end with \
}

View File

@ -1,61 +1,64 @@
//+build jsoniter_sloppy
package jsoniter package jsoniter
import ( import (
"testing" "github.com/stretchr/testify/require"
"io" "io"
"testing"
) )
func Test_string_end(t *testing.T) { func Test_string_end(t *testing.T) {
end, escaped := ParseString(`abc"`).findStringEnd() end, escaped := ParseString(ConfigDefault, `abc"`).findStringEnd()
if end != 4 { if end != 4 {
t.Fatal(end) t.Fatal(end)
} }
if escaped != false { if escaped != false {
t.Fatal(escaped) t.Fatal(escaped)
} }
end, escaped = ParseString(`abc\\"`).findStringEnd() end, escaped = ParseString(ConfigDefault, `abc\\"`).findStringEnd()
if end != 6 { if end != 6 {
t.Fatal(end) t.Fatal(end)
} }
if escaped != true { if escaped != true {
t.Fatal(escaped) t.Fatal(escaped)
} }
end, escaped = ParseString(`abc\\\\"`).findStringEnd() end, escaped = ParseString(ConfigDefault, `abc\\\\"`).findStringEnd()
if end != 8 { if end != 8 {
t.Fatal(end) t.Fatal(end)
} }
if escaped != true { if escaped != true {
t.Fatal(escaped) t.Fatal(escaped)
} }
end, escaped = ParseString(`abc\"`).findStringEnd() end, escaped = ParseString(ConfigDefault, `abc\"`).findStringEnd()
if end != -1 { if end != -1 {
t.Fatal(end) t.Fatal(end)
} }
if escaped != false { if escaped != false {
t.Fatal(escaped) t.Fatal(escaped)
} }
end, escaped = ParseString(`abc\`).findStringEnd() end, escaped = ParseString(ConfigDefault, `abc\`).findStringEnd()
if end != -1 { if end != -1 {
t.Fatal(end) t.Fatal(end)
} }
if escaped != true { if escaped != true {
t.Fatal(escaped) t.Fatal(escaped)
} }
end, escaped = ParseString(`abc\\`).findStringEnd() end, escaped = ParseString(ConfigDefault, `abc\\`).findStringEnd()
if end != -1 { if end != -1 {
t.Fatal(end) t.Fatal(end)
} }
if escaped != false { if escaped != false {
t.Fatal(escaped) t.Fatal(escaped)
} }
end, escaped = ParseString(`\\`).findStringEnd() end, escaped = ParseString(ConfigDefault, `\\`).findStringEnd()
if end != -1 { if end != -1 {
t.Fatal(end) t.Fatal(end)
} }
if escaped != false { if escaped != false {
t.Fatal(escaped) t.Fatal(escaped)
} }
end, escaped = ParseString(`\`).findStringEnd() end, escaped = ParseString(ConfigDefault, `\`).findStringEnd()
if end != -1 { if end != -1 {
t.Fatal(end) t.Fatal(end)
} }
@ -89,66 +92,55 @@ func (reader *StagedReader) Read(p []byte) (n int, err error) {
} }
func Test_skip_string(t *testing.T) { func Test_skip_string(t *testing.T) {
iter := ParseString(`"abc`) should := require.New(t)
iter := ParseString(ConfigDefault, `"abc`)
iter.skipString() iter.skipString()
if iter.head != 1 { should.Equal(1, iter.head)
t.Fatal(iter.head) iter = ParseString(ConfigDefault, `\""abc`)
}
iter = ParseString(`\""abc`)
iter.skipString() iter.skipString()
if iter.head != 3 { should.Equal(3, iter.head)
t.Fatal(iter.head)
}
reader := &StagedReader{ reader := &StagedReader{
r1: `abc`, r1: `abc`,
r2: `"`, r2: `"`,
} }
iter = Parse(reader, 4096) iter = Parse(ConfigDefault, reader, 4096)
iter.skipString() iter.skipString()
if iter.head != 1 { should.Equal(1, iter.head)
t.Fatal(iter.head)
}
reader = &StagedReader{ reader = &StagedReader{
r1: `abc`, r1: `abc`,
r2: `1"`, r2: `1"`,
} }
iter = Parse(reader, 4096) iter = Parse(ConfigDefault, reader, 4096)
iter.skipString() iter.skipString()
if iter.head != 2 { should.Equal(2, iter.head)
t.Fatal(iter.head)
}
reader = &StagedReader{ reader = &StagedReader{
r1: `abc\`, r1: `abc\`,
r2: `"`, r2: `"`,
} }
iter = Parse(reader, 4096) iter = Parse(ConfigDefault, reader, 4096)
iter.skipString() iter.skipString()
if iter.Error != io.EOF { should.NotNil(iter.Error)
t.Fatal(iter.Error)
}
reader = &StagedReader{ reader = &StagedReader{
r1: `abc\`, r1: `abc\`,
r2: `""`, r2: `""`,
} }
iter = Parse(reader, 4096) iter = Parse(ConfigDefault, reader, 4096)
iter.skipString() iter.skipString()
if iter.head != 2 { should.Equal(2, iter.head)
t.Fatal(iter.head)
}
} }
func Test_skip_object(t *testing.T) { func Test_skip_object(t *testing.T) {
iter := ParseString(`}`) iter := ParseString(ConfigDefault, `}`)
iter.skipObject() iter.skipObject()
if iter.head != 1 { if iter.head != 1 {
t.Fatal(iter.head) t.Fatal(iter.head)
} }
iter = ParseString(`a}`) iter = ParseString(ConfigDefault, `a}`)
iter.skipObject() iter.skipObject()
if iter.head != 2 { if iter.head != 2 {
t.Fatal(iter.head) t.Fatal(iter.head)
} }
iter = ParseString(`{}}a`) iter = ParseString(ConfigDefault, `{}}a`)
iter.skipObject() iter.skipObject()
if iter.head != 3 { if iter.head != 3 {
t.Fatal(iter.head) t.Fatal(iter.head)
@ -157,12 +149,12 @@ func Test_skip_object(t *testing.T) {
r1: `{`, r1: `{`,
r2: `}}a`, r2: `}}a`,
} }
iter = Parse(reader, 4096) iter = Parse(ConfigDefault, reader, 4096)
iter.skipObject() iter.skipObject()
if iter.head != 2 { if iter.head != 2 {
t.Fatal(iter.head) t.Fatal(iter.head)
} }
iter = ParseString(`"}"}a`) iter = ParseString(ConfigDefault, `"}"}a`)
iter.skipObject() iter.skipObject()
if iter.head != 4 { if iter.head != 4 {
t.Fatal(iter.head) t.Fatal(iter.head)

99
iter_skip_strict.go Normal file
View File

@ -0,0 +1,99 @@
//+build !jsoniter_sloppy
package jsoniter
import (
"fmt"
"io"
)
func (iter *Iterator) skipNumber() {
if !iter.trySkipNumber() {
iter.unreadByte()
if iter.Error != nil && iter.Error != io.EOF {
return
}
iter.ReadFloat64()
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = nil
iter.ReadBigFloat()
}
}
}
func (iter *Iterator) trySkipNumber() bool {
dotFound := false
for i := iter.head; i < iter.tail; i++ {
c := iter.buf[i]
switch c {
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
case '.':
if dotFound {
iter.ReportError("validateNumber", `more than one dot found in number`)
return true // already failed
}
if i+1 == iter.tail {
return false
}
c = iter.buf[i+1]
switch c {
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
default:
iter.ReportError("validateNumber", `missing digit after dot`)
return true // already failed
}
dotFound = true
default:
switch c {
case ',', ']', '}', ' ', '\t', '\n', '\r':
if iter.head == i {
return false // if - without following digits
}
iter.head = i
return true // must be valid
}
return false // may be invalid
}
}
return false
}
func (iter *Iterator) skipString() {
if !iter.trySkipString() {
iter.unreadByte()
iter.ReadString()
}
}
func (iter *Iterator) trySkipString() bool {
for i := iter.head; i < iter.tail; i++ {
c := iter.buf[i]
if c == '"' {
iter.head = i + 1
return true // valid
} else if c == '\\' {
return false
} else if c < ' ' {
iter.ReportError("trySkipString",
fmt.Sprintf(`invalid control character found: %d`, c))
return true // already failed
}
}
return false
}
func (iter *Iterator) skipObject() {
iter.unreadByte()
iter.ReadObjectCB(func(iter *Iterator, field string) bool {
iter.Skip()
return true
})
}
func (iter *Iterator) skipArray() {
iter.unreadByte()
iter.ReadArrayCB(func(iter *Iterator) bool {
iter.Skip()
return true
})
}

215
iter_str.go Normal file
View File

@ -0,0 +1,215 @@
package jsoniter
import (
"fmt"
"unicode/utf16"
)
// ReadString read string from iterator
func (iter *Iterator) ReadString() (ret string) {
c := iter.nextToken()
if c == '"' {
for i := iter.head; i < iter.tail; i++ {
c := iter.buf[i]
if c == '"' {
ret = string(iter.buf[iter.head:i])
iter.head = i + 1
return ret
} else if c == '\\' {
break
} else if c < ' ' {
iter.ReportError("ReadString",
fmt.Sprintf(`invalid control character found: %d`, c))
return
}
}
return iter.readStringSlowPath()
} else if c == 'n' {
iter.skipThreeBytes('u', 'l', 'l')
return ""
}
iter.ReportError("ReadString", `expects " or n, but found `+string([]byte{c}))
return
}
func (iter *Iterator) readStringSlowPath() (ret string) {
var str []byte
var c byte
for iter.Error == nil {
c = iter.readByte()
if c == '"' {
return string(str)
}
if c == '\\' {
c = iter.readByte()
str = iter.readEscapedChar(c, str)
} else {
str = append(str, c)
}
}
iter.ReportError("readStringSlowPath", "unexpected end of input")
return
}
func (iter *Iterator) readEscapedChar(c byte, str []byte) []byte {
switch c {
case 'u':
r := iter.readU4()
if utf16.IsSurrogate(r) {
c = iter.readByte()
if iter.Error != nil {
return nil
}
if c != '\\' {
iter.unreadByte()
str = appendRune(str, r)
return str
}
c = iter.readByte()
if iter.Error != nil {
return nil
}
if c != 'u' {
str = appendRune(str, r)
return iter.readEscapedChar(c, str)
}
r2 := iter.readU4()
if iter.Error != nil {
return nil
}
combined := utf16.DecodeRune(r, r2)
if combined == '\uFFFD' {
str = appendRune(str, r)
str = appendRune(str, r2)
} else {
str = appendRune(str, combined)
}
} else {
str = appendRune(str, r)
}
case '"':
str = append(str, '"')
case '\\':
str = append(str, '\\')
case '/':
str = append(str, '/')
case 'b':
str = append(str, '\b')
case 'f':
str = append(str, '\f')
case 'n':
str = append(str, '\n')
case 'r':
str = append(str, '\r')
case 't':
str = append(str, '\t')
default:
iter.ReportError("readEscapedChar",
`invalid escape char after \`)
return nil
}
return str
}
// ReadStringAsSlice read string from iterator without copying into string form.
// The []byte can not be kept, as it will change after next iterator call.
func (iter *Iterator) ReadStringAsSlice() (ret []byte) {
c := iter.nextToken()
if c == '"' {
for i := iter.head; i < iter.tail; i++ {
// require ascii string and no escape
// for: field name, base64, number
if iter.buf[i] == '"' {
// fast path: reuse the underlying buffer
ret = iter.buf[iter.head:i]
iter.head = i + 1
return ret
}
}
readLen := iter.tail - iter.head
copied := make([]byte, readLen, readLen*2)
copy(copied, iter.buf[iter.head:iter.tail])
iter.head = iter.tail
for iter.Error == nil {
c := iter.readByte()
if c == '"' {
return copied
}
copied = append(copied, c)
}
return copied
}
iter.ReportError("ReadStringAsSlice", `expects " or n, but found `+string([]byte{c}))
return
}
func (iter *Iterator) readU4() (ret rune) {
for i := 0; i < 4; i++ {
c := iter.readByte()
if iter.Error != nil {
return
}
if c >= '0' && c <= '9' {
ret = ret*16 + rune(c-'0')
} else if c >= 'a' && c <= 'f' {
ret = ret*16 + rune(c-'a'+10)
} else if c >= 'A' && c <= 'F' {
ret = ret*16 + rune(c-'A'+10)
} else {
iter.ReportError("readU4", "expects 0~9 or a~f, but found "+string([]byte{c}))
return
}
}
return ret
}
const (
t1 = 0x00 // 0000 0000
tx = 0x80 // 1000 0000
t2 = 0xC0 // 1100 0000
t3 = 0xE0 // 1110 0000
t4 = 0xF0 // 1111 0000
t5 = 0xF8 // 1111 1000
maskx = 0x3F // 0011 1111
mask2 = 0x1F // 0001 1111
mask3 = 0x0F // 0000 1111
mask4 = 0x07 // 0000 0111
rune1Max = 1<<7 - 1
rune2Max = 1<<11 - 1
rune3Max = 1<<16 - 1
surrogateMin = 0xD800
surrogateMax = 0xDFFF
maxRune = '\U0010FFFF' // Maximum valid Unicode code point.
runeError = '\uFFFD' // the "error" Rune or "Unicode replacement character"
)
func appendRune(p []byte, r rune) []byte {
// Negative values are erroneous. Making it unsigned addresses the problem.
switch i := uint32(r); {
case i <= rune1Max:
p = append(p, byte(r))
return p
case i <= rune2Max:
p = append(p, t2|byte(r>>6))
p = append(p, tx|byte(r)&maskx)
return p
case i > maxRune, surrogateMin <= i && i <= surrogateMax:
r = runeError
fallthrough
case i <= rune3Max:
p = append(p, t3|byte(r>>12))
p = append(p, tx|byte(r>>6)&maskx)
p = append(p, tx|byte(r)&maskx)
return p
default:
p = append(p, t4|byte(r>>18))
p = append(p, tx|byte(r>>12)&maskx)
p = append(p, tx|byte(r>>6)&maskx)
p = append(p, tx|byte(r)&maskx)
return p
}
}

View File

@ -1,916 +1,18 @@
// Package jsoniter implements encoding and decoding of JSON as defined in
// RFC 4627 and provides interfaces with identical syntax of standard lib encoding/json.
// Converting from encoding/json to jsoniter is no more than replacing the package with jsoniter
// and variable type declarations (if any).
// jsoniter interfaces gives 100% compatibility with code using standard lib.
//
// "JSON and Go"
// (https://golang.org/doc/articles/json_and_go.html)
// gives a description of how Marshal/Unmarshal operate
// between arbitrary or predefined json objects and bytes,
// and it applies to jsoniter.Marshal/Unmarshal as well.
//
// Besides, jsoniter.Iterator provides a different set of interfaces
// iterating given bytes/string/reader
// and yielding parsed elements one by one.
// This set of interfaces reads input as required and gives
// better performance.
package jsoniter package jsoniter
import (
"io"
"fmt"
"unicode/utf16"
"strconv"
"unsafe"
"encoding/base64"
)
type ValueType int
const (
Invalid ValueType = iota
String
Number
Null
Bool
Array
Object
)
var digits []byte
var valueTypes []ValueType
func init() {
digits = make([]byte, 256)
for i := 0; i < len(digits); i++ {
digits[i] = 255
}
for i := '0'; i <= '9'; i++ {
digits[i] = byte(i - '0');
}
for i := 'a'; i <= 'f'; i++ {
digits[i] = byte((i - 'a') + 10);
}
for i := 'A'; i <= 'F'; i++ {
digits[i] = byte((i - 'A') + 10);
}
valueTypes = make([]ValueType, 256)
for i := 0; i < len(valueTypes); i++ {
valueTypes[i] = Invalid
}
valueTypes['"'] = String;
valueTypes['-'] = Number;
valueTypes['0'] = Number;
valueTypes['1'] = Number;
valueTypes['2'] = Number;
valueTypes['3'] = Number;
valueTypes['4'] = Number;
valueTypes['5'] = Number;
valueTypes['6'] = Number;
valueTypes['7'] = Number;
valueTypes['8'] = Number;
valueTypes['9'] = Number;
valueTypes['t'] = Bool;
valueTypes['f'] = Bool;
valueTypes['n'] = Null;
valueTypes['['] = Array;
valueTypes['{'] = Object;
}
type Iterator struct {
reader io.Reader
buf []byte
head int
tail int
Error error
}
func Parse(reader io.Reader, bufSize int) *Iterator {
iter := &Iterator{
reader: reader,
buf: make([]byte, bufSize),
head: 0,
tail: 0,
}
iter.skipWhitespaces()
return iter
}
func ParseBytes(input []byte) *Iterator {
iter := &Iterator{
reader: nil,
buf: input,
head: 0,
tail: len(input),
}
iter.skipWhitespaces()
return iter
}
func ParseString(input string) *Iterator {
return ParseBytes([]byte(input))
}
func (iter *Iterator) Reset(reader io.Reader) *Iterator {
iter.reader = reader
iter.head = 0
iter.tail = 0
iter.skipWhitespaces()
return iter
}
func (iter *Iterator) ResetBytes(input []byte) *Iterator {
// only for benchmarking
iter.reader = nil
iter.Error = nil
iter.buf = input
iter.head = 0
iter.tail = len(input)
iter.skipWhitespaces()
return iter
}
func (iter *Iterator) WhatIsNext() ValueType {
valueType := valueTypes[iter.readByte()];
iter.unreadByte();
return valueType;
}
func (iter *Iterator) skipWhitespaces() {
for {
for i := iter.head; i < iter.tail; i++ {
c := iter.buf[i]
switch c {
case ' ', '\n', '\t', '\r':
continue
}
iter.head = i
return
}
if !iter.loadMore() {
return
}
}
}
func (iter *Iterator) skipWhitespacesWithoutLoadMore() bool {
for i := iter.head; i < iter.tail; i++ {
c := iter.buf[i]
switch c {
case ' ', '\n', '\t', '\r':
continue
}
iter.head = i
return false
}
return true
}
func (iter *Iterator) nextToken() byte {
// a variation of skip whitespaces, returning the next non-whitespace token
for {
for i := iter.head; i < iter.tail; i++ {
c := iter.buf[i]
switch c {
case ' ', '\n', '\t', 'r':
continue
}
iter.head = i+1
return c
}
if !iter.loadMore() {
return 0
}
}
}
func (iter *Iterator) ReportError(operation string, msg string) {
if iter.Error != nil {
return
}
peekStart := iter.head - 10
if peekStart < 0 {
peekStart = 0
}
iter.Error = fmt.Errorf("%s: %s, parsing %v ...%s... at %s", operation, msg, iter.head,
string(iter.buf[peekStart: iter.head]), string(iter.buf[0:iter.tail]))
}
func (iter *Iterator) CurrentBuffer() string {
peekStart := iter.head - 10
if peekStart < 0 {
peekStart = 0
}
return fmt.Sprintf("parsing %v ...|%s|... at %s", iter.head,
string(iter.buf[peekStart: iter.head]), string(iter.buf[0:iter.tail]))
}
func (iter *Iterator) readByte() (ret byte) {
if iter.head == iter.tail {
if iter.loadMore() {
ret = iter.buf[iter.head]
iter.head++
return ret
} else {
return 0
}
}
ret = iter.buf[iter.head]
iter.head++
return ret
}
func (iter *Iterator) loadMore() bool {
if iter.reader == nil {
iter.Error = io.EOF
return false
}
for {
n, err := iter.reader.Read(iter.buf)
if n == 0 {
if err != nil {
iter.Error = err
return false
} else {
// n == 0, err == nil is not EOF
continue
}
} else {
iter.head = 0
iter.tail = n
return true
}
}
}
func (iter *Iterator) unreadByte() {
if iter.head == 0 {
iter.ReportError("unreadByte", "unread too many bytes")
return
}
iter.head -= 1
return
}
const maxUint64 = (1 << 64 - 1)
const cutoffUint64 = maxUint64 / 10 + 1
const maxUint32 = (1 << 32 - 1)
const cutoffUint32 = maxUint32 / 10 + 1
func (iter *Iterator) ReadUint() (ret uint) {
val := iter.ReadUint64()
converted := uint(val)
if uint64(converted) != val {
iter.ReportError("ReadUint", "int overflow")
return
}
return converted
}
func (iter *Iterator) ReadUint8() (ret uint8) {
val := iter.ReadUint64()
converted := uint8(val)
if uint64(converted) != val {
iter.ReportError("ReadUint8", "int overflow")
return
}
return converted
}
func (iter *Iterator) ReadUint16() (ret uint16) {
val := iter.ReadUint64()
converted := uint16(val)
if uint64(converted) != val {
iter.ReportError("ReadUint16", "int overflow")
return
}
return converted
}
func (iter *Iterator) ReadUint32() (ret uint32) {
val := iter.ReadUint64()
converted := uint32(val)
if uint64(converted) != val {
iter.ReportError("ReadUint32", "int overflow")
return
}
return converted
}
func (iter *Iterator) ReadUint64() (ret uint64) {
c := iter.readByte()
v := digits[c]
if v == 0 {
return 0 // single zero
}
if v == 255 {
iter.ReportError("ReadUint64", "unexpected character")
return
}
for {
if ret >= cutoffUint64 {
iter.ReportError("ReadUint64", "overflow")
return
}
ret = ret * 10 + uint64(v)
c = iter.readByte()
v = digits[c]
if v == 255 {
iter.unreadByte()
break
}
}
return ret
}
func (iter *Iterator) ReadInt() (ret int) {
val := iter.ReadInt64()
converted := int(val)
if int64(converted) != val {
iter.ReportError("ReadInt", "int overflow")
return
}
return converted
}
func (iter *Iterator) ReadInt8() (ret int8) {
val := iter.ReadInt64()
converted := int8(val)
if int64(converted) != val {
iter.ReportError("ReadInt8", "int overflow")
return
}
return converted
}
func (iter *Iterator) ReadInt16() (ret int16) {
val := iter.ReadInt64()
converted := int16(val)
if int64(converted) != val {
iter.ReportError("ReadInt16", "int overflow")
return
}
return converted
}
func (iter *Iterator) ReadInt32() (ret int32) {
val := iter.ReadInt64()
converted := int32(val)
if int64(converted) != val {
iter.ReportError("ReadInt32", "int overflow")
return
}
return converted
}
func (iter *Iterator) ReadInt64() (ret int64) {
c := iter.readByte()
if iter.Error != nil {
return
}
/* optional leading minus */
if c == '-' {
n := iter.ReadUint64()
return -int64(n)
} else {
iter.unreadByte()
n := iter.ReadUint64()
return int64(n)
}
}
func (iter *Iterator) ReadString() (ret string) {
return string(iter.readStringAsBytes())
}
func (iter *Iterator) readStringAsBytes() (ret []byte) {
c := iter.readByte()
if c == 'n' {
iter.skipUntilBreak()
return
}
if c != '"' {
iter.ReportError("ReadString", `expects " or n`)
return
}
end := iter.findStringEndWithoutEscape()
if end != -1 {
// fast path: reuse the underlying buffer
ret = iter.buf[iter.head:end-1]
iter.head = end
return ret
}
str := make([]byte, 0, 8)
for iter.Error == nil {
c = iter.readByte()
if c == '"' {
return str
}
if c == '\\' {
c = iter.readByte()
if iter.Error != nil {
return
}
switch c {
case 'u':
r := iter.readU4()
if iter.Error != nil {
return
}
if utf16.IsSurrogate(r) {
c = iter.readByte()
if iter.Error != nil {
return
}
if c != '\\' {
iter.ReportError("ReadString",
`expects \u after utf16 surrogate, but \ not found`)
return
}
c = iter.readByte()
if iter.Error != nil {
return
}
if c != 'u' {
iter.ReportError("ReadString",
`expects \u after utf16 surrogate, but \u not found`)
return
}
r2 := iter.readU4()
if iter.Error != nil {
return
}
combined := utf16.DecodeRune(r, r2)
str = appendRune(str, combined)
} else {
str = appendRune(str, r)
}
case '"':
str = append(str, '"')
case '\\':
str = append(str, '\\')
case '/':
str = append(str, '/')
case 'b':
str = append(str, '\b')
case 'f':
str = append(str, '\f')
case 'n':
str = append(str, '\n')
case 'r':
str = append(str, '\r')
case 't':
str = append(str, '\t')
default:
iter.ReportError("ReadString",
`invalid escape char after \`)
return
}
} else {
str = append(str, c)
}
}
return
}
func (iter *Iterator) readU4() (ret rune) {
for i := 0; i < 4; i++ {
c := iter.readByte()
if iter.Error != nil {
return
}
if (c >= '0' && c <= '9') {
if ret >= cutoffUint32 {
iter.ReportError("readU4", "overflow")
return
}
ret = ret * 16 + rune(c - '0')
} else if ((c >= 'a' && c <= 'f') ) {
if ret >= cutoffUint32 {
iter.ReportError("readU4", "overflow")
return
}
ret = ret * 16 + rune(c - 'a' + 10)
} else {
iter.ReportError("readU4", "expects 0~9 or a~f")
return
}
}
return ret
}
const (
t1 = 0x00 // 0000 0000
tx = 0x80 // 1000 0000
t2 = 0xC0 // 1100 0000
t3 = 0xE0 // 1110 0000
t4 = 0xF0 // 1111 0000
t5 = 0xF8 // 1111 1000
maskx = 0x3F // 0011 1111
mask2 = 0x1F // 0001 1111
mask3 = 0x0F // 0000 1111
mask4 = 0x07 // 0000 0111
rune1Max = 1 << 7 - 1
rune2Max = 1 << 11 - 1
rune3Max = 1 << 16 - 1
surrogateMin = 0xD800
surrogateMax = 0xDFFF
MaxRune = '\U0010FFFF' // Maximum valid Unicode code point.
RuneError = '\uFFFD' // the "error" Rune or "Unicode replacement character"
)
func appendRune(p []byte, r rune) []byte {
// Negative values are erroneous. Making it unsigned addresses the problem.
switch i := uint32(r); {
case i <= rune1Max:
p = append(p, byte(r))
return p
case i <= rune2Max:
p = append(p, t2 | byte(r >> 6))
p = append(p, tx | byte(r) & maskx)
return p
case i > MaxRune, surrogateMin <= i && i <= surrogateMax:
r = RuneError
fallthrough
case i <= rune3Max:
p = append(p, t3 | byte(r >> 12))
p = append(p, tx | byte(r >> 6) & maskx)
p = append(p, tx | byte(r) & maskx)
return p
default:
p = append(p, t4 | byte(r >> 18))
p = append(p, tx | byte(r >> 12) & maskx)
p = append(p, tx | byte(r >> 6) & maskx)
p = append(p, tx | byte(r) & maskx)
return p
}
}
func (iter *Iterator) ReadArray() (ret bool) {
c := iter.nextToken()
if iter.Error != nil {
return
}
switch c {
case 'n': {
iter.skipUntilBreak()
return false // null
}
case '[': {
c = iter.nextToken()
if iter.Error != nil {
return
}
if c == ']' {
return false
} else {
iter.unreadByte()
return true
}
}
case ']': return false
case ',':
iter.skipWhitespaces()
return true
default:
iter.ReportError("ReadArray", "expect [ or , or ] or n")
return
}
}
func (iter *Iterator) ReadObject() (ret string) {
c := iter.nextToken()
if iter.Error != nil {
return
}
switch c {
case 'n': {
iter.skipUntilBreak()
if iter.Error != nil {
return
}
return "" // null
}
case '{': {
c = iter.nextToken()
if iter.Error != nil {
return
}
switch c {
case '}':
return "" // end of object
case '"':
iter.unreadByte()
return iter.readObjectField()
default:
iter.ReportError("ReadObject", `expect " after {`)
return
}
}
case ',':
iter.skipWhitespaces()
return iter.readObjectField()
case '}':
return "" // end of object
default:
iter.ReportError("ReadObject", `expect { or , or } or n`)
return
}
}
func (iter *Iterator) readObjectField() (ret string) {
str := iter.readStringAsBytes()
if iter.skipWhitespacesWithoutLoadMore() {
if ret == "" {
ret = string(str);
}
if !iter.loadMore() {
return
}
}
if iter.buf[iter.head] != ':' {
iter.ReportError("ReadObject", "expect : after object field")
return
}
iter.head++
if iter.skipWhitespacesWithoutLoadMore() {
if ret == "" {
ret = string(str);
}
if !iter.loadMore() {
return
}
}
if ret == "" {
return *(*string)(unsafe.Pointer(&str))
}
return ret
}
func (iter *Iterator) ReadFloat32() (ret float32) {
strBuf := [8]byte{}
str := strBuf[0:0]
hasMore := true
for(hasMore) {
for i := iter.head; i < iter.tail; i++ {
c := iter.buf[i]
switch c {
case '-', '+', '.', 'e', 'E', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
str = append(str, c)
continue
default:
hasMore = false
break
}
}
if hasMore {
if !iter.loadMore() {
break
}
}
}
if iter.Error != nil && iter.Error != io.EOF {
return
}
val, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&str)), 32)
if err != nil {
iter.Error = err
return
}
return float32(val)
}
func (iter *Iterator) ReadFloat64() (ret float64) {
strBuf := [8]byte{}
str := strBuf[0:0]
hasMore := true
for(hasMore) {
for i := iter.head; i < iter.tail; i++ {
c := iter.buf[i]
switch c {
case '-', '+', '.', 'e', 'E', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
str = append(str, c)
continue
default:
hasMore = false
break
}
}
if hasMore {
if !iter.loadMore() {
break
}
}
}
if iter.Error != nil && iter.Error != io.EOF {
return
}
val, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&str)), 64)
if err != nil {
iter.Error = err
return
}
return val
}
func (iter *Iterator) ReadBool() (ret bool) {
c := iter.readByte()
if iter.Error != nil {
return
}
switch c {
case 't':
iter.skipUntilBreak()
return true
case 'f':
iter.skipUntilBreak()
return false
default:
iter.ReportError("ReadBool", "expect t or f")
return
}
}
func (iter *Iterator) ReadBase64() (ret []byte) {
src := iter.readStringAsBytes()
if iter.Error != nil {
return
}
b64 := base64.StdEncoding
ret = make([]byte, b64.DecodedLen(len(src)))
n, err := b64.Decode(ret, src)
if err != nil {
iter.Error = err
return
}
return ret[:n]
}
func (iter *Iterator) ReadNull() (ret bool) {
c := iter.readByte()
if c == 'n' {
iter.skipUntilBreak()
return true
}
iter.unreadByte()
return false
}
func (iter *Iterator) Skip() {
c := iter.readByte()
switch c {
case '"':
iter.skipString()
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 't', 'f', 'n':
iter.skipUntilBreak()
case '[':
iter.skipArray()
case '{':
iter.skipObject()
default:
iter.ReportError("Skip", fmt.Sprintf("do not know how to skip: %v", c))
return
}
}
func (iter *Iterator) skipString() {
for {
end, escaped := iter.findStringEnd()
if end == -1 {
if !iter.loadMore() {
return
}
if escaped {
iter.head = 1 // skip the first char as last char read is \
}
} else {
iter.head = end
return
}
}
}
// adapted from: https://github.com/buger/jsonparser/blob/master/parser.go
// Tries to find the end of string
// Support if string contains escaped quote symbols.
func (iter *Iterator) findStringEnd() (int, bool) {
escaped := false
for i := iter.head; i < iter.tail; i++ {
c := iter.buf[i]
if c == '"' {
if !escaped {
return i + 1, false
} else {
j := i - 1
for {
if j < iter.head || iter.buf[j] != '\\' {
// even number of backslashes
// either end of buffer, or " found
return i + 1, true
}
j--
if j < iter.head || iter.buf[j] != '\\' {
// odd number of backslashes
// it is \" or \\\"
break
}
j--
}
}
} else if c == '\\' {
escaped = true
}
}
j := iter.tail - 1
for {
if j < iter.head || iter.buf[j] != '\\' {
// even number of backslashes
// either end of buffer, or " found
return -1, false // do not end with \
}
j--
if j < iter.head || iter.buf[j] != '\\' {
// odd number of backslashes
// it is \" or \\\"
break
}
j--
}
return -1, true // end with \
}
func (iter *Iterator) findStringEndWithoutEscape() int {
for i := iter.head; i < iter.tail; i++ {
c := iter.buf[i]
if c == '"' {
return i + 1
} else if c == '\\' {
return -1
}
}
return -1
}
func (iter *Iterator) skipArray() {
level := 1
for {
for i := iter.head; i < iter.tail; i++ {
switch iter.buf[i] {
case '"': // If inside string, skip it
iter.head = i + 1
iter.skipString()
i = iter.head - 1 // it will be i++ soon
case '[': // If open symbol, increase level
level++
case ']': // If close symbol, increase level
level--
// If we have returned to the original level, we're done
if level == 0 {
iter.head = i + 1
return
}
}
}
if (!iter.loadMore()) {
return
}
}
}
func (iter *Iterator) skipObject() {
level := 1
for {
for i := iter.head; i < iter.tail; i++ {
switch iter.buf[i] {
case '"': // If inside string, skip it
iter.head = i + 1
iter.skipString()
i = iter.head - 1 // it will be i++ soon
case '{': // If open symbol, increase level
level++
case '}': // If close symbol, increase level
level--
// If we have returned to the original level, we're done
if level == 0 {
iter.head = i + 1
return
}
}
}
if (!iter.loadMore()) {
return
}
}
}
func (iter *Iterator) skipUntilBreak() {
// true, false, null, number
for {
for i := iter.head; i < iter.tail; i++ {
c := iter.buf[i]
switch c {
case ' ', '\n', '\r', '\t', ',', '}', ']':
iter.head = i
return
}
}
if (!iter.loadMore()) {
return
}
}
}

View File

@ -1,14 +0,0 @@
package jsoniter
import "io"
// adapt to json/encoding api
func Unmarshal(data []byte, v interface{}) error {
iter := ParseBytes(data)
iter.Read(v)
if iter.Error == io.EOF {
return nil
}
return iter.Error
}

View File

@ -1,39 +0,0 @@
package jsoniter
import (
"testing"
"fmt"
)
func Test_read_string_as_any(t *testing.T) {
iter := ParseString(`[1, {"hello": "world"}, 2]`)
any := iter.ReadAny()
if any.ToString(1, "hello") != "world" {
t.FailNow()
}
}
func Test_read_float64_as_any(t *testing.T) {
iter := ParseString(`1.23`)
any := iter.ReadAny()
if any.ToFloat32() != 1.23 {
t.FailNow()
}
}
func Test_read_int_as_any(t *testing.T) {
iter := ParseString(`123`)
any := iter.ReadAny()
if any.ToFloat32() != 123 {
t.FailNow()
}
}
func Test_read_any_from_nested(t *testing.T) {
iter := ParseString(`{"numbers": ["1", "2", ["3", "4"]]}`)
val := iter.ReadAny()
if val.ToInt("numbers", 2, 0) != 3 {
fmt.Println(val.Error)
t.FailNow()
}
}

View File

@ -1,138 +0,0 @@
package jsoniter
import (
"testing"
"encoding/json"
)
func Test_empty_array(t *testing.T) {
iter := ParseString(`[]`)
cont := iter.ReadArray()
if cont != false {
t.FailNow()
}
}
func Test_one_element(t *testing.T) {
iter := ParseString(`[1]`)
cont := iter.ReadArray()
if cont != true {
t.FailNow()
}
if iter.ReadInt64() != 1 {
t.FailNow()
}
cont = iter.ReadArray()
if cont != false {
t.FailNow()
}
}
func Test_two_elements(t *testing.T) {
iter := ParseString(`[1,2]`)
cont := iter.ReadArray()
if cont != true {
t.FailNow()
}
if iter.ReadInt64() != 1 {
t.FailNow()
}
cont = iter.ReadArray()
if cont != true {
t.FailNow()
}
if iter.ReadInt64() != 2 {
t.FailNow()
}
cont = iter.ReadArray()
if cont != false {
t.FailNow()
}
}
func Test_invalid_array(t *testing.T) {
iter := ParseString(`[`)
iter.ReadArray()
if iter.Error == nil {
t.FailNow()
}
}
func Test_whitespace_in_head(t *testing.T) {
iter := ParseString(` [1]`)
cont := iter.ReadArray()
if cont != true {
t.FailNow()
}
if iter.ReadUint64() != 1 {
t.FailNow()
}
}
func Test_whitespace_after_array_start(t *testing.T) {
iter := ParseString(`[ 1]`)
cont := iter.ReadArray()
if cont != true {
t.FailNow()
}
if iter.ReadUint64() != 1 {
t.FailNow()
}
}
func Test_whitespace_before_array_end(t *testing.T) {
iter := ParseString(`[1 ]`)
cont := iter.ReadArray()
if cont != true {
t.FailNow()
}
if iter.ReadUint64() != 1 {
t.FailNow()
}
cont = iter.ReadArray()
if cont != false {
t.FailNow()
}
}
func Test_whitespace_before_comma(t *testing.T) {
iter := ParseString(`[1 ,2]`)
cont := iter.ReadArray()
if cont != true {
t.FailNow()
}
if iter.ReadUint64() != 1 {
t.FailNow()
}
cont = iter.ReadArray()
if cont != true {
t.FailNow()
}
if iter.ReadUint64() != 2 {
t.FailNow()
}
cont = iter.ReadArray()
if cont != false {
t.FailNow()
}
}
func Benchmark_jsoniter_array(b *testing.B) {
b.ReportAllocs()
input := []byte(`[1,2,3,4,5,6,7,8,9]`)
iter := ParseBytes(input)
b.ResetTimer()
for n := 0; n < b.N; n++ {
iter.ResetBytes(input)
for iter.ReadArray() {
iter.ReadUint64()
}
}
}
func Benchmark_json_array(b *testing.B) {
for n := 0; n < b.N; n++ {
result := []interface{}{}
json.Unmarshal([]byte(`[1,2,3]`), &result)
}
}

View File

@ -1,17 +0,0 @@
package jsoniter
import "testing"
func Test_true(t *testing.T) {
iter := ParseString(`true`)
if iter.ReadBool() != true {
t.FailNow()
}
}
func Test_false(t *testing.T) {
iter := ParseString(`false`)
if iter.ReadBool() != false {
t.FailNow()
}
}

View File

@ -1,65 +0,0 @@
package jsoniter
import (
"testing"
"time"
"unsafe"
"strconv"
"reflect"
)
func Test_customize_type_decoder(t *testing.T) {
RegisterTypeDecoder("time.Time", func(ptr unsafe.Pointer, iter *Iterator) {
t, err := time.ParseInLocation("2006-01-02 15:04:05", iter.ReadString(), time.UTC)
if err != nil {
iter.Error = err
return
}
*((*time.Time)(ptr)) = t
})
defer ClearDecoders()
val := time.Time{}
err := Unmarshal([]byte(`"2016-12-05 08:43:28"`), &val)
if err != nil {
t.Fatal(err)
}
year, month, day := val.Date()
if year != 2016 || month != 12 || day != 5 {
t.Fatal(val)
}
}
type Tom struct {
field1 string
}
func Test_customize_field_decoder(t *testing.T) {
RegisterFieldDecoder("jsoniter.Tom", "field1", func(ptr unsafe.Pointer, iter *Iterator) {
*((*string)(ptr)) = strconv.Itoa(iter.ReadInt())
})
defer ClearDecoders()
tom := Tom{}
err := Unmarshal([]byte(`{"field1": 100}`), &tom)
if err != nil {
t.Fatal(err)
}
}
func Test_customize_field_decoder_factory(t *testing.T) {
RegisterFieldCustomizer(func(type_ reflect.Type, field *reflect.StructField) ([]string, DecoderFunc) {
if (type_.String() == "jsoniter.Tom" && field.Name == "field1") {
return []string{"field-1"}, func(ptr unsafe.Pointer, iter *Iterator) {
*((*string)(ptr)) = strconv.Itoa(iter.ReadInt())
}
}
return nil, nil
})
tom := Tom{}
err := Unmarshal([]byte(`{"field-1": 100}`), &tom)
if err != nil {
t.Fatal(err)
}
if tom.field1 != "100" {
t.Fatal(tom.field1)
}
}

View File

@ -1,56 +0,0 @@
package jsoniter
import (
"testing"
"fmt"
)
func Test_bind_api_demo(t *testing.T) {
iter := ParseString(`[0,1,2,3]`)
val := []int{}
iter.Read(&val)
fmt.Println(val[3])
}
func Test_any_api_demo(t *testing.T) {
iter := ParseString(`[0,1,2,3]`)
val := iter.ReadAny()
fmt.Println(val.Get(3))
}
func Test_iterator_api_demo(t *testing.T) {
iter := ParseString(`[0,1,2,3]`)
total := 0
for iter.ReadArray() {
total += iter.ReadInt()
}
fmt.Println(total)
}
type ABC struct {
a Any
}
func Test_deep_nested_any_api(t *testing.T) {
iter := ParseString(`{"a": {"b": {"c": "d"}}}`)
abc := &ABC{}
iter.Read(&abc)
fmt.Println(abc.a.Get("b", "c"))
}
type User struct {
userId int
name string
tags []string
}
func Test_iterator_and_bind_api(t *testing.T) {
iter := ParseString(`[123, {"name": "taowen", "tags": ["crazy", "hacker"]}]`)
user := User{}
iter.ReadArray()
user.userId = iter.ReadInt()
iter.ReadArray()
iter.Read(&user)
iter.ReadArray() // array end
fmt.Println(user)
}

View File

@ -1,47 +0,0 @@
package jsoniter
import (
"testing"
"encoding/json"
"fmt"
)
func Test_float64_0(t *testing.T) {
iter := ParseString(`0`)
val := iter.ReadFloat64()
if val != 0 {
t.Fatal(val)
}
}
func Test_float64_1_dot_1(t *testing.T) {
iter := ParseString(`1.1`)
val := iter.ReadFloat64()
if val != 1.1 {
t.Fatal(val)
}
}
func Test_float32_1_dot_1_comma(t *testing.T) {
iter := ParseString(`1.1,`)
val := iter.ReadFloat32()
if val != 1.1 {
fmt.Println(iter.Error)
t.Fatal(val)
}
}
func Benchmark_jsoniter_float(b *testing.B) {
b.ReportAllocs()
for n := 0; n < b.N; n++ {
iter := ParseString(`1.1111111111`)
iter.ReadFloat64()
}
}
func Benchmark_json_float(b *testing.B) {
for n := 0; n < b.N; n++ {
result := float64(0)
json.Unmarshal([]byte(`1.1`), &result)
}
}

View File

@ -1,83 +0,0 @@
package jsoniter
import (
"testing"
"bytes"
"encoding/json"
)
func Test_uint64_0(t *testing.T) {
iter := Parse(bytes.NewBufferString("0"), 4096)
val := iter.ReadUint64()
if iter.Error != nil {
t.Fatal(iter.Error)
}
if val != 0 {
t.Fatal(val)
}
}
func Test_uint64_1(t *testing.T) {
iter := Parse(bytes.NewBufferString("1"), 4096)
val := iter.ReadUint64()
if val != 1 {
t.Fatal(val)
}
}
func Test_uint64_100(t *testing.T) {
iter := Parse(bytes.NewBufferString("100"), 4096)
val := iter.ReadUint64()
if val != 100 {
t.Fatal(val)
}
}
func Test_uint64_100_comma(t *testing.T) {
iter := Parse(bytes.NewBufferString("100,"), 4096)
val := iter.ReadUint64()
if iter.Error != nil {
t.Fatal(iter.Error)
}
if val != 100 {
t.Fatal(val)
}
}
func Test_uint64_invalid(t *testing.T) {
iter := Parse(bytes.NewBufferString(","), 4096)
iter.ReadUint64()
if iter.Error == nil {
t.FailNow()
}
}
func Test_int64_100(t *testing.T) {
iter := Parse(bytes.NewBufferString("100"), 4096)
val := iter.ReadInt64()
if val != 100 {
t.Fatal(val)
}
}
func Test_int64_minus_100(t *testing.T) {
iter := Parse(bytes.NewBufferString("-100"), 4096)
val := iter.ReadInt64()
if val != -100 {
t.Fatal(val)
}
}
func Benchmark_jsoniter_int(b *testing.B) {
for n := 0; n < b.N; n++ {
iter := ParseString(`-100`)
iter.ReadInt64()
}
}
func Benchmark_json_int(b *testing.B) {
for n := 0; n < b.N; n++ {
result := int64(0)
json.Unmarshal([]byte(`-100`), &result)
}
}

View File

@ -1,84 +0,0 @@
package jsoniter
import (
"testing"
"bytes"
"io"
)
func Test_read_by_one(t *testing.T) {
iter := Parse(bytes.NewBufferString("abc"), 1)
b := iter.readByte()
if iter.Error != nil {
t.Fatal(iter.Error)
}
if b != 'a' {
t.Fatal(b)
}
iter.unreadByte()
if iter.Error != nil {
t.Fatal(iter.Error)
}
iter.unreadByte()
if iter.Error == nil {
t.FailNow()
}
iter.Error = nil
b = iter.readByte()
if iter.Error != nil {
t.Fatal(iter.Error)
}
if b != 'a' {
t.Fatal(b)
}
}
func Test_read_by_two(t *testing.T) {
iter := Parse(bytes.NewBufferString("abc"), 2)
b := iter.readByte()
if iter.Error != nil {
t.Fatal(iter.Error)
}
if b != 'a' {
t.Fatal(b)
}
b = iter.readByte()
if iter.Error != nil {
t.Fatal(iter.Error)
}
if b != 'b' {
t.Fatal(b)
}
iter.unreadByte()
if iter.Error != nil {
t.Fatal(iter.Error)
}
iter.unreadByte()
if iter.Error != nil {
t.Fatal(iter.Error)
}
b = iter.readByte()
if iter.Error != nil {
t.Fatal(iter.Error)
}
if b != 'a' {
t.Fatal(b)
}
}
func Test_read_until_eof(t *testing.T) {
iter := Parse(bytes.NewBufferString("abc"), 2)
iter.readByte()
iter.readByte()
b := iter.readByte()
if iter.Error != nil {
t.Fatal(iter.Error)
}
if b != 'c' {
t.Fatal(b)
}
iter.readByte()
if iter.Error != io.EOF {
t.Fatal(iter.Error)
}
}

View File

@ -1,50 +0,0 @@
package jsoniter
import (
"testing"
"os"
"encoding/json"
"io/ioutil"
)
func Test_large_file(t *testing.T) {
file, err := os.Open("/tmp/large-file.json")
if err != nil {
t.Fatal(err)
}
iter := Parse(file, 4096)
count := 0
for iter.ReadArray() {
iter.Skip()
count++
}
if count != 11351 {
t.Fatal(count)
}
}
func Benchmark_jsoniter_large_file(b *testing.B) {
b.ReportAllocs()
for n := 0; n < b.N; n++ {
file, _ := os.Open("/tmp/large-file.json")
iter := Parse(file, 4096)
count := 0
for iter.ReadArray() {
iter.Skip()
count++
}
file.Close()
}
}
func Benchmark_json_large_file(b *testing.B) {
b.ReportAllocs()
for n := 0; n < b.N; n++ {
file, _ := os.Open("/tmp/large-file.json")
bytes, _ := ioutil.ReadAll(file)
file.Close()
result := []struct{}{}
json.Unmarshal(bytes, &result)
}
}

View File

@ -1,38 +0,0 @@
package jsoniter
import (
"testing"
"reflect"
"fmt"
)
func Test_read_map(t *testing.T) {
iter := ParseString(`{"hello": "world"}`)
m := map[string]string{"1": "2"}
iter.Read(&m)
copy(iter.buf, []byte{0,0,0,0,0,0})
if !reflect.DeepEqual(map[string]string{"1": "2", "hello": "world"}, m) {
fmt.Println(iter.Error)
t.Fatal(m)
}
}
func Test_read_map_of_interface(t *testing.T) {
iter := ParseString(`{"hello": "world"}`)
m := map[string]interface{}{"1": "2"}
iter.Read(&m)
if !reflect.DeepEqual(map[string]interface{}{"1": "2", "hello": "world"}, m) {
fmt.Println(iter.Error)
t.Fatal(m)
}
}
func Test_read_map_of_any(t *testing.T) {
iter := ParseString(`{"hello": "world"}`)
m := map[string]Any{"1": *MakeAny("2")}
iter.Read(&m)
if !reflect.DeepEqual(map[string]Any{"1": *MakeAny("2"), "hello": *MakeAny("world")}, m) {
fmt.Println(iter.Error)
t.Fatal(m)
}
}

View File

@ -1,88 +0,0 @@
package jsoniter
import (
"testing"
"reflect"
"encoding/json"
)
type Level1 struct {
Hello []Level2
}
type Level2 struct {
World string
}
func Test_nested(t *testing.T) {
iter := ParseString(`{"hello": [{"world": "value1"}, {"world": "value2"}]}`)
l1 := Level1{}
for l1Field := iter.ReadObject(); l1Field != ""; l1Field = iter.ReadObject() {
switch l1Field {
case "hello":
l2Array := []Level2{}
for iter.ReadArray() {
l2 := Level2{}
for l2Field := iter.ReadObject(); l2Field != ""; l2Field = iter.ReadObject() {
switch l2Field {
case "world":
l2.World = iter.ReadString()
default:
iter.ReportError("bind l2", "unexpected field: " + l2Field)
}
}
l2Array = append(l2Array, l2)
}
l1.Hello = l2Array
default:
iter.ReportError("bind l1", "unexpected field: " + l1Field)
}
}
if !reflect.DeepEqual(l1, Level1{
Hello: []Level2{
{World: "value1"},
{World: "value2"},
},
}) {
t.Fatal(l1)
}
}
func Benchmark_jsoniter_nested(b *testing.B) {
for n := 0; n < b.N; n++ {
iter := ParseString(`{"hello": [{"world": "value1"}, {"world": "value2"}]}`)
l1 := Level1{}
for l1Field := iter.ReadObject(); l1Field != ""; l1Field = iter.ReadObject() {
switch l1Field {
case "hello":
l1.Hello = readLevel1Hello(iter)
default:
iter.Skip()
}
}
}
}
func readLevel1Hello(iter *Iterator) []Level2 {
l2Array := make([]Level2, 0, 2)
for iter.ReadArray() {
l2 := Level2{}
for l2Field := iter.ReadObject(); l2Field != ""; l2Field = iter.ReadObject() {
switch l2Field {
case "world":
l2.World = iter.ReadString()
default:
iter.Skip()
}
}
l2Array = append(l2Array, l2)
}
return l2Array
}
func Benchmark_json_nested(b *testing.B) {
for n := 0; n < b.N; n++ {
l1 := Level1{}
json.Unmarshal([]byte(`{"hello": [{"world": "value1"}, {"world": "value2"}]}`), &l1)
}
}

View File

@ -1,58 +0,0 @@
package jsoniter
import (
"testing"
)
func Test_null(t *testing.T) {
iter := ParseString(`null`)
if iter.ReadNull() != true {
t.FailNow()
}
}
func Test_null_object(t *testing.T) {
iter := ParseString(`[null,"a"]`)
iter.ReadArray()
if iter.ReadObject() != "" {
t.FailNow()
}
iter.ReadArray()
if iter.ReadString() != "a" {
t.FailNow()
}
}
func Test_null_array(t *testing.T) {
iter := ParseString(`[null,"a"]`)
iter.ReadArray()
if iter.ReadArray() != false {
t.FailNow()
}
iter.ReadArray()
if iter.ReadString() != "a" {
t.FailNow()
}
}
func Test_null_string(t *testing.T) {
iter := ParseString(`[null,"a"]`)
iter.ReadArray()
if iter.ReadString() != "" {
t.FailNow()
}
iter.ReadArray()
if iter.ReadString() != "a" {
t.FailNow()
}
}
func Test_null_skip(t *testing.T) {
iter := ParseString(`[null,"a"]`)
iter.ReadArray()
iter.Skip()
iter.ReadArray()
if iter.ReadString() != "a" {
t.FailNow()
}
}

View File

@ -1,96 +0,0 @@
package jsoniter
import (
"testing"
"encoding/json"
"fmt"
)
func Test_empty_object(t *testing.T) {
iter := ParseString(`{}`)
field := iter.ReadObject()
if field != "" {
t.Fatal(field)
}
}
func Test_one_field(t *testing.T) {
iter := ParseString(`{"a": "b"}`)
field := iter.ReadObject()
if field != "a" {
fmt.Println(iter.Error)
t.Fatal(field)
}
value := iter.ReadString()
if value != "b" {
t.Fatal(field)
}
field = iter.ReadObject()
if field != "" {
t.Fatal(field)
}
}
func Test_two_field(t *testing.T) {
iter := ParseString(`{ "a": "b" , "c": "d" }`)
field := iter.ReadObject()
if field != "a" {
t.Fatal(field)
}
value := iter.ReadString()
if value != "b" {
t.Fatal(field)
}
field = iter.ReadObject()
if field != "c" {
t.Fatal(field)
}
value = iter.ReadString()
if value != "d" {
t.Fatal(field)
}
field = iter.ReadObject()
if field != "" {
t.Fatal(field)
}
iter = ParseString(`{"field1": "1", "field2": 2}`)
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
switch field {
case "field1":
iter.ReadString()
case "field2":
iter.ReadInt64()
default:
iter.ReportError("bind object", "unexpected field")
}
}
}
type TestObj struct {
Field1 string
Field2 uint64
}
func Benchmark_jsoniter_object(b *testing.B) {
for n := 0; n < b.N; n++ {
iter := ParseString(`{"field1": "1", "field2": 2}`)
obj := TestObj{}
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
switch field {
case "field1":
obj.Field1 = iter.ReadString()
case "field2":
obj.Field2 = iter.ReadUint64()
default:
iter.ReportError("bind object", "unexpected field")
}
}
}
}
func Benchmark_json_object(b *testing.B) {
for n := 0; n < b.N; n++ {
result := TestObj{}
json.Unmarshal([]byte(`{"field1": "1", "field2": 2}`), &result)
}
}

View File

@ -1,819 +0,0 @@
package jsoniter
import (
"reflect"
"errors"
"fmt"
"unsafe"
"sync/atomic"
"strings"
"io"
"strconv"
)
type Decoder interface {
decode(ptr unsafe.Pointer, iter *Iterator)
}
type stringDecoder struct {
}
func (decoder *stringDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*string)(ptr)) = iter.ReadString()
}
type intDecoder struct {
}
func (decoder *intDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*int)(ptr)) = iter.ReadInt()
}
type int8Decoder struct {
}
func (decoder *int8Decoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*int8)(ptr)) = iter.ReadInt8()
}
type int16Decoder struct {
}
func (decoder *int16Decoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*int16)(ptr)) = iter.ReadInt16()
}
type int32Decoder struct {
}
func (decoder *int32Decoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*int32)(ptr)) = iter.ReadInt32()
}
type int64Decoder struct {
}
func (decoder *int64Decoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*int64)(ptr)) = iter.ReadInt64()
}
type uintDecoder struct {
}
func (decoder *uintDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*uint)(ptr)) = iter.ReadUint()
}
type uint8Decoder struct {
}
func (decoder *uint8Decoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*uint8)(ptr)) = iter.ReadUint8()
}
type uint16Decoder struct {
}
func (decoder *uint16Decoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*uint16)(ptr)) = iter.ReadUint16()
}
type uint32Decoder struct {
}
func (decoder *uint32Decoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*uint32)(ptr)) = iter.ReadUint32()
}
type uint64Decoder struct {
}
func (decoder *uint64Decoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*uint64)(ptr)) = iter.ReadUint64()
}
type float32Decoder struct {
}
func (decoder *float32Decoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*float32)(ptr)) = iter.ReadFloat32()
}
type float64Decoder struct {
}
func (decoder *float64Decoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*float64)(ptr)) = iter.ReadFloat64()
}
type boolDecoder struct {
}
func (decoder *boolDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*bool)(ptr)) = iter.ReadBool()
}
type interfaceDecoder struct {
}
func (decoder *interfaceDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*interface{})(ptr)) = iter.ReadAny().Get()
}
type anyDecoder struct {
}
func (decoder *anyDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
*((*Any)(ptr)) = *iter.ReadAny()
}
type stringNumberDecoder struct {
elemDecoder Decoder
}
func (decoder *stringNumberDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
c := iter.readByte()
if c != '"' {
iter.ReportError("stringNumberDecoder", `expect "`)
return
}
decoder.elemDecoder.decode(ptr, iter)
if iter.Error != nil {
return
}
c = iter.readByte()
if c != '"' {
iter.ReportError("stringNumberDecoder", `expect "`)
return
}
}
type optionalDecoder struct {
valueType reflect.Type
valueDecoder Decoder
}
func (decoder *optionalDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
if iter.ReadNull() {
*((*unsafe.Pointer)(ptr)) = nil
} else {
value := reflect.New(decoder.valueType)
decoder.valueDecoder.decode(unsafe.Pointer(value.Pointer()), iter)
*((*uintptr)(ptr)) = value.Pointer()
}
}
type generalStructDecoder struct {
type_ reflect.Type
fields map[string]*structFieldDecoder
}
func (decoder *generalStructDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
fieldDecoder := decoder.fields[field]
if fieldDecoder == nil {
iter.Skip()
} else {
fieldDecoder.decode(ptr, iter)
}
}
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.type_, iter.Error.Error())
}
}
type skipDecoder struct {
type_ reflect.Type
}
func (decoder *skipDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
iter.Skip()
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.type_, iter.Error.Error())
}
}
type oneFieldStructDecoder struct {
type_ reflect.Type
fieldName string
fieldDecoder *structFieldDecoder
}
func (decoder *oneFieldStructDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
if field == decoder.fieldName {
decoder.fieldDecoder.decode(ptr, iter)
} else {
iter.Skip()
}
}
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.type_, iter.Error.Error())
}
}
type twoFieldsStructDecoder struct {
type_ reflect.Type
fieldName1 string
fieldDecoder1 *structFieldDecoder
fieldName2 string
fieldDecoder2 *structFieldDecoder
}
func (decoder *twoFieldsStructDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
switch field {
case decoder.fieldName1:
decoder.fieldDecoder1.decode(ptr, iter)
case decoder.fieldName2:
decoder.fieldDecoder2.decode(ptr, iter)
default:
iter.Skip()
}
}
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.type_, iter.Error.Error())
}
}
type threeFieldsStructDecoder struct {
type_ reflect.Type
fieldName1 string
fieldDecoder1 *structFieldDecoder
fieldName2 string
fieldDecoder2 *structFieldDecoder
fieldName3 string
fieldDecoder3 *structFieldDecoder
}
func (decoder *threeFieldsStructDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
switch field {
case decoder.fieldName1:
decoder.fieldDecoder1.decode(ptr, iter)
case decoder.fieldName2:
decoder.fieldDecoder2.decode(ptr, iter)
case decoder.fieldName3:
decoder.fieldDecoder3.decode(ptr, iter)
default:
iter.Skip()
}
}
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.type_, iter.Error.Error())
}
}
type fourFieldsStructDecoder struct {
type_ reflect.Type
fieldName1 string
fieldDecoder1 *structFieldDecoder
fieldName2 string
fieldDecoder2 *structFieldDecoder
fieldName3 string
fieldDecoder3 *structFieldDecoder
fieldName4 string
fieldDecoder4 *structFieldDecoder
}
func (decoder *fourFieldsStructDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
switch field {
case decoder.fieldName1:
decoder.fieldDecoder1.decode(ptr, iter)
case decoder.fieldName2:
decoder.fieldDecoder2.decode(ptr, iter)
case decoder.fieldName3:
decoder.fieldDecoder3.decode(ptr, iter)
case decoder.fieldName4:
decoder.fieldDecoder4.decode(ptr, iter)
default:
iter.Skip()
}
}
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.type_, iter.Error.Error())
}
}
type structFieldDecoder struct {
field *reflect.StructField
fieldDecoder Decoder
}
func (decoder *structFieldDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
fieldPtr := uintptr(ptr) + decoder.field.Offset
decoder.fieldDecoder.decode(unsafe.Pointer(fieldPtr), iter)
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%s: %s", decoder.field.Name, iter.Error.Error())
}
}
type mapDecoder struct {
mapType reflect.Type
elemType reflect.Type
elemDecoder Decoder
mapInterface emptyInterface
}
func (decoder *mapDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
// dark magic to cast unsafe.Pointer back to interface{} using reflect.Type
mapInterface := decoder.mapInterface
mapInterface.word = ptr
realInterface := (*interface{})(unsafe.Pointer(&mapInterface))
realVal := reflect.ValueOf(*realInterface).Elem()
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
elem := reflect.New(decoder.elemType)
decoder.elemDecoder.decode(unsafe.Pointer(elem.Pointer()), iter)
realVal.SetMapIndex(reflect.ValueOf(string([]byte(field))), elem.Elem())
}
}
type sliceDecoder struct {
sliceType reflect.Type
elemType reflect.Type
elemDecoder Decoder
}
// sliceHeader is a safe version of SliceHeader used within this package.
type sliceHeader struct {
Data unsafe.Pointer
Len int
Cap int
}
func (decoder *sliceDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
decoder.doDecode(ptr, iter)
if iter.Error != nil && iter.Error != io.EOF {
iter.Error = fmt.Errorf("%v: %s", decoder.sliceType, iter.Error.Error())
}
}
func (decoder *sliceDecoder) doDecode(ptr unsafe.Pointer, iter *Iterator) {
slice := (*sliceHeader)(ptr)
reuseSlice(slice, decoder.sliceType, 4)
if !iter.ReadArray() {
return
}
offset := uintptr(0)
decoder.elemDecoder.decode(unsafe.Pointer(uintptr(slice.Data) + offset), iter)
if !iter.ReadArray() {
slice.Len = 1
return
}
offset += decoder.elemType.Size()
decoder.elemDecoder.decode(unsafe.Pointer(uintptr(slice.Data) + offset), iter)
if !iter.ReadArray() {
slice.Len = 2
return
}
offset += decoder.elemType.Size()
decoder.elemDecoder.decode(unsafe.Pointer(uintptr(slice.Data) + offset), iter)
if !iter.ReadArray() {
slice.Len = 3
return
}
offset += decoder.elemType.Size()
decoder.elemDecoder.decode(unsafe.Pointer(uintptr(slice.Data) + offset), iter)
slice.Len = 4
for iter.ReadArray() {
growOne(slice, decoder.sliceType, decoder.elemType)
offset += decoder.elemType.Size()
decoder.elemDecoder.decode(unsafe.Pointer(uintptr(slice.Data) + offset), iter)
}
}
// grow grows the slice s so that it can hold extra more values, allocating
// more capacity if needed. It also returns the old and new slice lengths.
func growOne(slice *sliceHeader, sliceType reflect.Type, elementType reflect.Type) {
newLen := slice.Len + 1
if newLen <= slice.Cap {
slice.Len = newLen
return
}
newCap := slice.Cap
if newCap == 0 {
newCap = 1
} else {
for newCap < newLen {
if slice.Len < 1024 {
newCap += newCap
} else {
newCap += newCap / 4
}
}
}
dst := unsafe.Pointer(reflect.MakeSlice(sliceType, newLen, newCap).Pointer())
originalBytesCount := uintptr(slice.Len) * elementType.Size()
srcPtr := (*[1 << 30]byte)(slice.Data)
dstPtr := (*[1 << 30]byte)(dst)
for i := uintptr(0); i < originalBytesCount; i++ {
dstPtr[i] = srcPtr[i]
}
slice.Len = newLen
slice.Cap = newCap
slice.Data = dst
}
func reuseSlice(slice *sliceHeader, sliceType reflect.Type, expectedCap int) {
if expectedCap <= slice.Cap {
return
}
dst := unsafe.Pointer(reflect.MakeSlice(sliceType, 0, expectedCap).Pointer())
slice.Cap = expectedCap
slice.Data = dst
}
var DECODERS unsafe.Pointer
func addDecoderToCache(cacheKey reflect.Type, decoder Decoder) {
retry := true
for retry {
ptr := atomic.LoadPointer(&DECODERS)
cache := *(*map[reflect.Type]Decoder)(ptr)
copy := map[reflect.Type]Decoder{}
for k, v := range cache {
copy[k] = v
}
copy[cacheKey] = decoder
retry = !atomic.CompareAndSwapPointer(&DECODERS, ptr, unsafe.Pointer(&copy))
}
}
func getDecoderFromCache(cacheKey reflect.Type) Decoder {
ptr := atomic.LoadPointer(&DECODERS)
cache := *(*map[reflect.Type]Decoder)(ptr)
return cache[cacheKey]
}
var typeDecoders map[string]Decoder
var fieldDecoders map[string]Decoder
var fieldCustomizers []FieldCustomizerFunc
func init() {
typeDecoders = map[string]Decoder{}
fieldDecoders = map[string]Decoder{}
fieldCustomizers = []FieldCustomizerFunc{}
atomic.StorePointer(&DECODERS, unsafe.Pointer(&map[string]Decoder{}))
}
type DecoderFunc func(ptr unsafe.Pointer, iter *Iterator)
type FieldCustomizerFunc func(type_ reflect.Type, field *reflect.StructField) ([]string, DecoderFunc)
type funcDecoder struct {
func_ DecoderFunc
}
func (decoder *funcDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
decoder.func_(ptr, iter)
}
func RegisterTypeDecoder(type_ string, func_ DecoderFunc) {
typeDecoders[type_] = &funcDecoder{func_}
}
func RegisterFieldDecoder(type_ string, field string, func_ DecoderFunc) {
fieldDecoders[fmt.Sprintf("%s/%s", type_, field)] = &funcDecoder{func_}
}
func RegisterFieldCustomizer(func_ FieldCustomizerFunc) {
fieldCustomizers = append(fieldCustomizers, func_)
}
func ClearDecoders() {
typeDecoders = map[string]Decoder{}
fieldDecoders = map[string]Decoder{}
}
// emptyInterface is the header for an interface{} value.
type emptyInterface struct {
typ *struct{}
word unsafe.Pointer
}
func (iter *Iterator) ReadAny() (ret *Any) {
valueType := iter.WhatIsNext()
switch valueType {
case String:
return MakeAny(iter.ReadString())
case Number:
return iter.readNumber()
case Null:
return MakeAny(nil)
case Bool:
return MakeAny(iter.ReadBool())
case Array:
val := []interface{}{}
for (iter.ReadArray()) {
element := iter.ReadAny()
if iter.Error != nil {
return
}
val = append(val, element.val)
}
return MakeAny(val)
case Object:
val := map[string]interface{}{}
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
element := iter.ReadAny()
if iter.Error != nil {
return
}
val[string([]byte(field))] = element.val
}
return MakeAny(val)
default:
iter.ReportError("ReadAny", fmt.Sprintf("unexpected value type: %v", valueType))
return MakeAny(nil)
}
}
func (iter *Iterator) readNumber() (ret *Any) {
strBuf := [8]byte{}
str := strBuf[0:0]
hasMore := true
foundFloat := false
foundNegative := false
for(hasMore) {
for i := iter.head; i < iter.tail; i++ {
c := iter.buf[i]
switch c {
case '-':
foundNegative = true
str = append(str, c)
continue
case '+', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
str = append(str, c)
continue
case '.', 'e', 'E':
foundFloat = true
str = append(str, c)
continue
default:
hasMore = false
break
}
}
if hasMore {
if !iter.loadMore() {
break
}
}
}
if iter.Error != nil && iter.Error != io.EOF {
return
}
number := *(*string)(unsafe.Pointer(&str))
if foundFloat {
val, err := strconv.ParseFloat(number, 64)
if err != nil {
iter.Error = err
return
}
return MakeAny(val)
}
if foundNegative {
val, err := strconv.ParseInt(number, 10, 64)
if err != nil {
iter.Error = err
return
}
return MakeAny(val)
}
val, err := strconv.ParseUint(number, 10, 64)
if err != nil {
iter.Error = err
return
}
return MakeAny(val)
}
func (iter *Iterator) Read(obj interface{}) {
type_ := reflect.TypeOf(obj)
cacheKey := type_.Elem()
cachedDecoder := getDecoderFromCache(cacheKey)
if cachedDecoder == nil {
decoder, err := decoderOfType(type_)
if err != nil {
iter.Error = err
return
}
cachedDecoder = decoder
addDecoderToCache(cacheKey, decoder)
}
e := (*emptyInterface)(unsafe.Pointer(&obj))
cachedDecoder.decode(e.word, iter)
}
type prefix string
func (p prefix) addTo(decoder Decoder, err error) (Decoder, error) {
if err != nil {
return nil, fmt.Errorf("%s: %s", p, err.Error())
}
return decoder, err
}
func decoderOfType(type_ reflect.Type) (Decoder, error) {
switch type_.Kind() {
case reflect.Ptr:
return prefix("ptr").addTo(decoderOfPtr(type_.Elem()))
default:
return nil, errors.New("expect ptr")
}
}
func decoderOfPtr(type_ reflect.Type) (Decoder, error) {
typeName := type_.String()
if typeName == "jsoniter.Any" {
return &anyDecoder{}, nil
}
typeDecoder := typeDecoders[typeName]
if typeDecoder != nil {
return typeDecoder, nil
}
switch type_.Kind() {
case reflect.String:
return &stringDecoder{}, nil
case reflect.Int:
return &intDecoder{}, nil
case reflect.Int8:
return &int8Decoder{}, nil
case reflect.Int16:
return &int16Decoder{}, nil
case reflect.Int32:
return &int32Decoder{}, nil
case reflect.Int64:
return &int64Decoder{}, nil
case reflect.Uint:
return &uintDecoder{}, nil
case reflect.Uint8:
return &uint8Decoder{}, nil
case reflect.Uint16:
return &uint16Decoder{}, nil
case reflect.Uint32:
return &uint32Decoder{}, nil
case reflect.Uint64:
return &uint64Decoder{}, nil
case reflect.Float32:
return &float32Decoder{}, nil
case reflect.Float64:
return &float64Decoder{}, nil
case reflect.Bool:
return &boolDecoder{}, nil
case reflect.Interface:
return &interfaceDecoder{}, nil
case reflect.Struct:
return decoderOfStruct(type_)
case reflect.Slice:
return prefix("[slice]").addTo(decoderOfSlice(type_))
case reflect.Map:
return prefix("[map]").addTo(decoderOfMap(type_))
case reflect.Ptr:
return prefix("[optional]").addTo(decoderOfOptional(type_.Elem()))
default:
return nil, fmt.Errorf("unsupported type: %v", type_)
}
}
func decoderOfOptional(type_ reflect.Type) (Decoder, error) {
decoder, err := decoderOfPtr(type_)
if err != nil {
return nil, err
}
return &optionalDecoder{type_, decoder}, nil
}
func decoderOfStruct(type_ reflect.Type) (Decoder, error) {
fields := map[string]*structFieldDecoder{}
for i := 0; i < type_.NumField(); i++ {
field := type_.Field(i)
fieldDecoderKey := fmt.Sprintf("%s/%s", type_.String(), field.Name)
var fieldNames []string
for _, customizer := range fieldCustomizers {
alternativeFieldNames, func_ := customizer(type_, &field)
if alternativeFieldNames != nil {
fieldNames = alternativeFieldNames
}
if func_ != nil {
fieldDecoders[fieldDecoderKey] = &funcDecoder{func_}
}
}
decoder := fieldDecoders[fieldDecoderKey]
tagParts := strings.Split(field.Tag.Get("json"), ",")
if fieldNames == nil {
switch tagParts[0] {
case "":
fieldNames = []string{field.Name}
case "-":
fieldNames = []string{}
}
}
if decoder == nil {
var err error
decoder, err = decoderOfPtr(field.Type)
if err != nil {
return prefix(fmt.Sprintf("{%s}", field.Name)).addTo(decoder, err)
}
}
if len(tagParts) > 1 && tagParts[1] == "string" {
decoder = &stringNumberDecoder{decoder}
}
for _, fieldName := range fieldNames {
fields[fieldName] = &structFieldDecoder{&field, decoder}
}
}
switch len(fields) {
case 0:
return &skipDecoder{type_}, nil
case 1:
for fieldName, fieldDecoder := range fields {
return &oneFieldStructDecoder{type_, fieldName, fieldDecoder}, nil
}
case 2:
var fieldName1 string
var fieldName2 string
var fieldDecoder1 *structFieldDecoder
var fieldDecoder2 *structFieldDecoder
for fieldName, fieldDecoder := range fields {
if fieldName1 == "" {
fieldName1 = fieldName
fieldDecoder1 = fieldDecoder
} else {
fieldName2 = fieldName
fieldDecoder2 = fieldDecoder
}
}
return &twoFieldsStructDecoder{type_, fieldName1, fieldDecoder1, fieldName2, fieldDecoder2}, nil
case 3:
var fieldName1 string
var fieldName2 string
var fieldName3 string
var fieldDecoder1 *structFieldDecoder
var fieldDecoder2 *structFieldDecoder
var fieldDecoder3 *structFieldDecoder
for fieldName, fieldDecoder := range fields {
if fieldName1 == "" {
fieldName1 = fieldName
fieldDecoder1 = fieldDecoder
} else if fieldName2 == "" {
fieldName2 = fieldName
fieldDecoder2 = fieldDecoder
} else {
fieldName3 = fieldName
fieldDecoder3 = fieldDecoder
}
}
return &threeFieldsStructDecoder{type_,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3}, nil
case 4:
var fieldName1 string
var fieldName2 string
var fieldName3 string
var fieldName4 string
var fieldDecoder1 *structFieldDecoder
var fieldDecoder2 *structFieldDecoder
var fieldDecoder3 *structFieldDecoder
var fieldDecoder4 *structFieldDecoder
for fieldName, fieldDecoder := range fields {
if fieldName1 == "" {
fieldName1 = fieldName
fieldDecoder1 = fieldDecoder
} else if fieldName2 == "" {
fieldName2 = fieldName
fieldDecoder2 = fieldDecoder
} else if fieldName3 == "" {
fieldName3 = fieldName
fieldDecoder3 = fieldDecoder
} else {
fieldName4 = fieldName
fieldDecoder4 = fieldDecoder
}
}
return &fourFieldsStructDecoder{type_,
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
fieldName4, fieldDecoder4}, nil
}
return &generalStructDecoder{type_, fields}, nil
}
func decoderOfSlice(type_ reflect.Type) (Decoder, error) {
decoder, err := decoderOfPtr(type_.Elem())
if err != nil {
return nil, err
}
return &sliceDecoder{type_, type_.Elem(), decoder}, nil
}
func decoderOfMap(type_ reflect.Type) (Decoder, error) {
decoder, err := decoderOfPtr(type_.Elem())
if err != nil {
return nil, err
}
mapInterface := reflect.New(type_).Interface()
return &mapDecoder{type_, type_.Elem(), decoder, *((*emptyInterface)(unsafe.Pointer(&mapInterface)))}, nil
}

View File

@ -1,341 +0,0 @@
package jsoniter
import (
"testing"
"fmt"
"encoding/json"
"unsafe"
)
func Test_reflect_str(t *testing.T) {
iter := ParseString(`"hello"`)
str := ""
iter.Read(&str)
if str != "hello" {
fmt.Println(iter.Error)
t.Fatal(str)
}
}
func Test_reflect_ptr_str(t *testing.T) {
iter := ParseString(`"hello"`)
var str *string
iter.Read(&str)
if *str != "hello" {
t.Fatal(str)
}
}
func Test_reflect_int(t *testing.T) {
iter := ParseString(`123`)
val := int(0)
iter.Read(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_int8(t *testing.T) {
iter := ParseString(`123`)
val := int8(0)
iter.Read(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_int16(t *testing.T) {
iter := ParseString(`123`)
val := int16(0)
iter.Read(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_int32(t *testing.T) {
iter := ParseString(`123`)
val := int32(0)
iter.Read(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_int64(t *testing.T) {
iter := ParseString(`123`)
val := int64(0)
iter.Read(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_uint(t *testing.T) {
iter := ParseString(`123`)
val := uint(0)
iter.Read(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_uint8(t *testing.T) {
iter := ParseString(`123`)
val := uint8(0)
iter.Read(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_uint16(t *testing.T) {
iter := ParseString(`123`)
val := uint16(0)
iter.Read(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_uint32(t *testing.T) {
iter := ParseString(`123`)
val := uint32(0)
iter.Read(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_uint64(t *testing.T) {
iter := ParseString(`123`)
val := uint64(0)
iter.Read(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_byte(t *testing.T) {
iter := ParseString(`123`)
val := byte(0)
iter.Read(&val)
if val != 123 {
t.Fatal(val)
}
}
func Test_reflect_float32(t *testing.T) {
iter := ParseString(`1.23`)
val := float32(0)
iter.Read(&val)
if val != 1.23 {
fmt.Println(iter.Error)
t.Fatal(val)
}
}
func Test_reflect_float64(t *testing.T) {
iter := ParseString(`1.23`)
val := float64(0)
iter.Read(&val)
if val != 1.23 {
fmt.Println(iter.Error)
t.Fatal(val)
}
}
func Test_reflect_bool(t *testing.T) {
iter := ParseString(`true`)
val := false
iter.Read(&val)
if val != true {
fmt.Println(iter.Error)
t.Fatal(val)
}
}
type StructOfString struct {
field1 string
field2 string
}
func Test_reflect_struct_string(t *testing.T) {
iter := ParseString(`{"field1": "hello", "field2": "world"}`)
struct_ := StructOfString{}
iter.Read(&struct_)
if struct_.field1 != "hello" {
fmt.Println(iter.Error)
t.Fatal(struct_.field1)
}
if struct_.field2 != "world" {
fmt.Println(iter.Error)
t.Fatal(struct_.field1)
}
}
type StructOfStringPtr struct {
field1 *string
field2 *string
}
func Test_reflect_struct_string_ptr(t *testing.T) {
iter := ParseString(`{"field1": null, "field2": "world"}`)
struct_ := StructOfStringPtr{}
iter.Read(&struct_)
if struct_.field1 != nil {
fmt.Println(iter.Error)
t.Fatal(struct_.field1)
}
if *struct_.field2 != "world" {
fmt.Println(iter.Error)
t.Fatal(struct_.field2)
}
}
type StructOfTag struct {
field1 string `json:"field-1"`
field2 string `json:"-"`
field3 int `json:",string"`
}
func Test_reflect_struct_tag_field(t *testing.T) {
iter := ParseString(`{"field-1": "hello", "field2": "", "field3": "100"}`)
struct_ := StructOfTag{field2: "world"}
iter.Read(&struct_)
if struct_.field1 != "hello" {
fmt.Println(iter.Error)
t.Fatal(struct_.field1)
}
if struct_.field2 != "world" {
fmt.Println(iter.Error)
t.Fatal(struct_.field2)
}
if struct_.field3 != 100 {
fmt.Println(iter.Error)
t.Fatal(struct_.field3)
}
}
func Test_reflect_slice(t *testing.T) {
iter := ParseString(`["hello", "world"]`)
slice := make([]string, 0, 5)
iter.Read(&slice)
if len(slice) != 2 {
fmt.Println(iter.Error)
t.Fatal(len(slice))
}
if slice[0] != "hello" {
fmt.Println(iter.Error)
t.Fatal(slice[0])
}
if slice[1] != "world" {
fmt.Println(iter.Error)
t.Fatal(slice[1])
}
}
func Test_reflect_large_slice(t *testing.T) {
iter := ParseString(`[1,2,3,4,5,6,7,8,9]`)
slice := make([]int, 0, 1)
iter.Read(&slice)
if len(slice) != 9 {
fmt.Println(iter.Error)
t.Fatal(len(slice))
}
if slice[0] != 1 {
fmt.Println(iter.Error)
t.Fatal(slice[0])
}
if slice[8] != 9 {
fmt.Println(iter.Error)
t.Fatal(slice[1])
}
}
func Test_reflect_nested(t *testing.T) {
iter := ParseString(`[{"field1": "hello"}, null, {"field2": "world"}]`)
slice := []*StructOfString{}
iter.Read(&slice)
if len(slice) != 3 {
fmt.Println(iter.Error)
t.Fatal(len(slice))
}
if slice[0].field1 != "hello" {
fmt.Println(iter.Error)
t.Fatal(slice[0])
}
if slice[1] != nil {
fmt.Println(iter.Error)
t.Fatal(slice[1])
}
if slice[2].field2 != "world" {
fmt.Println(iter.Error)
t.Fatal(slice[1])
}
}
func Test_reflect_base64(t *testing.T) {
iter := ParseString(`"YWJj"`)
val := []byte{}
RegisterTypeDecoder("[]uint8", func(ptr unsafe.Pointer, iter *Iterator) {
*((*[]byte)(ptr)) = iter.ReadBase64()
})
defer ClearDecoders()
iter.Read(&val)
if "abc" != string(val) {
t.Fatal(string(val))
}
}
type StructOfTagOne struct {
field1 string `json:"field1"`
field2 string `json:"field2"`
field3 int `json:"field3,string"`
field4 int `json:"field4,string"`
}
func Benchmark_jsoniter_reflect(b *testing.B) {
b.ReportAllocs()
for n := 0; n < b.N; n++ {
iter := ParseString(`{"field3": "100"}`)
struct_ := StructOfTagOne{}
iter.Read(&struct_)
//iter := ParseString(`[1,2,3]`)
//var array []int
//iter.Read(&array)
}
}
func Benchmark_jsoniter_direct(b *testing.B) {
b.ReportAllocs()
for n := 0; n < b.N; n++ {
//iter := ParseString(`{"field1": "hello", "field2": "world"}`)
//struct_ := StructOfString{}
//for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
// switch field {
// case "field1":
// struct_.field1 = iter.ReadString()
// case "field2":
// struct_.field2 = iter.ReadString()
// default:
// iter.Skip()
// }
//}
iter := ParseString(`["hello", "world"]`)
array := make([]string, 0, 2)
for iter.ReadArray() {
array = append(array, iter.ReadString())
}
}
}
func Benchmark_json_reflect(b *testing.B) {
b.ReportAllocs()
for n := 0; n < b.N; n++ {
struct_ := StructOfTagOne{}
json.Unmarshal([]byte(`{"field3": "100"}`), &struct_)
//array := make([]string, 0, 2)
//json.Unmarshal([]byte(`["hello", "world"]`), &array)
}
}

View File

@ -1,151 +0,0 @@
package jsoniter
import (
"testing"
"encoding/json"
)
func Test_skip_number(t *testing.T) {
iter := ParseString(`[-0.12, "b"]`)
iter.ReadArray()
iter.Skip()
iter.ReadArray()
if iter.ReadString() != "b" {
t.FailNow()
}
}
func Test_skip_null(t *testing.T) {
iter := ParseString(`[null , "b"]`)
iter.ReadArray()
iter.Skip()
iter.ReadArray()
if iter.ReadString() != "b" {
t.FailNow()
}
}
func Test_skip_true(t *testing.T) {
iter := ParseString(`[true , "b"]`)
iter.ReadArray()
iter.Skip()
iter.ReadArray()
if iter.ReadString() != "b" {
t.FailNow()
}
}
func Test_skip_false(t *testing.T) {
iter := ParseString(`[false , "b"]`)
iter.ReadArray()
iter.Skip()
iter.ReadArray()
if iter.ReadString() != "b" {
t.FailNow()
}
}
func Test_skip_array(t *testing.T) {
iter := ParseString(`[[1, [2, [3], 4]], "b"]`)
iter.ReadArray()
iter.Skip()
iter.ReadArray()
if iter.ReadString() != "b" {
t.FailNow()
}
}
func Test_skip_empty_array(t *testing.T) {
iter := ParseString(`[ [ ], "b"]`)
iter.ReadArray()
iter.Skip()
iter.ReadArray()
if iter.ReadString() != "b" {
t.FailNow()
}
}
func Test_skip_nested(t *testing.T) {
iter := ParseString(`[ {"a" : [{"b": "c"}], "d": 102 }, "b"]`)
iter.ReadArray()
iter.Skip()
iter.ReadArray()
if iter.ReadString() != "b" {
t.FailNow()
}
}
type TestResp struct {
Code uint64
}
func Benchmark_jsoniter_skip(b *testing.B) {
input := []byte(`
{
"_shards":{
"total" : 5,
"successful" : 5,
"failed" : 0
},
"hits":{
"total" : 1,
"hits" : [
{
"_index" : "twitter",
"_type" : "tweet",
"_id" : "1",
"_source" : {
"user" : "kimchy",
"postDate" : "2009-11-15T14:12:12",
"message" : "trying out Elasticsearch"
}
}
]
},
"code": 200
}`)
for n := 0; n < b.N; n++ {
result := TestResp{}
iter := ParseBytes(input)
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
switch field {
case "code":
result.Code = iter.ReadUint64()
default:
iter.Skip()
}
}
}
}
func Benchmark_json_skip(b *testing.B) {
input := []byte(`
{
"_shards":{
"total" : 5,
"successful" : 5,
"failed" : 0
},
"hits":{
"total" : 1,
"hits" : [
{
"_index" : "twitter",
"_type" : "tweet",
"_id" : "1",
"_source" : {
"user" : "kimchy",
"postDate" : "2009-11-15T14:12:12",
"message" : "trying out Elasticsearch"
}
}
]
},
"code": 200
}`)
for n := 0; n < b.N; n++ {
result := TestResp{}
json.Unmarshal(input, &result)
}
}

View File

@ -1,124 +0,0 @@
package jsoniter
import (
"testing"
"bytes"
"encoding/json"
)
func Test_string_empty(t *testing.T) {
iter := Parse(bytes.NewBufferString(`""`), 4096)
val := iter.ReadString()
if iter.Error != nil {
t.Fatal(iter.Error)
}
if val != "" {
t.Fatal(val)
}
}
func Test_string_hello(t *testing.T) {
iter := Parse(bytes.NewBufferString(`"hello"`), 4096)
val := iter.ReadString()
if iter.Error != nil {
t.Fatal(iter.Error)
}
if val != "hello" {
t.Fatal(val)
}
}
func Test_string_escape_quote(t *testing.T) {
iter := Parse(bytes.NewBufferString(`"hel\"lo"`), 4096)
val := iter.ReadString()
if iter.Error != nil {
t.Fatal(iter.Error)
}
if val != `hel"lo` {
t.Fatal(val)
}
}
func Test_string_escape_newline(t *testing.T) {
iter := Parse(bytes.NewBufferString(`"hel\nlo"`), 4096)
val := iter.ReadString()
if iter.Error != nil {
t.Fatal(iter.Error)
}
if val != "hel\nlo" {
t.Fatal(val)
}
}
func Test_string_escape_unicode(t *testing.T) {
iter := Parse(bytes.NewBufferString(`"\u4e2d\u6587"`), 4096)
val := iter.ReadString()
if iter.Error != nil {
t.Fatal(iter.Error)
}
if val != "中文" {
t.Fatal(val)
}
}
func Test_string_escape_unicode_with_surrogate(t *testing.T) {
iter := Parse(bytes.NewBufferString(`"\ud83d\udc4a"`), 4096)
val := iter.ReadString()
if iter.Error != nil {
t.Fatal(iter.Error)
}
if val != "\xf0\x9f\x91\x8a" {
t.Fatal(val)
}
}
func Test_string_as_bytes(t *testing.T) {
iter := Parse(bytes.NewBufferString(`"hello""world"`), 4096)
val := string(iter.readStringAsBytes())
if val != "hello" {
t.Fatal(val)
}
val = string(iter.readStringAsBytes())
if val != "world" {
t.Fatal(val)
}
}
func Benchmark_jsoniter_unicode(b *testing.B) {
for n := 0; n < b.N; n++ {
iter := ParseString(`"\ud83d\udc4a"`)
iter.ReadString()
}
}
func Benchmark_jsoniter_ascii(b *testing.B) {
iter := ParseString(`"hello, world!"`)
b.ResetTimer()
for n := 0; n < b.N; n++ {
iter.ResetBytes(iter.buf)
iter.ReadString()
}
}
func Benchmark_jsoniter_string_as_bytes(b *testing.B) {
iter := ParseString(`"hello, world!"`)
b.ResetTimer()
for n := 0; n < b.N; n++ {
iter.ResetBytes(iter.buf)
iter.readStringAsBytes()
}
}
func Benchmark_json_unicode(b *testing.B) {
for n := 0; n < b.N; n++ {
result := ""
json.Unmarshal([]byte(`"\ud83d\udc4a"`), &result)
}
}
func Benchmark_json_ascii(b *testing.B) {
for n := 0; n < b.N; n++ {
result := ""
json.Unmarshal([]byte(`"hello"`), &result)
}
}

View File

@ -0,0 +1,247 @@
package misc_tests
import (
"bytes"
"encoding/json"
"testing"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
func Test_empty_array(t *testing.T) {
should := require.New(t)
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[]`)
cont := iter.ReadArray()
should.False(cont)
iter = jsoniter.ParseString(jsoniter.ConfigDefault, `[]`)
iter.ReadArrayCB(func(iter *jsoniter.Iterator) bool {
should.FailNow("should not call")
return true
})
}
func Test_one_element(t *testing.T) {
should := require.New(t)
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[1]`)
should.True(iter.ReadArray())
should.Equal(1, iter.ReadInt())
should.False(iter.ReadArray())
iter = jsoniter.ParseString(jsoniter.ConfigDefault, `[1]`)
iter.ReadArrayCB(func(iter *jsoniter.Iterator) bool {
should.Equal(1, iter.ReadInt())
return true
})
}
func Test_two_elements(t *testing.T) {
should := require.New(t)
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[1,2]`)
should.True(iter.ReadArray())
should.Equal(int64(1), iter.ReadInt64())
should.True(iter.ReadArray())
should.Equal(int64(2), iter.ReadInt64())
should.False(iter.ReadArray())
iter = jsoniter.ParseString(jsoniter.ConfigDefault, `[1,2]`)
should.Equal([]interface{}{float64(1), float64(2)}, iter.Read())
}
func Test_whitespace_in_head(t *testing.T) {
iter := jsoniter.ParseString(jsoniter.ConfigDefault, ` [1]`)
cont := iter.ReadArray()
if cont != true {
t.FailNow()
}
if iter.ReadUint64() != 1 {
t.FailNow()
}
}
func Test_whitespace_after_array_start(t *testing.T) {
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[ 1]`)
cont := iter.ReadArray()
if cont != true {
t.FailNow()
}
if iter.ReadUint64() != 1 {
t.FailNow()
}
}
func Test_whitespace_before_array_end(t *testing.T) {
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[1 ]`)
cont := iter.ReadArray()
if cont != true {
t.FailNow()
}
if iter.ReadUint64() != 1 {
t.FailNow()
}
cont = iter.ReadArray()
if cont != false {
t.FailNow()
}
}
func Test_whitespace_before_comma(t *testing.T) {
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[1 ,2]`)
cont := iter.ReadArray()
if cont != true {
t.FailNow()
}
if iter.ReadUint64() != 1 {
t.FailNow()
}
cont = iter.ReadArray()
if cont != true {
t.FailNow()
}
if iter.ReadUint64() != 2 {
t.FailNow()
}
cont = iter.ReadArray()
if cont != false {
t.FailNow()
}
}
func Test_write_array(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := jsoniter.NewStream(jsoniter.Config{IndentionStep: 2}.Froze(), buf, 4096)
stream.WriteArrayStart()
stream.WriteInt(1)
stream.WriteMore()
stream.WriteInt(2)
stream.WriteArrayEnd()
stream.Flush()
should.Nil(stream.Error)
should.Equal("[\n 1,\n 2\n]", buf.String())
}
func Test_write_val_array(t *testing.T) {
should := require.New(t)
val := []int{1, 2, 3}
str, err := jsoniter.MarshalToString(&val)
should.Nil(err)
should.Equal("[1,2,3]", str)
}
func Test_write_val_empty_array(t *testing.T) {
should := require.New(t)
val := []int{}
str, err := jsoniter.MarshalToString(val)
should.Nil(err)
should.Equal("[]", str)
}
func Test_write_array_of_interface_in_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field []interface{}
Field2 string
}
val := TestObject{[]interface{}{1, 2}, ""}
str, err := jsoniter.MarshalToString(val)
should.Nil(err)
should.Contains(str, `"Field":[1,2]`)
should.Contains(str, `"Field2":""`)
}
func Test_encode_byte_array(t *testing.T) {
should := require.New(t)
bytes, err := json.Marshal([]byte{1, 2, 3})
should.Nil(err)
should.Equal(`"AQID"`, string(bytes))
bytes, err = jsoniter.Marshal([]byte{1, 2, 3})
should.Nil(err)
should.Equal(`"AQID"`, string(bytes))
}
func Test_encode_empty_byte_array(t *testing.T) {
should := require.New(t)
bytes, err := json.Marshal([]byte{})
should.Nil(err)
should.Equal(`""`, string(bytes))
bytes, err = jsoniter.Marshal([]byte{})
should.Nil(err)
should.Equal(`""`, string(bytes))
}
func Test_encode_nil_byte_array(t *testing.T) {
should := require.New(t)
var nilSlice []byte
bytes, err := json.Marshal(nilSlice)
should.Nil(err)
should.Equal(`null`, string(bytes))
bytes, err = jsoniter.Marshal(nilSlice)
should.Nil(err)
should.Equal(`null`, string(bytes))
}
func Test_decode_byte_array_from_base64(t *testing.T) {
should := require.New(t)
data := []byte{}
err := json.Unmarshal([]byte(`"AQID"`), &data)
should.Nil(err)
should.Equal([]byte{1, 2, 3}, data)
err = jsoniter.Unmarshal([]byte(`"AQID"`), &data)
should.Nil(err)
should.Equal([]byte{1, 2, 3}, data)
}
func Test_decode_byte_array_from_base64_with_newlines(t *testing.T) {
should := require.New(t)
data := []byte{}
err := json.Unmarshal([]byte(`"A\rQ\nID"`), &data)
should.Nil(err)
should.Equal([]byte{1, 2, 3}, data)
err = jsoniter.Unmarshal([]byte(`"A\rQ\nID"`), &data)
should.Nil(err)
should.Equal([]byte{1, 2, 3}, data)
}
func Test_decode_byte_array_from_array(t *testing.T) {
should := require.New(t)
data := []byte{}
err := json.Unmarshal([]byte(`[1,2,3]`), &data)
should.Nil(err)
should.Equal([]byte{1, 2, 3}, data)
err = jsoniter.Unmarshal([]byte(`[1,2,3]`), &data)
should.Nil(err)
should.Equal([]byte{1, 2, 3}, data)
}
func Test_decode_slice(t *testing.T) {
should := require.New(t)
slice := make([]string, 0, 5)
jsoniter.UnmarshalFromString(`["hello", "world"]`, &slice)
should.Equal([]string{"hello", "world"}, slice)
}
func Test_decode_large_slice(t *testing.T) {
should := require.New(t)
slice := make([]int, 0, 1)
jsoniter.UnmarshalFromString(`[1,2,3,4,5,6,7,8,9]`, &slice)
should.Equal([]int{1, 2, 3, 4, 5, 6, 7, 8, 9}, slice)
}
func Benchmark_jsoniter_array(b *testing.B) {
b.ReportAllocs()
input := []byte(`[1,2,3,4,5,6,7,8,9]`)
iter := jsoniter.ParseBytes(jsoniter.ConfigDefault, input)
b.ResetTimer()
for n := 0; n < b.N; n++ {
iter.ResetBytes(input)
for iter.ReadArray() {
iter.ReadUint64()
}
}
}
func Benchmark_json_array(b *testing.B) {
for n := 0; n < b.N; n++ {
result := []interface{}{}
json.Unmarshal([]byte(`[1,2,3]`), &result)
}
}

View File

@ -0,0 +1,47 @@
package misc_tests
import (
"bytes"
"testing"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
func Test_true(t *testing.T) {
should := require.New(t)
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `true`)
should.True(iter.ReadBool())
iter = jsoniter.ParseString(jsoniter.ConfigDefault, `true`)
should.Equal(true, iter.Read())
}
func Test_false(t *testing.T) {
should := require.New(t)
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `false`)
should.False(iter.ReadBool())
}
func Test_write_true_false(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := jsoniter.NewStream(jsoniter.ConfigDefault, buf, 4096)
stream.WriteTrue()
stream.WriteFalse()
stream.WriteBool(false)
stream.Flush()
should.Nil(stream.Error)
should.Equal("truefalsefalse", buf.String())
}
func Test_write_val_bool(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := jsoniter.NewStream(jsoniter.ConfigDefault, buf, 4096)
stream.WriteVal(true)
should.Equal(stream.Buffered(), 4)
stream.Flush()
should.Equal(stream.Buffered(), 0)
should.Nil(stream.Error)
should.Equal("true", buf.String())
}

View File

@ -0,0 +1,116 @@
package misc_tests
import (
"encoding/json"
"math"
"testing"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
func Test_read_big_float(t *testing.T) {
should := require.New(t)
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `12.3`)
val := iter.ReadBigFloat()
val64, _ := val.Float64()
should.Equal(12.3, val64)
}
func Test_read_big_int(t *testing.T) {
should := require.New(t)
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `92233720368547758079223372036854775807`)
val := iter.ReadBigInt()
should.NotNil(val)
should.Equal(`92233720368547758079223372036854775807`, val.String())
}
func Test_read_float_as_interface(t *testing.T) {
should := require.New(t)
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `12.3`)
should.Equal(float64(12.3), iter.Read())
}
func Test_wrap_float(t *testing.T) {
should := require.New(t)
str, err := jsoniter.MarshalToString(jsoniter.WrapFloat64(12.3))
should.Nil(err)
should.Equal("12.3", str)
}
func Test_read_float64_cursor(t *testing.T) {
should := require.New(t)
iter := jsoniter.ParseString(jsoniter.ConfigDefault, "[1.23456789\n,2,3]")
should.True(iter.ReadArray())
should.Equal(1.23456789, iter.Read())
should.True(iter.ReadArray())
should.Equal(float64(2), iter.Read())
}
func Test_read_float_scientific(t *testing.T) {
should := require.New(t)
var obj interface{}
should.NoError(jsoniter.UnmarshalFromString(`1e1`, &obj))
should.Equal(float64(10), obj)
should.NoError(json.Unmarshal([]byte(`1e1`), &obj))
should.Equal(float64(10), obj)
should.NoError(jsoniter.UnmarshalFromString(`1.0e1`, &obj))
should.Equal(float64(10), obj)
should.NoError(json.Unmarshal([]byte(`1.0e1`), &obj))
should.Equal(float64(10), obj)
}
func Test_lossy_float_marshal(t *testing.T) {
should := require.New(t)
api := jsoniter.Config{MarshalFloatWith6Digits: true}.Froze()
output, err := api.MarshalToString(float64(0.1234567))
should.Nil(err)
should.Equal("0.123457", output)
output, err = api.MarshalToString(float32(0.1234567))
should.Nil(err)
should.Equal("0.123457", output)
}
func Test_read_number(t *testing.T) {
should := require.New(t)
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `92233720368547758079223372036854775807`)
val := iter.ReadNumber()
should.Equal(`92233720368547758079223372036854775807`, string(val))
}
func Test_encode_inf(t *testing.T) {
should := require.New(t)
_, err := json.Marshal(math.Inf(1))
should.Error(err)
_, err = jsoniter.Marshal(float32(math.Inf(1)))
should.Error(err)
_, err = jsoniter.Marshal(math.Inf(-1))
should.Error(err)
}
func Test_encode_nan(t *testing.T) {
should := require.New(t)
_, err := json.Marshal(math.NaN())
should.Error(err)
_, err = jsoniter.Marshal(float32(math.NaN()))
should.Error(err)
_, err = jsoniter.Marshal(math.NaN())
should.Error(err)
}
func Benchmark_jsoniter_float(b *testing.B) {
b.ReportAllocs()
input := []byte(`1.1123,`)
iter := jsoniter.NewIterator(jsoniter.ConfigDefault)
for n := 0; n < b.N; n++ {
iter.ResetBytes(input)
iter.ReadFloat64()
}
}
func Benchmark_json_float(b *testing.B) {
for n := 0; n < b.N; n++ {
result := float64(0)
json.Unmarshal([]byte(`1.1`), &result)
}
}

View File

@ -0,0 +1,192 @@
// +build go1.8
package misc_tests
import (
"bytes"
"encoding/json"
"io"
"io/ioutil"
"math/rand"
"strconv"
"testing"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
func Test_read_uint64_invalid(t *testing.T) {
should := require.New(t)
iter := jsoniter.ParseString(jsoniter.ConfigDefault, ",")
iter.ReadUint64()
should.NotNil(iter.Error)
}
func Test_read_int32_array(t *testing.T) {
should := require.New(t)
input := `[123,456,789]`
val := make([]int32, 0)
jsoniter.UnmarshalFromString(input, &val)
should.Equal(3, len(val))
}
func Test_read_int64_array(t *testing.T) {
should := require.New(t)
input := `[123,456,789]`
val := make([]int64, 0)
jsoniter.UnmarshalFromString(input, &val)
should.Equal(3, len(val))
}
func Test_wrap_int(t *testing.T) {
should := require.New(t)
str, err := jsoniter.MarshalToString(jsoniter.WrapInt64(100))
should.Nil(err)
should.Equal("100", str)
}
func Test_write_val_int(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := jsoniter.NewStream(jsoniter.ConfigDefault, buf, 4096)
stream.WriteVal(1001)
stream.Flush()
should.Nil(stream.Error)
should.Equal("1001", buf.String())
}
func Test_write_val_int_ptr(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := jsoniter.NewStream(jsoniter.ConfigDefault, buf, 4096)
val := 1001
stream.WriteVal(&val)
stream.Flush()
should.Nil(stream.Error)
should.Equal("1001", buf.String())
}
func Test_float_as_int(t *testing.T) {
should := require.New(t)
var i int
should.NotNil(jsoniter.Unmarshal([]byte(`1.1`), &i))
}
// chunkedData is io.Reader which returns random amount of data in range [1, chunkedData.chunkSize].
// It simulates chunked data on from HTTP server, which is commonly used by net/http package.
type chunkedData struct {
chunkSize int
data []byte
head int
}
// Read is implementation of the io.Reader which returns random amount of data in range [1, chunkedData.chunkSize].
func (c *chunkedData) Read(p []byte) (n int, err error) {
to := c.head + int(rand.Int31n(int32(c.chunkSize))+1)
// copy does not copy more data then p can consume
n = copy(p, c.data[c.head:to])
c.head = c.head + n
if c.head >= len(c.data) {
err = io.EOF
}
return n, err
}
// TestIterator_ReadInt_chunkedInput validates the behaviour of Iterator.ReadInt() method in where:
// - it reads data from io.Reader,
// - expected value is 0 (zero)
// - Iterator.tail == Iterator.head
// - Iterator.tail < len(Iterator.buf)
// - value in buffer after Iterator.tail is presented from previous read and has '.' character.
func TestIterator_ReadInt_chunkedInput(t *testing.T) {
should := require.New(t)
data := &chunkedData{
data: jsonFloatIntArray(t, 10),
}
// because this test is rely on randomness of chunkedData, we are doing multiple iterations to
// be sure, that we can hit a required case.
for data.chunkSize = 3; data.chunkSize <= len(data.data); data.chunkSize++ {
data.head = 0
iter := jsoniter.Parse(jsoniter.ConfigDefault, data, data.chunkSize)
i := 0
for iter.ReadArray() {
// every even item is float, let's just skip it.
if i%2 == 0 {
iter.Skip()
i++
continue
}
should.Zero(iter.ReadInt())
should.NoError(iter.Error)
i++
}
}
}
// jsonFloatIntArray generates JSON array where every
// - even item is float 0.1
// - odd item is integer 0
//
// [0.1, 0, 0.1, 0]
func jsonFloatIntArray(t *testing.T, numberOfItems int) []byte {
t.Helper()
numbers := make([]jsoniter.Any, numberOfItems)
for i := range numbers {
switch i % 2 {
case 0:
numbers[i] = jsoniter.WrapFloat64(0.1)
default:
numbers[i] = jsoniter.WrapInt64(0)
}
}
fixture, err := jsoniter.ConfigFastest.Marshal(numbers)
if err != nil {
panic(err)
}
b := &bytes.Buffer{}
require.NoError(
t,
json.Compact(b, fixture),
"json should be compactable",
)
return b.Bytes()
}
func Benchmark_jsoniter_encode_int(b *testing.B) {
stream := jsoniter.NewStream(jsoniter.ConfigDefault, ioutil.Discard, 64)
for n := 0; n < b.N; n++ {
stream.Reset(nil)
stream.WriteUint64(0xffffffff)
}
}
func Benchmark_itoa(b *testing.B) {
for n := 0; n < b.N; n++ {
strconv.FormatInt(0xffffffff, 10)
}
}
func Benchmark_jsoniter_int(b *testing.B) {
iter := jsoniter.NewIterator(jsoniter.ConfigDefault)
input := []byte(`100`)
for n := 0; n < b.N; n++ {
iter.ResetBytes(input)
iter.ReadInt64()
}
}
func Benchmark_json_int(b *testing.B) {
for n := 0; n < b.N; n++ {
result := int64(0)
json.Unmarshal([]byte(`-100`), &result)
}
}

View File

@ -0,0 +1,178 @@
package misc_tests
import (
"encoding/json"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"io"
"testing"
)
func Test_nil_non_empty_interface(t *testing.T) {
type TestObject struct {
Field []io.Closer
}
should := require.New(t)
obj := TestObject{}
b := []byte(`{"Field":["AAA"]}`)
should.NotNil(json.Unmarshal(b, &obj))
should.NotNil(jsoniter.Unmarshal(b, &obj))
}
func Test_nil_out_null_interface(t *testing.T) {
type TestData struct {
Field interface{} `json:"field"`
}
should := require.New(t)
var boolVar bool
obj := TestData{
Field: &boolVar,
}
data1 := []byte(`{"field": true}`)
err := jsoniter.Unmarshal(data1, &obj)
should.NoError(err)
should.Equal(true, *(obj.Field.(*bool)))
data2 := []byte(`{"field": null}`)
err = jsoniter.Unmarshal(data2, &obj)
should.NoError(err)
should.Nil(obj.Field)
// Checking stdlib behavior matches.
obj2 := TestData{
Field: &boolVar,
}
err = json.Unmarshal(data1, &obj2)
should.NoError(err)
should.Equal(true, *(obj2.Field.(*bool)))
err = json.Unmarshal(data2, &obj2)
should.NoError(err)
should.Equal(nil, obj2.Field)
}
func Test_overwrite_interface_ptr_value_with_nil(t *testing.T) {
type Wrapper struct {
Payload interface{} `json:"payload,omitempty"`
}
type Payload struct {
Value int `json:"val,omitempty"`
}
should := require.New(t)
payload := &Payload{}
wrapper := &Wrapper{
Payload: &payload,
}
err := json.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
should.NoError(err)
should.Equal(&payload, wrapper.Payload)
should.Equal(42, (*(wrapper.Payload.(**Payload))).Value)
err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper)
should.NoError(err)
should.Equal(&payload, wrapper.Payload)
should.Equal((*Payload)(nil), payload)
payload = &Payload{}
wrapper = &Wrapper{
Payload: &payload,
}
err = jsoniter.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
should.Equal(nil, err)
should.Equal(&payload, wrapper.Payload)
should.Equal(42, (*(wrapper.Payload.(**Payload))).Value)
err = jsoniter.Unmarshal([]byte(`{"payload": null}`), &wrapper)
should.NoError(err)
should.Equal(&payload, wrapper.Payload)
should.Equal((*Payload)(nil), payload)
}
func Test_overwrite_interface_value_with_nil(t *testing.T) {
type Wrapper struct {
Payload interface{} `json:"payload,omitempty"`
}
type Payload struct {
Value int `json:"val,omitempty"`
}
should := require.New(t)
payload := &Payload{}
wrapper := &Wrapper{
Payload: payload,
}
err := json.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
should.NoError(err)
should.Equal(42, wrapper.Payload.(*Payload).Value)
err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper)
should.NoError(err)
should.Equal(nil, wrapper.Payload)
should.Equal(42, payload.Value)
payload = &Payload{}
wrapper = &Wrapper{
Payload: payload,
}
err = jsoniter.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
should.Equal(nil, err)
should.Equal(42, wrapper.Payload.(*Payload).Value)
err = jsoniter.Unmarshal([]byte(`{"payload": null}`), &wrapper)
should.Equal(nil, err)
should.Equal(nil, wrapper.Payload)
should.Equal(42, payload.Value)
}
func Test_unmarshal_into_nil(t *testing.T) {
type Payload struct {
Value int `json:"val,omitempty"`
}
type Wrapper struct {
Payload interface{} `json:"payload,omitempty"`
}
should := require.New(t)
var payload *Payload
wrapper := &Wrapper{
Payload: payload,
}
err := json.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
should.NoError(err)
should.NotNil(wrapper.Payload)
should.Nil(payload)
err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper)
should.NoError(err)
should.Nil(wrapper.Payload)
should.Nil(payload)
payload = nil
wrapper = &Wrapper{
Payload: payload,
}
err = jsoniter.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
should.NoError(err)
should.NotNil(wrapper.Payload)
should.Nil(payload)
err = jsoniter.Unmarshal([]byte(`{"payload": null}`), &wrapper)
should.NoError(err)
should.Nil(wrapper.Payload)
should.Nil(payload)
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,52 @@
package misc_tests
import (
"encoding/json"
"math/big"
"testing"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"strings"
)
func Test_decode_TextMarshaler_key_map(t *testing.T) {
should := require.New(t)
var val map[*big.Float]string
should.Nil(jsoniter.UnmarshalFromString(`{"1":"2"}`, &val))
str, err := jsoniter.MarshalToString(val)
should.Nil(err)
should.Equal(`{"1":"2"}`, str)
}
func Test_read_map_with_reader(t *testing.T) {
should := require.New(t)
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}`
reader := strings.NewReader(input)
decoder := jsoniter.ConfigCompatibleWithStandardLibrary.NewDecoder(reader)
m1 := map[string]interface{}{}
should.Nil(decoder.Decode(&m1))
m2 := map[string]interface{}{}
should.Nil(json.Unmarshal([]byte(input), &m2))
should.Equal(m2, m1)
should.Equal("1.0.76", m1["note"].(map[string]interface{})["CoreServices"].(map[string]interface{})["version_name"])
}
func Test_map_eface_of_eface(t *testing.T) {
should := require.New(t)
json := jsoniter.ConfigCompatibleWithStandardLibrary
output, err := json.MarshalToString(map[interface{}]interface{}{
"1": 2,
3: "4",
})
should.NoError(err)
should.Equal(`{"1":2,"3":"4"}`, output)
}
func Test_encode_nil_map(t *testing.T) {
should := require.New(t)
var nilMap map[string]string
output, err := jsoniter.MarshalToString(nilMap)
should.NoError(err)
should.Equal(`null`, output)
}

View File

@ -0,0 +1,327 @@
package misc_tests
import (
"encoding/json"
"github.com/json-iterator/go"
"reflect"
"strings"
"testing"
)
type Level1 struct {
Hello []Level2
}
type Level2 struct {
World string
}
func Test_deep_nested(t *testing.T) {
type unstructured interface{}
testcases := []struct {
name string
data []byte
expectError string
}{
{
name: "array under maxDepth",
data: []byte(`{"a":` + strings.Repeat(`[`, 10000-1) + strings.Repeat(`]`, 10000-1) + `}`),
expectError: "",
},
{
name: "array over maxDepth",
data: []byte(`{"a":` + strings.Repeat(`[`, 10000) + strings.Repeat(`]`, 10000) + `}`),
expectError: "max depth",
},
{
name: "object under maxDepth",
data: []byte(`{"a":` + strings.Repeat(`{"a":`, 10000-1) + `0` + strings.Repeat(`}`, 10000-1) + `}`),
expectError: "",
},
{
name: "object over maxDepth",
data: []byte(`{"a":` + strings.Repeat(`{"a":`, 10000) + `0` + strings.Repeat(`}`, 10000) + `}`),
expectError: "max depth",
},
}
targets := []struct {
name string
new func() interface{}
}{
{
name: "unstructured",
new: func() interface{} {
var v interface{}
return &v
},
},
{
name: "typed named field",
new: func() interface{} {
v := struct {
A interface{} `json:"a"`
}{}
return &v
},
},
{
name: "typed missing field",
new: func() interface{} {
v := struct {
B interface{} `json:"b"`
}{}
return &v
},
},
{
name: "typed 1 field",
new: func() interface{} {
v := struct {
A interface{} `json:"a"`
}{}
return &v
},
},
{
name: "typed 2 field",
new: func() interface{} {
v := struct {
A interface{} `json:"a"`
B interface{} `json:"b"`
}{}
return &v
},
},
{
name: "typed 3 field",
new: func() interface{} {
v := struct {
A interface{} `json:"a"`
B interface{} `json:"b"`
C interface{} `json:"c"`
}{}
return &v
},
},
{
name: "typed 4 field",
new: func() interface{} {
v := struct {
A interface{} `json:"a"`
B interface{} `json:"b"`
C interface{} `json:"c"`
D interface{} `json:"d"`
}{}
return &v
},
},
{
name: "typed 5 field",
new: func() interface{} {
v := struct {
A interface{} `json:"a"`
B interface{} `json:"b"`
C interface{} `json:"c"`
D interface{} `json:"d"`
E interface{} `json:"e"`
}{}
return &v
},
},
{
name: "typed 6 field",
new: func() interface{} {
v := struct {
A interface{} `json:"a"`
B interface{} `json:"b"`
C interface{} `json:"c"`
D interface{} `json:"d"`
E interface{} `json:"e"`
F interface{} `json:"f"`
}{}
return &v
},
},
{
name: "typed 7 field",
new: func() interface{} {
v := struct {
A interface{} `json:"a"`
B interface{} `json:"b"`
C interface{} `json:"c"`
D interface{} `json:"d"`
E interface{} `json:"e"`
F interface{} `json:"f"`
G interface{} `json:"g"`
}{}
return &v
},
},
{
name: "typed 8 field",
new: func() interface{} {
v := struct {
A interface{} `json:"a"`
B interface{} `json:"b"`
C interface{} `json:"c"`
D interface{} `json:"d"`
E interface{} `json:"e"`
F interface{} `json:"f"`
G interface{} `json:"g"`
H interface{} `json:"h"`
}{}
return &v
},
},
{
name: "typed 9 field",
new: func() interface{} {
v := struct {
A interface{} `json:"a"`
B interface{} `json:"b"`
C interface{} `json:"c"`
D interface{} `json:"d"`
E interface{} `json:"e"`
F interface{} `json:"f"`
G interface{} `json:"g"`
H interface{} `json:"h"`
I interface{} `json:"i"`
}{}
return &v
},
},
{
name: "typed 10 field",
new: func() interface{} {
v := struct {
A interface{} `json:"a"`
B interface{} `json:"b"`
C interface{} `json:"c"`
D interface{} `json:"d"`
E interface{} `json:"e"`
F interface{} `json:"f"`
G interface{} `json:"g"`
H interface{} `json:"h"`
I interface{} `json:"i"`
J interface{} `json:"j"`
}{}
return &v
},
},
{
name: "typed 11 field",
new: func() interface{} {
v := struct {
A interface{} `json:"a"`
B interface{} `json:"b"`
C interface{} `json:"c"`
D interface{} `json:"d"`
E interface{} `json:"e"`
F interface{} `json:"f"`
G interface{} `json:"g"`
H interface{} `json:"h"`
I interface{} `json:"i"`
J interface{} `json:"j"`
K interface{} `json:"k"`
}{}
return &v
},
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
for _, target := range targets {
t.Run(target.name, func(t *testing.T) {
err := jsoniter.Unmarshal(tc.data, target.new())
if len(tc.expectError) == 0 {
if err != nil {
t.Errorf("unexpected error: %v", err)
}
} else {
if err == nil {
t.Errorf("expected error, got none")
} else if !strings.Contains(err.Error(), tc.expectError) {
t.Errorf("expected error containing '%s', got: %v", tc.expectError, err)
}
}
})
}
})
}
}
func Test_nested(t *testing.T) {
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `{"hello": [{"world": "value1"}, {"world": "value2"}]}`)
l1 := Level1{}
for l1Field := iter.ReadObject(); l1Field != ""; l1Field = iter.ReadObject() {
switch l1Field {
case "hello":
l2Array := []Level2{}
for iter.ReadArray() {
l2 := Level2{}
for l2Field := iter.ReadObject(); l2Field != ""; l2Field = iter.ReadObject() {
switch l2Field {
case "world":
l2.World = iter.ReadString()
default:
iter.ReportError("bind l2", "unexpected field: "+l2Field)
}
}
l2Array = append(l2Array, l2)
}
l1.Hello = l2Array
default:
iter.ReportError("bind l1", "unexpected field: "+l1Field)
}
}
if !reflect.DeepEqual(l1, Level1{
Hello: []Level2{
{World: "value1"},
{World: "value2"},
},
}) {
t.Fatal(l1)
}
}
func Benchmark_jsoniter_nested(b *testing.B) {
for n := 0; n < b.N; n++ {
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `{"hello": [{"world": "value1"}, {"world": "value2"}]}`)
l1 := Level1{}
for l1Field := iter.ReadObject(); l1Field != ""; l1Field = iter.ReadObject() {
switch l1Field {
case "hello":
l1.Hello = readLevel1Hello(iter)
default:
iter.Skip()
}
}
}
}
func readLevel1Hello(iter *jsoniter.Iterator) []Level2 {
l2Array := make([]Level2, 0, 2)
for iter.ReadArray() {
l2 := Level2{}
for l2Field := iter.ReadObject(); l2Field != ""; l2Field = iter.ReadObject() {
switch l2Field {
case "world":
l2.World = iter.ReadString()
default:
iter.Skip()
}
}
l2Array = append(l2Array, l2)
}
return l2Array
}
func Benchmark_json_nested(b *testing.B) {
for n := 0; n < b.N; n++ {
l1 := Level1{}
json.Unmarshal([]byte(`{"hello": [{"world": "value1"}, {"world": "value2"}]}`), &l1)
}
}

View File

@ -0,0 +1,81 @@
package misc_tests
import (
"bytes"
"io"
"testing"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
func Test_read_null(t *testing.T) {
should := require.New(t)
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `null`)
should.True(iter.ReadNil())
iter = jsoniter.ParseString(jsoniter.ConfigDefault, `null`)
should.Nil(iter.Read())
iter = jsoniter.ParseString(jsoniter.ConfigDefault, `navy`)
iter.Read()
should.True(iter.Error != nil && iter.Error != io.EOF)
iter = jsoniter.ParseString(jsoniter.ConfigDefault, `navy`)
iter.ReadNil()
should.True(iter.Error != nil && iter.Error != io.EOF)
}
func Test_write_null(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := jsoniter.NewStream(jsoniter.ConfigDefault, buf, 4096)
stream.WriteNil()
stream.Flush()
should.Nil(stream.Error)
should.Equal("null", buf.String())
}
func Test_decode_null_object_field(t *testing.T) {
should := require.New(t)
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[null,"a"]`)
iter.ReadArray()
if iter.ReadObject() != "" {
t.FailNow()
}
iter.ReadArray()
if iter.ReadString() != "a" {
t.FailNow()
}
type TestObject struct {
Field string
}
objs := []TestObject{}
should.Nil(jsoniter.UnmarshalFromString("[null]", &objs))
should.Len(objs, 1)
}
func Test_decode_null_array_element(t *testing.T) {
should := require.New(t)
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[null,"a"]`)
should.True(iter.ReadArray())
should.True(iter.ReadNil())
should.True(iter.ReadArray())
should.Equal("a", iter.ReadString())
}
func Test_decode_null_string(t *testing.T) {
should := require.New(t)
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[null,"a"]`)
should.True(iter.ReadArray())
should.Equal("", iter.ReadString())
should.True(iter.ReadArray())
should.Equal("a", iter.ReadString())
}
func Test_decode_null_skip(t *testing.T) {
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `[null,"a"]`)
iter.ReadArray()
iter.Skip()
iter.ReadArray()
if iter.ReadString() != "a" {
t.FailNow()
}
}

View File

@ -0,0 +1,372 @@
package misc_tests
import (
"bytes"
"reflect"
"testing"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"strings"
"time"
)
func Test_empty_object(t *testing.T) {
should := require.New(t)
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `{}`)
field := iter.ReadObject()
should.Equal("", field)
iter = jsoniter.ParseString(jsoniter.ConfigDefault, `{}`)
iter.ReadObjectCB(func(iter *jsoniter.Iterator, field string) bool {
should.FailNow("should not call")
return true
})
}
func Test_one_field(t *testing.T) {
should := require.New(t)
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `{"a": "stream"}`)
field := iter.ReadObject()
should.Equal("a", field)
value := iter.ReadString()
should.Equal("stream", value)
field = iter.ReadObject()
should.Equal("", field)
iter = jsoniter.ParseString(jsoniter.ConfigDefault, `{"a": "stream"}`)
should.True(iter.ReadObjectCB(func(iter *jsoniter.Iterator, field string) bool {
should.Equal("a", field)
iter.Skip()
return true
}))
}
func Test_two_field(t *testing.T) {
should := require.New(t)
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `{ "a": "stream" , "c": "d" }`)
field := iter.ReadObject()
should.Equal("a", field)
value := iter.ReadString()
should.Equal("stream", value)
field = iter.ReadObject()
should.Equal("c", field)
value = iter.ReadString()
should.Equal("d", value)
field = iter.ReadObject()
should.Equal("", field)
iter = jsoniter.ParseString(jsoniter.ConfigDefault, `{"field1": "1", "field2": 2}`)
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
switch field {
case "field1":
iter.ReadString()
case "field2":
iter.ReadInt64()
default:
iter.ReportError("bind object", "unexpected field")
}
}
}
func Test_write_object(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := jsoniter.NewStream(jsoniter.Config{IndentionStep: 2}.Froze(), buf, 4096)
stream.WriteObjectStart()
stream.WriteObjectField("hello")
stream.WriteInt(1)
stream.WriteMore()
stream.WriteObjectField("world")
stream.WriteInt(2)
stream.WriteObjectEnd()
stream.Flush()
should.Nil(stream.Error)
should.Equal("{\n \"hello\": 1,\n \"world\": 2\n}", buf.String())
}
func Test_reader_and_load_more(t *testing.T) {
should := require.New(t)
type TestObject struct {
CreatedAt time.Time
}
reader := strings.NewReader(`
{
"agency": null,
"candidateId": 0,
"candidate": "Blah Blah",
"bookingId": 0,
"shiftId": 1,
"shiftTypeId": 0,
"shift": "Standard",
"bonus": 0,
"bonusNI": 0,
"days": [],
"totalHours": 27,
"expenses": [],
"weekEndingDateSystem": "2016-10-09",
"weekEndingDateClient": "2016-10-09",
"submittedAt": null,
"submittedById": null,
"approvedAt": "2016-10-10T18:38:04Z",
"approvedById": 0,
"authorisedAt": "2016-10-10T18:38:04Z",
"authorisedById": 0,
"invoicedAt": "2016-10-10T20:00:00Z",
"revokedAt": null,
"revokedById": null,
"revokeReason": null,
"rejectedAt": null,
"rejectedById": null,
"rejectReasonCode": null,
"rejectReason": null,
"createdAt": "2016-10-03T00:00:00Z",
"updatedAt": "2016-11-09T10:26:13Z",
"updatedById": null,
"overrides": [],
"bookingApproverId": null,
"bookingApprover": null,
"status": "approved"
}
`)
decoder := jsoniter.ConfigCompatibleWithStandardLibrary.NewDecoder(reader)
obj := TestObject{}
should.Nil(decoder.Decode(&obj))
}
func Test_unmarshal_into_existing_value(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 int
Field2 interface{}
}
var obj TestObject
m := map[string]interface{}{}
obj.Field2 = &m
cfg := jsoniter.Config{UseNumber: true}.Froze()
err := cfg.Unmarshal([]byte(`{"Field1":1,"Field2":{"k":"v"}}`), &obj)
should.NoError(err)
should.Equal(map[string]interface{}{
"k": "v",
}, m)
}
// for issue421
func Test_unmarshal_anonymous_struct_invalid(t *testing.T) {
should := require.New(t)
t0 := struct {
Field1 string
}{}
cfg := jsoniter.ConfigCompatibleWithStandardLibrary
err := cfg.UnmarshalFromString(`{"Field1":`, &t0)
should.NotNil(err)
should.NotContains(err.Error(), reflect.TypeOf(t0).String())
cfgCaseSensitive := jsoniter.Config{
CaseSensitive: true,
}.Froze()
type TestObject1 struct {
Field1 struct {
InnerField1 string
}
}
t1 := TestObject1{}
err = cfgCaseSensitive.UnmarshalFromString(`{"Field1":{"InnerField1"`, &t1)
should.NotNil(err)
should.NotContains(err.Error(), reflect.TypeOf(t1.Field1).String())
should.Contains(err.Error(), reflect.TypeOf(t1).String())
type TestObject2 struct {
Field1 int
Field2 struct {
InnerField1 string
InnerField2 string
}
}
t2 := TestObject2{}
err = cfgCaseSensitive.UnmarshalFromString(`{"Field2":{"InnerField2"`, &t2)
should.NotNil(err)
should.NotContains(err.Error(), reflect.TypeOf(t2.Field2).String())
should.Contains(err.Error(), reflect.TypeOf(t2).String())
type TestObject3 struct {
Field1 int
Field2 int
Field3 struct {
InnerField1 string
InnerField2 string
InnerField3 string
}
}
t3 := TestObject3{}
err = cfgCaseSensitive.UnmarshalFromString(`{"Field3":{"InnerField3"`, &t3)
should.NotNil(err)
should.NotContains(err.Error(), reflect.TypeOf(t3.Field3).String())
should.Contains(err.Error(), reflect.TypeOf(t3).String())
type TestObject4 struct {
Field1 int
Field2 int
Field3 int
Field4 struct {
InnerField1 string
InnerField2 string
InnerField3 string
InnerField4 string
}
}
t4 := TestObject4{}
err = cfgCaseSensitive.UnmarshalFromString(`{"Field4":{"InnerField4"`, &t4)
should.NotNil(err)
should.NotContains(err.Error(), reflect.TypeOf(t4.Field4).String())
should.Contains(err.Error(), reflect.TypeOf(t4).String())
type TestObject5 struct {
Field1 int
Field2 int
Field3 int
Field4 int
Field5 struct {
InnerField1 string
InnerField2 string
InnerField3 string
InnerField4 string
InnerField5 string
}
}
t5 := TestObject5{}
err = cfgCaseSensitive.UnmarshalFromString(`{"Field5":{"InnerField5"`, &t5)
should.NotNil(err)
should.NotContains(err.Error(), reflect.TypeOf(t5.Field5).String())
should.Contains(err.Error(), reflect.TypeOf(t5).String())
type TestObject6 struct {
Field1 int
Field2 int
Field3 int
Field4 int
Field5 int
Field6 struct {
InnerField1 string
InnerField2 string
InnerField3 string
InnerField4 string
InnerField5 string
InnerField6 string
}
}
t6 := TestObject6{}
err = cfgCaseSensitive.UnmarshalFromString(`{"Field6":{"InnerField6"`, &t6)
should.NotNil(err)
should.NotContains(err.Error(), reflect.TypeOf(t6.Field6).String())
should.Contains(err.Error(), reflect.TypeOf(t6).String())
type TestObject7 struct {
Field1 int
Field2 int
Field3 int
Field4 int
Field5 int
Field6 int
Field7 struct {
InnerField1 string
InnerField2 string
InnerField3 string
InnerField4 string
InnerField5 string
InnerField6 string
InnerField7 string
}
}
t7 := TestObject7{}
err = cfgCaseSensitive.UnmarshalFromString(`{"Field7":{"InnerField7"`, &t7)
should.NotNil(err)
should.NotContains(err.Error(), reflect.TypeOf(t7.Field7).String())
should.Contains(err.Error(), reflect.TypeOf(t7).String())
type TestObject8 struct {
Field1 int
Field2 int
Field3 int
Field4 int
Field5 int
Field6 int
Field7 int
Field8 struct {
InnerField1 string
InnerField2 string
InnerField3 string
InnerField4 string
InnerField5 string
InnerField6 string
InnerField7 string
InnerField8 string
}
}
t8 := TestObject8{}
err = cfgCaseSensitive.UnmarshalFromString(`{"Field8":{"InnerField8"`, &t8)
should.NotNil(err)
should.NotContains(err.Error(), reflect.TypeOf(t8.Field8).String())
should.Contains(err.Error(), reflect.TypeOf(t8).String())
type TestObject9 struct {
Field1 int
Field2 int
Field3 int
Field4 int
Field5 int
Field6 int
Field7 int
Field8 int
Field9 struct {
InnerField1 string
InnerField2 string
InnerField3 string
InnerField4 string
InnerField5 string
InnerField6 string
InnerField7 string
InnerField8 string
InnerField9 string
}
}
t9 := TestObject9{}
err = cfgCaseSensitive.UnmarshalFromString(`{"Field9":{"InnerField9"`, &t9)
should.NotNil(err)
should.NotContains(err.Error(), reflect.TypeOf(t9.Field9).String())
should.Contains(err.Error(), reflect.TypeOf(t9).String())
type TestObject10 struct {
Field1 int
Field2 int
Field3 int
Field4 int
Field5 int
Field6 int
Field7 int
Field8 int
Field9 int
Field10 struct {
InnerField1 string
InnerField2 string
InnerField3 string
InnerField4 string
InnerField5 string
InnerField6 string
InnerField7 string
InnerField8 string
InnerField9 string
InnerField10 string
}
}
t10 := TestObject10{}
err = cfgCaseSensitive.UnmarshalFromString(`{"Field10":{"InnerField10"`, &t10)
should.NotNil(err)
should.NotContains(err.Error(), reflect.TypeOf(t10.Field10).String())
should.Contains(err.Error(), reflect.TypeOf(t10).String())
err = cfg.UnmarshalFromString(`{"Field10":{"InnerField10"`, &t10)
should.NotNil(err)
should.NotContains(err.Error(), reflect.TypeOf(t10.Field10).String())
should.Contains(err.Error(), reflect.TypeOf(t10).String())
}

View File

@ -0,0 +1,87 @@
package misc_tests
import (
"encoding/json"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"strings"
"testing"
)
func Test_jsoniter_RawMessage(t *testing.T) {
should := require.New(t)
var data jsoniter.RawMessage
should.Nil(jsoniter.Unmarshal([]byte(`[1,2,3]`), &data))
should.Equal(`[1,2,3]`, string(data))
str, err := jsoniter.MarshalToString(data)
should.Nil(err)
should.Equal(`[1,2,3]`, str)
}
func Test_encode_map_of_jsoniter_raw_message(t *testing.T) {
should := require.New(t)
type RawMap map[string]*jsoniter.RawMessage
value := jsoniter.RawMessage("[]")
rawMap := RawMap{"hello": &value}
output, err := jsoniter.MarshalToString(rawMap)
should.Nil(err)
should.Equal(`{"hello":[]}`, output)
}
func Test_marshal_invalid_json_raw_message(t *testing.T) {
type A struct {
Raw json.RawMessage `json:"raw"`
}
message := []byte(`{}`)
a := A{}
should := require.New(t)
should.Nil(jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(message, &a))
aout, aouterr := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(&a)
should.Equal(`{"raw":null}`, string(aout))
should.Nil(aouterr)
}
func Test_marshal_nil_json_raw_message(t *testing.T) {
type A struct {
Nil1 jsoniter.RawMessage `json:"raw1"`
Nil2 json.RawMessage `json:"raw2"`
}
a := A{}
should := require.New(t)
aout, aouterr := jsoniter.Marshal(&a)
should.Equal(`{"raw1":null,"raw2":null}`, string(aout))
should.Nil(aouterr)
a.Nil1 = []byte(`Any`)
a.Nil2 = []byte(`Any`)
should.Nil(jsoniter.Unmarshal(aout, &a))
should.Nil(a.Nil1)
should.Nil(a.Nil2)
}
func Test_raw_message_memory_not_copied_issue(t *testing.T) {
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"}}`
type IteratorObject struct {
Name *string `json:"name"`
BundleId *string `json:"bundle_id"`
AppCategory *string `json:"app_category"`
AppPlatform *string `json:"app_platform"`
BudgetDay *float32 `json:"budget_day"`
BiddingMax *float32 `json:"bidding_max"`
BiddingMin *float32 `json:"bidding_min"`
BiddingType *string `json:"bidding_type"`
Freq *jsoniter.RawMessage `json:"freq"`
Targeting *jsoniter.RawMessage `json:"targeting"`
Url *jsoniter.RawMessage `json:"url"`
Speed *int `json:"speed" db:"speed"`
}
obj := &IteratorObject{}
decoder := jsoniter.NewDecoder(strings.NewReader(jsonStream))
err := decoder.Decode(obj)
should := require.New(t)
should.Nil(err)
should.Equal(`{"open":true,"type":"day","num":100}`, string(*obj.Freq))
}

42
pool.go Normal file
View File

@ -0,0 +1,42 @@
package jsoniter
import (
"io"
)
// IteratorPool a thread safe pool of iterators with same configuration
type IteratorPool interface {
BorrowIterator(data []byte) *Iterator
ReturnIterator(iter *Iterator)
}
// StreamPool a thread safe pool of streams with same configuration
type StreamPool interface {
BorrowStream(writer io.Writer) *Stream
ReturnStream(stream *Stream)
}
func (cfg *frozenConfig) BorrowStream(writer io.Writer) *Stream {
stream := cfg.streamPool.Get().(*Stream)
stream.Reset(writer)
return stream
}
func (cfg *frozenConfig) ReturnStream(stream *Stream) {
stream.out = nil
stream.Error = nil
stream.Attachment = nil
cfg.streamPool.Put(stream)
}
func (cfg *frozenConfig) BorrowIterator(data []byte) *Iterator {
iter := cfg.iteratorPool.Get().(*Iterator)
iter.ResetBytes(data)
return iter
}
func (cfg *frozenConfig) ReturnIterator(iter *Iterator) {
iter.Error = nil
iter.Attachment = nil
cfg.iteratorPool.Put(iter)
}

Some files were not shown because too many files have changed in this diff Show More