multi_json 1.5.1 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/.travis.yml +2 -1
- data/.yardopts +6 -0
- data/CHANGELOG.md +116 -0
- data/CONTRIBUTING.md +46 -0
- data/Gemfile +28 -4
- data/LICENSE.md +1 -1
- data/README.md +31 -15
- data/Rakefile +2 -10
- data/lib/multi_json.rb +113 -106
- data/lib/multi_json/adapters/gson.rb +20 -0
- data/lib/multi_json/adapters/json_common.rb +18 -8
- data/lib/multi_json/adapters/json_gem.rb +1 -1
- data/lib/multi_json/adapters/json_pure.rb +1 -1
- data/lib/multi_json/adapters/nsjsonserialization.rb +4 -3
- data/lib/multi_json/adapters/oj.rb +9 -7
- data/lib/multi_json/adapters/ok_json.rb +22 -12
- data/lib/multi_json/adapters/yajl.rb +5 -3
- data/lib/multi_json/version.rb +1 -1
- data/multi_json.gemspec +20 -21
- data/spec/adapter_shared_example.rb +55 -20
- data/spec/helper.rb +20 -14
- data/spec/json_common_shared_example.rb +34 -0
- data/spec/multi_json_spec.rb +57 -24
- metadata +44 -61
- metadata.gz.sig +0 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'gson' unless defined?(::Gson)
|
2
|
+
|
3
|
+
module MultiJson
|
4
|
+
module Adapters
|
5
|
+
# Use the gson.rb library to dump/load.
|
6
|
+
module Gson
|
7
|
+
extend self
|
8
|
+
|
9
|
+
ParseError = ::Gson::DecodeError
|
10
|
+
|
11
|
+
def load(string, options={}) #:nodoc:
|
12
|
+
::Gson::Decoder.new(options).decode(string)
|
13
|
+
end
|
14
|
+
|
15
|
+
def dump(object, options={}) #:nodoc:
|
16
|
+
::Gson::Encoder.new(options).encode(object)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -1,23 +1,33 @@
|
|
1
1
|
module MultiJson
|
2
2
|
module Adapters
|
3
3
|
module JsonCommon
|
4
|
-
|
5
4
|
def load(string, options={})
|
6
5
|
string = string.read if string.respond_to?(:read)
|
7
|
-
::JSON.parse(string,
|
6
|
+
::JSON.parse("[#{string}]", process_load_options!(options)).first
|
8
7
|
end
|
9
8
|
|
10
9
|
def dump(object, options={})
|
11
|
-
|
10
|
+
::JSON.generate([object], process_dump_options!(options)).strip[1..-2]
|
12
11
|
end
|
13
12
|
|
14
13
|
protected
|
15
14
|
|
16
|
-
def
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
15
|
+
def process_load_options!(options={})
|
16
|
+
process_options!({:create_additions => false}, options) do |opts|
|
17
|
+
opts.merge!(:symbolize_names => true) if options.delete(:symbolize_keys)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def process_dump_options!(options={})
|
22
|
+
process_options!({}, options) do |opts|
|
23
|
+
opts.merge!(::JSON::PRETTY_STATE_PROTOTYPE.to_h) if options.delete(:pretty)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def process_options!(default_options, options)
|
28
|
+
return default_options if options.empty?
|
29
|
+
yield default_options
|
30
|
+
default_options.merge!(options)
|
21
31
|
end
|
22
32
|
|
23
33
|
end
|
@@ -3,10 +3,11 @@ require 'multi_json/adapters/ok_json'
|
|
3
3
|
|
4
4
|
module MultiJson
|
5
5
|
module Adapters
|
6
|
-
|
6
|
+
module Nsjsonserialization
|
7
|
+
extend self, MultiJson::Adapters::OkJson
|
7
8
|
ParseError = ::MultiJson::OkJson::Error
|
8
9
|
|
9
|
-
def
|
10
|
+
def load(string, options={})
|
10
11
|
string = string.read if string.respond_to?(:read)
|
11
12
|
data = string.dataUsingEncoding(NSUTF8StringEncoding)
|
12
13
|
object = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingMutableContainers | NSJSONReadingMutableLeaves, error: nil)
|
@@ -18,7 +19,7 @@ module MultiJson
|
|
18
19
|
end
|
19
20
|
end
|
20
21
|
|
21
|
-
def
|
22
|
+
def dump(object, options={})
|
22
23
|
pretty = options[:pretty] ? NSJSONWritingPrettyPrinted : 0
|
23
24
|
object = object.as_json if object.respond_to?(:as_json)
|
24
25
|
if NSJSONSerialization.isValidJSONObject(object)
|
@@ -3,24 +3,26 @@ require 'oj' unless defined?(::Oj)
|
|
3
3
|
module MultiJson
|
4
4
|
module Adapters
|
5
5
|
# Use the Oj library to dump/load.
|
6
|
-
|
6
|
+
module Oj
|
7
|
+
extend self
|
8
|
+
|
9
|
+
DEFAULT_OPTIONS = {:mode => :compat, :time_format => :ruby}.freeze
|
10
|
+
|
7
11
|
ParseError = if defined?(::Oj::ParseError)
|
8
12
|
::Oj::ParseError
|
9
13
|
else
|
10
14
|
SyntaxError
|
11
15
|
end
|
12
16
|
|
13
|
-
|
14
|
-
|
15
|
-
def self.load(string, options={}) #:nodoc:
|
17
|
+
def load(string, options={}) #:nodoc:
|
16
18
|
options.merge!(:symbol_keys => options[:symbolize_keys])
|
17
19
|
options[:mode] = :strict
|
18
|
-
::Oj.load(string, options)
|
20
|
+
::Oj.load(string, DEFAULT_OPTIONS.merge(options))
|
19
21
|
end
|
20
22
|
|
21
|
-
def
|
23
|
+
def dump(object, options={}) #:nodoc:
|
22
24
|
options.merge!(:indent => 2) if options[:pretty]
|
23
|
-
::Oj.dump(object, options)
|
25
|
+
::Oj.dump(object, DEFAULT_OPTIONS.merge(options))
|
24
26
|
end
|
25
27
|
end
|
26
28
|
end
|
@@ -2,45 +2,55 @@ require 'multi_json/vendor/okjson'
|
|
2
2
|
|
3
3
|
module MultiJson
|
4
4
|
module Adapters
|
5
|
-
|
5
|
+
module OkJson
|
6
|
+
extend self
|
7
|
+
|
6
8
|
ParseError = ::MultiJson::OkJson::Error
|
7
9
|
|
8
|
-
def
|
10
|
+
def load(string, options={}) #:nodoc:
|
9
11
|
string = string.read if string.respond_to?(:read)
|
10
12
|
result = ::MultiJson::OkJson.decode("[#{string}]").first
|
11
13
|
options[:symbolize_keys] ? symbolize_keys(result) : result
|
12
14
|
end
|
13
15
|
|
14
|
-
def
|
16
|
+
def dump(object, options={}) #:nodoc:
|
15
17
|
::MultiJson::OkJson.valenc(stringify_keys(object))
|
16
18
|
end
|
17
19
|
|
18
|
-
def
|
19
|
-
|
20
|
+
def symbolize_keys(object) #:nodoc:
|
21
|
+
prepare_object(object) do |key|
|
20
22
|
key.is_a?(String) ? key.to_sym : key
|
21
23
|
end
|
22
24
|
end
|
23
25
|
|
24
|
-
def
|
25
|
-
|
26
|
+
def stringify_keys(object) #:nodoc:
|
27
|
+
prepare_object(object) do |key|
|
26
28
|
key.respond_to?(:to_s) ? key.to_s : key
|
27
29
|
end
|
28
30
|
end
|
29
31
|
|
30
|
-
def
|
32
|
+
def prepare_object(object, &key_modifier) #:nodoc:
|
31
33
|
case object
|
32
34
|
when Array
|
33
35
|
object.map do |value|
|
34
|
-
|
36
|
+
prepare_object(value, &key_modifier)
|
35
37
|
end
|
36
38
|
when Hash
|
37
39
|
object.inject({}) do |result, (key, value)|
|
38
|
-
new_key =
|
39
|
-
new_value =
|
40
|
+
new_key = key_modifier.call(key)
|
41
|
+
new_value = prepare_object(value, &key_modifier)
|
40
42
|
result.merge! new_key => new_value
|
41
43
|
end
|
42
|
-
|
44
|
+
when String, Numeric, true, false, nil
|
43
45
|
object
|
46
|
+
else
|
47
|
+
if object.respond_to?(:to_json)
|
48
|
+
object
|
49
|
+
elsif object.respond_to?(:to_s)
|
50
|
+
object.to_s
|
51
|
+
else
|
52
|
+
object
|
53
|
+
end
|
44
54
|
end
|
45
55
|
end
|
46
56
|
end
|
@@ -3,14 +3,16 @@ require 'yajl' unless defined?(::Yajl)
|
|
3
3
|
module MultiJson
|
4
4
|
module Adapters
|
5
5
|
# Use the Yajl-Ruby library to dump/load.
|
6
|
-
|
6
|
+
module Yajl
|
7
|
+
extend self
|
8
|
+
|
7
9
|
ParseError = ::Yajl::ParseError
|
8
10
|
|
9
|
-
def
|
11
|
+
def load(string, options={}) #:nodoc:
|
10
12
|
::Yajl::Parser.new(:symbolize_keys => options[:symbolize_keys]).parse(string)
|
11
13
|
end
|
12
14
|
|
13
|
-
def
|
15
|
+
def dump(object, options={}) #:nodoc:
|
14
16
|
::Yajl::Encoder.encode(object, options)
|
15
17
|
end
|
16
18
|
end
|
data/lib/multi_json/version.rb
CHANGED
data/multi_json.gemspec
CHANGED
@@ -1,23 +1,22 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'multi_json/version'
|
3
5
|
|
4
|
-
Gem::Specification.new do |
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
gem.
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
gem.summary = %q{A gem to provide swappable JSON backends.}
|
21
|
-
gem.test_files = Dir['spec/**/*']
|
22
|
-
gem.version = MultiJson::VERSION
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.add_development_dependency 'bundler', '~> 1.0'
|
8
|
+
spec.authors = ["Michael Bleigh", "Josh Kalderimis", "Erik Michaels-Ober", "Pavel Pravosud"]
|
9
|
+
spec.cert_chain = %w(certs/sferik.pem)
|
10
|
+
spec.description = %q{A gem to provide easy switching between different JSON backends, including Oj, Yajl, the JSON gem (with C-extensions), the pure-Ruby JSON gem, and OkJson.}
|
11
|
+
spec.email = ['michael@intridea.com', 'josh.kalderimis@gmail.com', 'sferik@gmail.com']
|
12
|
+
spec.files = Dir['.yardopts', 'CHANGELOG.md', 'CONTRIBUTING.md', 'LICENSE.md', 'README.md', 'Rakefile', 'multi_json.gemspec', 'Gemfile', '.document', '.rspec', '.travis.yml' ,'spec/**/*', 'lib/**/*']
|
13
|
+
spec.homepage = 'http://github.com/intridea/multi_json'
|
14
|
+
spec.licenses = ['MIT']
|
15
|
+
spec.name = 'multi_json'
|
16
|
+
spec.require_paths = ['lib']
|
17
|
+
spec.required_rubygems_version = '>= 1.3.6'
|
18
|
+
spec.signing_key = File.expand_path("~/.gem/private_key.pem") if $0 =~ /gem\z/
|
19
|
+
spec.summary = %q{A gem to provide swappable JSON backends.}
|
20
|
+
spec.test_files = Dir['spec/**/*']
|
21
|
+
spec.version = MultiJson::VERSION
|
23
22
|
end
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
shared_examples_for 'an adapter' do |adapter|
|
2
4
|
|
3
5
|
before do
|
4
6
|
begin
|
@@ -12,12 +14,31 @@ shared_examples_for "an adapter" do |adapter|
|
|
12
14
|
it 'writes decodable JSON' do
|
13
15
|
[
|
14
16
|
{'abc' => 'def'},
|
15
|
-
[1, 2, 3,
|
17
|
+
[1, 2, 3, '4', true, false, nil]
|
16
18
|
].each do |example|
|
17
19
|
expect(MultiJson.load(MultiJson.dump(example))).to eq example
|
18
20
|
end
|
19
21
|
end
|
20
22
|
|
23
|
+
unless 'json_pure' == adapter || 'json_gem' == adapter
|
24
|
+
it 'dumps time in correct format' do
|
25
|
+
time = Time.at(1355218745).utc
|
26
|
+
|
27
|
+
# time does not respond to to_json method
|
28
|
+
class << time
|
29
|
+
undef_method :to_json
|
30
|
+
end
|
31
|
+
|
32
|
+
dumped_json = MultiJson.dump(time)
|
33
|
+
expected = if RUBY_VERSION > '1.9'
|
34
|
+
'2012-12-11 09:39:05 UTC'
|
35
|
+
else
|
36
|
+
'Tue Dec 11 09:39:05 UTC 2012'
|
37
|
+
end
|
38
|
+
expect(MultiJson.load(dumped_json)).to eq expected
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
21
42
|
it 'dumps symbol and fixnum keys as strings' do
|
22
43
|
[
|
23
44
|
[
|
@@ -43,8 +64,8 @@ shared_examples_for "an adapter" do |adapter|
|
|
43
64
|
end
|
44
65
|
|
45
66
|
it 'dumps rootless JSON' do
|
46
|
-
expect(MultiJson.dump(
|
47
|
-
expect(MultiJson.dump(123)).to eq
|
67
|
+
expect(MultiJson.dump('random rootless string')).to eq '"random rootless string"'
|
68
|
+
expect(MultiJson.dump(123)).to eq '123'
|
48
69
|
end
|
49
70
|
|
50
71
|
it 'passes options to the adapter' do
|
@@ -52,24 +73,26 @@ shared_examples_for "an adapter" do |adapter|
|
|
52
73
|
MultiJson.dump('foo', :bar => :baz)
|
53
74
|
end
|
54
75
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
76
|
+
# This behavior is currently not supported by gson.rb
|
77
|
+
# See discussion at https://github.com/intridea/multi_json/pull/71
|
78
|
+
unless adapter == 'gson'
|
79
|
+
it 'dumps custom objects that implement to_json' do
|
80
|
+
klass = Class.new do
|
81
|
+
def to_json(*)
|
82
|
+
'"foobar"'
|
83
|
+
end
|
61
84
|
end
|
85
|
+
expect(MultiJson.dump(klass.new)).to eq '"foobar"'
|
62
86
|
end
|
63
87
|
end
|
64
88
|
|
65
|
-
it '
|
66
|
-
expect(MultiJson.dump(TimeWithZone.new)).to eq "\"2005-02-01T15:15:10Z\""
|
67
|
-
end
|
68
|
-
|
69
|
-
it 'allow to dump JSON values' do
|
89
|
+
it 'allows to dump JSON values' do
|
70
90
|
expect(MultiJson.dump(42)).to eq '42'
|
71
91
|
end
|
72
92
|
|
93
|
+
it 'allows to dump JSON with UTF-8 characters' do
|
94
|
+
expect(MultiJson.dump({'color' => 'żółć'})).to eq('{"color":"żółć"}')
|
95
|
+
end
|
73
96
|
end
|
74
97
|
|
75
98
|
describe '.load' do
|
@@ -77,11 +100,20 @@ shared_examples_for "an adapter" do |adapter|
|
|
77
100
|
expect(MultiJson.load('{"abc":"def"}')).to eq({'abc' => 'def'})
|
78
101
|
end
|
79
102
|
|
80
|
-
it 'raises MultiJson::
|
81
|
-
expect{MultiJson.load('{"abc"}')}.to raise_error(MultiJson::
|
103
|
+
it 'raises MultiJson::LoadError on invalid JSON' do
|
104
|
+
expect{MultiJson.load('{"abc"}')}.to raise_error(MultiJson::LoadError)
|
82
105
|
end
|
83
106
|
|
84
|
-
it 'raises MultiJson::
|
107
|
+
it 'raises MultiJson::LoadError with data on invalid JSON' do
|
108
|
+
data = '{invalid}'
|
109
|
+
begin
|
110
|
+
MultiJson.load(data)
|
111
|
+
rescue MultiJson::LoadError => le
|
112
|
+
expect(le.data).to eq data
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'catches MultiJson::DecodeError for legacy support' do
|
85
117
|
data = '{invalid}'
|
86
118
|
begin
|
87
119
|
MultiJson.load(data)
|
@@ -92,7 +124,7 @@ shared_examples_for "an adapter" do |adapter|
|
|
92
124
|
|
93
125
|
it 'stringifys symbol keys when encoding' do
|
94
126
|
dumped_json = MultiJson.dump(:a => 1, :b => {:c => 2})
|
95
|
-
expect(MultiJson.load(dumped_json)).to eq({
|
127
|
+
expect(MultiJson.load(dumped_json)).to eq({'a' => 1, 'b' => {'c' => 2}})
|
96
128
|
end
|
97
129
|
|
98
130
|
it 'properly loads valid JSON in StringIOs' do
|
@@ -119,9 +151,12 @@ shared_examples_for "an adapter" do |adapter|
|
|
119
151
|
end
|
120
152
|
end
|
121
153
|
|
122
|
-
it '
|
154
|
+
it 'allows to load JSON values' do
|
123
155
|
expect(MultiJson.load('42')).to eq 42
|
124
156
|
end
|
125
157
|
|
158
|
+
it 'allows to load JSON with UTF-8 characters' do
|
159
|
+
expect(MultiJson.load('{"color":"żółć"}')).to eq({'color' => 'żółć'})
|
160
|
+
end
|
126
161
|
end
|
127
162
|
end
|
data/spec/helper.rb
CHANGED
@@ -1,15 +1,7 @@
|
|
1
|
-
|
2
|
-
defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
|
3
|
-
end
|
4
|
-
|
5
|
-
def macruby?
|
6
|
-
defined?(RUBY_ENGINE) && RUBY_ENGINE == 'macruby'
|
7
|
-
end
|
8
|
-
|
9
|
-
unless ENV['CI'] || macruby?
|
1
|
+
if !ENV['CI'] && defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ruby'
|
10
2
|
require 'simplecov'
|
11
3
|
SimpleCov.start do
|
12
|
-
add_filter '
|
4
|
+
add_filter 'vendor'
|
13
5
|
end
|
14
6
|
end
|
15
7
|
|
@@ -22,6 +14,14 @@ RSpec.configure do |config|
|
|
22
14
|
end
|
23
15
|
end
|
24
16
|
|
17
|
+
def macruby?
|
18
|
+
defined?(RUBY_ENGINE) && RUBY_ENGINE == 'macruby'
|
19
|
+
end
|
20
|
+
|
21
|
+
def jruby?
|
22
|
+
defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
|
23
|
+
end
|
24
|
+
|
25
25
|
class MockDecoder
|
26
26
|
def self.load(string, options={})
|
27
27
|
{'abc' => 'def'}
|
@@ -32,8 +32,14 @@ class MockDecoder
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
module MockModuleDecoder
|
36
|
+
extend self
|
37
|
+
|
38
|
+
def load(string, options={})
|
39
|
+
{'abc' => 'def'}
|
38
40
|
end
|
39
|
-
|
41
|
+
|
42
|
+
def dump(string)
|
43
|
+
'{"abc":"def"}'
|
44
|
+
end
|
45
|
+
end
|