multi_json 1.15.0 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +100 -103
- data/CONTRIBUTING.md +1 -1
- data/LICENSE.md +1 -1
- data/README.md +19 -27
- data/lib/multi_json/adapter.rb +21 -14
- data/lib/multi_json/adapter_error.rb +5 -7
- data/lib/multi_json/adapters/gson.rb +2 -3
- data/lib/multi_json/adapters/jr_jackson.rb +3 -3
- data/lib/multi_json/adapters/json_gem.rb +26 -3
- data/lib/multi_json/adapters/json_pure.rb +2 -6
- data/lib/multi_json/adapters/oj.rb +31 -31
- data/lib/multi_json/adapters/ok_json.rb +3 -3
- data/lib/multi_json/adapters/yajl.rb +3 -3
- data/lib/multi_json/convertible_hash_keys.rb +29 -21
- data/lib/multi_json/options.rb +23 -13
- data/lib/multi_json/options_cache.rb +43 -19
- data/lib/multi_json/parse_error.rb +8 -8
- data/lib/multi_json/vendor/okjson.rb +145 -206
- data/lib/multi_json/version.rb +2 -2
- data/lib/multi_json.rb +59 -48
- metadata +15 -51
- data/lib/multi_json/adapters/json_common.rb +0 -23
- data/lib/multi_json/adapters/nsjsonserialization.rb +0 -35
@@ -1,11 +1,34 @@
|
|
1
|
-
|
2
|
-
require
|
1
|
+
require_relative "../adapter"
|
2
|
+
require "json"
|
3
3
|
|
4
4
|
module MultiJson
|
5
5
|
module Adapters
|
6
6
|
# Use the JSON gem to dump/load.
|
7
|
-
class JsonGem <
|
7
|
+
class JsonGem < Adapter
|
8
8
|
ParseError = ::JSON::ParserError
|
9
|
+
|
10
|
+
defaults :load, create_additions: false, quirks_mode: true
|
11
|
+
|
12
|
+
PRETTY_STATE_PROTOTYPE = {
|
13
|
+
indent: " ",
|
14
|
+
space: " ",
|
15
|
+
object_nl: "\n",
|
16
|
+
array_nl: "\n"
|
17
|
+
}.freeze
|
18
|
+
private_constant :PRETTY_STATE_PROTOTYPE
|
19
|
+
|
20
|
+
def load(string, options = {})
|
21
|
+
string = string.dup.force_encoding(Encoding::UTF_8) if string.encoding != Encoding::UTF_8
|
22
|
+
|
23
|
+
options[:symbolize_names] = true if options.delete(:symbolize_keys)
|
24
|
+
::JSON.parse(string, options)
|
25
|
+
end
|
26
|
+
|
27
|
+
def dump(object, options = {})
|
28
|
+
options.merge!(PRETTY_STATE_PROTOTYPE) if options.delete(:pretty)
|
29
|
+
|
30
|
+
object.to_json(options)
|
31
|
+
end
|
9
32
|
end
|
10
33
|
end
|
11
34
|
end
|
@@ -1,11 +1,7 @@
|
|
1
|
-
|
2
|
-
require 'multi_json/adapters/json_common'
|
1
|
+
require_relative "json_gem"
|
3
2
|
|
4
3
|
module MultiJson
|
5
4
|
module Adapters
|
6
|
-
|
7
|
-
class JsonPure < JsonCommon
|
8
|
-
ParseError = ::JSON::ParserError
|
9
|
-
end
|
5
|
+
JsonPure = JsonGem
|
10
6
|
end
|
11
7
|
end
|
@@ -1,13 +1,12 @@
|
|
1
|
-
require
|
2
|
-
|
3
|
-
require 'multi_json/adapter'
|
1
|
+
require "oj"
|
2
|
+
require_relative "../adapter"
|
4
3
|
|
5
4
|
module MultiJson
|
6
5
|
module Adapters
|
7
6
|
# Use the Oj library to dump/load.
|
8
7
|
class Oj < Adapter
|
9
|
-
defaults :load, :
|
10
|
-
defaults :dump, :
|
8
|
+
defaults :load, mode: :strict, symbolize_keys: false
|
9
|
+
defaults :dump, mode: :compat, time_format: :ruby, use_to_json: true
|
11
10
|
|
12
11
|
# In certain cases OJ gem may throw JSON::ParserError exception instead
|
13
12
|
# of its own class. Also, we can't expect ::JSON::ParserError and
|
@@ -18,14 +17,10 @@ module MultiJson
|
|
18
17
|
# (at least for now).
|
19
18
|
class ParseError < ::SyntaxError
|
20
19
|
WRAPPED_CLASSES = %w[Oj::ParseError JSON::ParserError].to_set.freeze
|
20
|
+
private_constant :WRAPPED_CLASSES
|
21
21
|
|
22
22
|
def self.===(exception)
|
23
|
-
|
24
|
-
when ::SyntaxError
|
25
|
-
true
|
26
|
-
else
|
27
|
-
WRAPPED_CLASSES.include?(exception.class.to_s)
|
28
|
-
end
|
23
|
+
exception.is_a?(::SyntaxError) || WRAPPED_CLASSES.include?(exception.class.to_s)
|
29
24
|
end
|
30
25
|
end
|
31
26
|
|
@@ -34,29 +29,34 @@ module MultiJson
|
|
34
29
|
::Oj.load(string, options)
|
35
30
|
end
|
36
31
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
end
|
44
|
-
when /\A3\./
|
32
|
+
OJ_VERSION = ::Oj::VERSION
|
33
|
+
OJ_V2 = OJ_VERSION.start_with?("2.")
|
34
|
+
OJ_V3 = OJ_VERSION.start_with?("3.")
|
35
|
+
private_constant :OJ_VERSION, :OJ_V2, :OJ_V3
|
36
|
+
|
37
|
+
if OJ_V3
|
45
38
|
PRETTY_STATE_PROTOTYPE = {
|
46
|
-
:
|
47
|
-
:
|
48
|
-
:
|
49
|
-
:
|
50
|
-
:
|
51
|
-
:
|
52
|
-
}
|
53
|
-
|
54
|
-
|
39
|
+
indent: " ",
|
40
|
+
space: " ",
|
41
|
+
space_before: "",
|
42
|
+
object_nl: "\n",
|
43
|
+
array_nl: "\n",
|
44
|
+
ascii_only: false
|
45
|
+
}.freeze
|
46
|
+
private_constant :PRETTY_STATE_PROTOTYPE
|
47
|
+
end
|
48
|
+
|
49
|
+
def dump(object, options = {})
|
50
|
+
if OJ_V2
|
51
|
+
options[:indent] = 2 if options[:pretty]
|
52
|
+
options[:indent] = options[:indent].to_i if options[:indent]
|
53
|
+
elsif OJ_V3
|
55
54
|
options.merge!(PRETTY_STATE_PROTOTYPE.dup) if options.delete(:pretty)
|
56
|
-
|
55
|
+
else
|
56
|
+
raise "Unsupported Oj version: #{::Oj::VERSION}"
|
57
57
|
end
|
58
|
-
|
59
|
-
|
58
|
+
|
59
|
+
::Oj.dump(object, options)
|
60
60
|
end
|
61
61
|
end
|
62
62
|
end
|
@@ -1,6 +1,6 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
require_relative "../adapter"
|
2
|
+
require_relative "../convertible_hash_keys"
|
3
|
+
require_relative "../vendor/okjson"
|
4
4
|
|
5
5
|
module MultiJson
|
6
6
|
module Adapters
|
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
|
1
|
+
require "yajl"
|
2
|
+
require_relative "../adapter"
|
3
3
|
|
4
4
|
module MultiJson
|
5
5
|
module Adapters
|
@@ -8,7 +8,7 @@ module MultiJson
|
|
8
8
|
ParseError = ::Yajl::ParseError
|
9
9
|
|
10
10
|
def load(string, options = {})
|
11
|
-
::Yajl::Parser.new(:
|
11
|
+
::Yajl::Parser.new(symbolize_keys: options[:symbolize_keys]).parse(string)
|
12
12
|
end
|
13
13
|
|
14
14
|
def dump(object, options = {})
|
@@ -1,6 +1,9 @@
|
|
1
1
|
module MultiJson
|
2
2
|
module ConvertibleHashKeys
|
3
|
-
|
3
|
+
SIMPLE_OBJECT_CLASSES = [String, Numeric, TrueClass, FalseClass, NilClass].freeze
|
4
|
+
private_constant :SIMPLE_OBJECT_CLASSES
|
5
|
+
|
6
|
+
private
|
4
7
|
|
5
8
|
def symbolize_keys(hash)
|
6
9
|
prepare_hash(hash) do |key|
|
@@ -14,30 +17,35 @@ module MultiJson
|
|
14
17
|
end
|
15
18
|
end
|
16
19
|
|
17
|
-
def prepare_hash(
|
18
|
-
|
19
|
-
case hash
|
20
|
+
def prepare_hash(value, &)
|
21
|
+
case value
|
20
22
|
when Array
|
21
|
-
|
22
|
-
prepare_hash(value, &key_modifier)
|
23
|
-
end
|
23
|
+
handle_array(value, &)
|
24
24
|
when Hash
|
25
|
-
|
26
|
-
new_key = key_modifier.call(key)
|
27
|
-
new_value = prepare_hash(value, &key_modifier)
|
28
|
-
result.merge! new_key => new_value
|
29
|
-
end
|
30
|
-
when String, Numeric, true, false, nil
|
31
|
-
hash
|
25
|
+
handle_hash(value, &)
|
32
26
|
else
|
33
|
-
|
34
|
-
hash
|
35
|
-
elsif hash.respond_to?(:to_s)
|
36
|
-
hash.to_s
|
37
|
-
else
|
38
|
-
hash
|
39
|
-
end
|
27
|
+
handle_simple_objects(value)
|
40
28
|
end
|
41
29
|
end
|
30
|
+
|
31
|
+
def handle_simple_objects(obj)
|
32
|
+
return obj if simple_object?(obj) || obj.respond_to?(:to_json)
|
33
|
+
|
34
|
+
obj.respond_to?(:to_s) ? obj.to_s : obj
|
35
|
+
end
|
36
|
+
|
37
|
+
def handle_array(array, &key_modifier)
|
38
|
+
array.map { |value| prepare_hash(value, &key_modifier) }
|
39
|
+
end
|
40
|
+
|
41
|
+
def handle_hash(original_hash, &key_modifier)
|
42
|
+
original_hash.each_with_object({}) do |(key, value), result|
|
43
|
+
result[key_modifier.call(key)] = prepare_hash(value, &key_modifier)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def simple_object?(obj)
|
48
|
+
SIMPLE_OBJECT_CLASSES.any? { |klass| obj.is_a?(klass) }
|
49
|
+
end
|
42
50
|
end
|
43
51
|
end
|
data/lib/multi_json/options.rb
CHANGED
@@ -10,30 +10,40 @@ module MultiJson
|
|
10
10
|
@dump_options = options
|
11
11
|
end
|
12
12
|
|
13
|
-
def load_options(*
|
14
|
-
defined?(@load_options) && get_options(@load_options, *
|
13
|
+
def load_options(*)
|
14
|
+
(defined?(@load_options) && get_options(@load_options, *)) || default_load_options
|
15
15
|
end
|
16
16
|
|
17
|
-
def dump_options(*
|
18
|
-
defined?(@dump_options) && get_options(@dump_options, *
|
17
|
+
def dump_options(*)
|
18
|
+
(defined?(@dump_options) && get_options(@dump_options, *)) || default_dump_options
|
19
19
|
end
|
20
20
|
|
21
21
|
def default_load_options
|
22
|
-
@default_load_options ||= {}
|
22
|
+
@default_load_options ||= {}.freeze
|
23
23
|
end
|
24
24
|
|
25
25
|
def default_dump_options
|
26
|
-
@default_dump_options ||= {}
|
26
|
+
@default_dump_options ||= {}.freeze
|
27
27
|
end
|
28
28
|
|
29
|
-
|
29
|
+
private
|
30
30
|
|
31
|
-
def get_options(options, *
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
31
|
+
def get_options(options, *)
|
32
|
+
return handle_callable_options(options, *) if options_callable?(options)
|
33
|
+
|
34
|
+
handle_hashable_options(options)
|
35
|
+
end
|
36
|
+
|
37
|
+
def options_callable?(options)
|
38
|
+
options.respond_to?(:call)
|
39
|
+
end
|
40
|
+
|
41
|
+
def handle_callable_options(options, *)
|
42
|
+
options.arity.zero? ? options.call : options.call(*)
|
43
|
+
end
|
44
|
+
|
45
|
+
def handle_hashable_options(options)
|
46
|
+
options.respond_to?(:to_hash) ? options.to_hash : nil
|
37
47
|
end
|
38
48
|
end
|
39
49
|
end
|
@@ -1,29 +1,53 @@
|
|
1
1
|
module MultiJson
|
2
2
|
module OptionsCache
|
3
|
-
|
3
|
+
class Store
|
4
|
+
# Normally MultiJson is used with a few option sets for both dump/load
|
5
|
+
# methods. When options are generated dynamically though, every call would
|
6
|
+
# cause a cache miss and the cache would grow indefinitely. To prevent
|
7
|
+
# this, we just reset the cache every time the number of keys outgrows
|
8
|
+
# 1000.
|
9
|
+
MAX_CACHE_SIZE = 1000
|
10
|
+
private_constant :MAX_CACHE_SIZE
|
4
11
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
12
|
+
def initialize
|
13
|
+
@cache = {}
|
14
|
+
@mutex = Mutex.new
|
15
|
+
end
|
9
16
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
17
|
+
def reset
|
18
|
+
@mutex.synchronize do
|
19
|
+
@cache = {}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def fetch(key, &)
|
24
|
+
@mutex.synchronize do
|
25
|
+
return @cache[key] if @cache.key?(key)
|
26
|
+
end
|
14
27
|
|
15
|
-
|
28
|
+
value = yield
|
16
29
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
30
|
+
@mutex.synchronize do
|
31
|
+
if @cache.key?(key)
|
32
|
+
# We ran into a race condition, keep the existing value
|
33
|
+
@cache[key]
|
34
|
+
else
|
35
|
+
@cache.clear if @cache.size >= MAX_CACHE_SIZE
|
36
|
+
@cache[key] = value
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class << self
|
43
|
+
attr_reader :dump, :load
|
23
44
|
|
24
|
-
|
25
|
-
|
26
|
-
|
45
|
+
def reset
|
46
|
+
@dump = Store.new
|
47
|
+
@load = Store.new
|
48
|
+
end
|
27
49
|
end
|
50
|
+
|
51
|
+
reset
|
28
52
|
end
|
29
53
|
end
|
@@ -1,15 +1,15 @@
|
|
1
1
|
module MultiJson
|
2
2
|
class ParseError < StandardError
|
3
|
-
attr_reader :data
|
3
|
+
attr_reader :data
|
4
|
+
|
5
|
+
def initialize(message = nil, data: nil, cause: nil)
|
6
|
+
super(message)
|
7
|
+
@data = data
|
8
|
+
set_backtrace(cause.backtrace) if cause
|
9
|
+
end
|
4
10
|
|
5
11
|
def self.build(original_exception, data)
|
6
|
-
new(original_exception.message
|
7
|
-
exception.instance_eval do
|
8
|
-
@cause = original_exception
|
9
|
-
set_backtrace original_exception.backtrace
|
10
|
-
@data = data
|
11
|
-
end
|
12
|
-
end
|
12
|
+
new(original_exception.message, data: data, cause: original_exception)
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|