yajl-ruby 1.3.0 → 1.4.1

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.

Potentially problematic release.


This version of yajl-ruby might be problematic. Click here for more details.

data/ext/yajl/yajl_ext.h CHANGED
@@ -53,10 +53,10 @@ static rb_encoding *utf8Encoding;
53
53
  #define RARRAY_LEN(s) (RARRAY(s)->len)
54
54
  #endif
55
55
 
56
- static VALUE cParseError, cEncodeError, mYajl, cParser, cEncoder;
56
+ static VALUE cStandardError, cParseError, cEncodeError, mYajl, cParser, cProjector, cEncoder;
57
57
  static ID intern_io_read, intern_call, intern_keys, intern_to_s,
58
58
  intern_to_json, intern_has_key, intern_to_sym, intern_as_json;
59
- static ID sym_allow_comments, sym_check_utf8, sym_pretty, sym_indent, sym_terminator, sym_symbolize_keys, sym_symbolize_names, sym_html_safe;
59
+ static ID sym_allow_comments, sym_check_utf8, sym_pretty, sym_indent, sym_terminator, sym_symbolize_keys, sym_symbolize_names, sym_html_safe, sym_entities;
60
60
 
61
61
  #define GetParser(obj, sval) Data_Get_Struct(obj, yajl_parser_wrapper, sval);
62
62
  #define GetEncoder(obj, sval) Data_Get_Struct(obj, yajl_encoder_wrapper, sval);
data/ext/yajl/yajl_lex.c CHANGED
@@ -38,29 +38,25 @@
38
38
  #include <assert.h>
39
39
  #include <string.h>
40
40
 
41
- #ifdef YAJL_LEXER_DEBUG
42
- static const char *
43
- tokToStr(yajl_tok tok)
44
- {
41
+ const char *yajl_tok_name(yajl_tok tok) {
45
42
  switch (tok) {
46
43
  case yajl_tok_bool: return "bool";
47
44
  case yajl_tok_colon: return "colon";
48
45
  case yajl_tok_comma: return "comma";
49
46
  case yajl_tok_eof: return "eof";
50
47
  case yajl_tok_error: return "error";
51
- case yajl_tok_left_brace: return "brace";
52
- case yajl_tok_left_bracket: return "bracket";
48
+ case yajl_tok_left_brace: return "open_array";
49
+ case yajl_tok_left_bracket: return "open_object";
53
50
  case yajl_tok_null: return "null";
54
51
  case yajl_tok_integer: return "integer";
55
52
  case yajl_tok_double: return "double";
56
- case yajl_tok_right_brace: return "brace";
57
- case yajl_tok_right_bracket: return "bracket";
53
+ case yajl_tok_right_brace: return "close_array";
54
+ case yajl_tok_right_bracket: return "close_object";
58
55
  case yajl_tok_string: return "string";
59
56
  case yajl_tok_string_with_escapes: return "string_with_escapes";
60
57
  }
61
58
  return "unknown";
62
59
  }
63
- #endif
64
60
 
65
61
  /* Impact of the stream parsing feature on the lexer:
66
62
  *
@@ -740,6 +736,10 @@ yajl_tok yajl_lex_peek(yajl_lexer lexer, const unsigned char * jsonText,
740
736
  tok = yajl_lex_lex(lexer, jsonText, jsonTextLen, &offset,
741
737
  &outBuf, &outLen);
742
738
 
739
+ if (tok == yajl_tok_eof) {
740
+ return tok;
741
+ }
742
+
743
743
  lexer->bufOff = bufOff;
744
744
  lexer->bufInUse = bufInUse;
745
745
  yajl_buf_truncate(lexer->buf, bufLen);
data/ext/yajl/yajl_lex.h CHANGED
@@ -36,33 +36,34 @@
36
36
  #include "api/yajl_common.h"
37
37
 
38
38
  typedef enum {
39
- yajl_tok_bool,
40
- yajl_tok_colon,
41
- yajl_tok_comma,
42
- yajl_tok_eof,
43
- yajl_tok_error,
44
- yajl_tok_left_brace,
45
- yajl_tok_left_bracket,
46
- yajl_tok_null,
47
- yajl_tok_right_brace,
48
- yajl_tok_right_bracket,
39
+ yajl_tok_bool, // 0
40
+ yajl_tok_colon, // 1
41
+ yajl_tok_comma, // 2
42
+ yajl_tok_eof, // 3
43
+ yajl_tok_error, // 4
44
+ yajl_tok_left_brace, // 5
45
+ yajl_tok_left_bracket, // 6
46
+ yajl_tok_null, // 7
47
+ yajl_tok_right_brace, // 8
48
+ yajl_tok_right_bracket, // 9
49
49
 
50
50
  /* we differentiate between integers and doubles to allow the
51
51
  * parser to interpret the number without re-scanning */
52
- yajl_tok_integer,
53
- yajl_tok_double,
52
+ yajl_tok_integer, // 10
53
+ yajl_tok_double, // 11
54
54
 
55
55
  /* we differentiate between strings which require further processing,
56
56
  * and strings that do not */
57
- yajl_tok_string,
58
- yajl_tok_string_with_escapes,
57
+ yajl_tok_string, // 12
58
+ yajl_tok_string_with_escapes, // 13
59
59
 
60
60
  /* comment tokens are not currently returned to the parser, ever */
61
- yajl_tok_comment
61
+ yajl_tok_comment // 14
62
62
  } yajl_tok;
63
63
 
64
64
  typedef struct yajl_lexer_t * yajl_lexer;
65
65
 
66
+ const char *yajl_tok_name(yajl_tok tok);
66
67
 
67
68
  YAJL_API
68
69
  yajl_lexer yajl_lex_alloc(yajl_alloc_funcs * alloc,
data/lib/yajl.rb CHANGED
@@ -23,6 +23,13 @@ module Yajl
23
23
  Encoder.encode(obj, args, &block)
24
24
  end
25
25
 
26
+ class Projector
27
+ def initialize(stream, read_bufsize=4096)
28
+ @stream = stream
29
+ @buffer_size = read_bufsize
30
+ end
31
+ end
32
+
26
33
  class Parser
27
34
  # A helper method for parse-and-forget use-cases
28
35
  #
data/lib/yajl/bzip2.rb CHANGED
@@ -6,6 +6,6 @@ begin
6
6
  require 'bzip2' unless defined?(Bzip2)
7
7
  require 'yajl/bzip2/stream_reader.rb'
8
8
  require 'yajl/bzip2/stream_writer.rb'
9
- rescue LoadError => e
9
+ rescue LoadError
10
10
  raise "Unable to load the bzip2 library. Is the bzip2-ruby gem installed?"
11
- end
11
+ end
@@ -4,6 +4,7 @@ require 'socket'
4
4
  require 'yajl'
5
5
  require 'yajl/version' unless defined? Yajl::VERSION
6
6
  require 'uri'
7
+ require 'cgi'
7
8
 
8
9
  module Yajl
9
10
  # This module is for making HTTP requests to which the response bodies (and possibly requests in the near future)
@@ -101,7 +102,7 @@ module Yajl
101
102
  default_headers["Content-Type"] = opts["Content-Type"] || "application/x-www-form-urlencoded"
102
103
  body = opts.delete(:body)
103
104
  if body.is_a?(Hash)
104
- body = body.keys.collect {|param| "#{URI.escape(param.to_s)}=#{URI.escape(body[param].to_s)}"}.join('&')
105
+ body = body.keys.collect {|param| "#{CGI.escape(param.to_s)}=#{CGI.escape(body[param].to_s)}"}.join('&')
105
106
  end
106
107
  default_headers["Content-Length"] = body.length
107
108
  end
@@ -161,7 +162,7 @@ module Yajl
161
162
  if block_given?
162
163
  chunkLeft = 0
163
164
  while !socket.eof? && (line = socket.gets)
164
- break if line.match /^0.*?\r\n/
165
+ break if line.match(/^0.*?\r\n/)
165
166
  next if line == "\r\n"
166
167
  size = line.hex
167
168
  json = socket.read(size)
data/lib/yajl/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Yajl
2
- VERSION = '1.3.0'
2
+ VERSION = '1.4.1'
3
3
  end
@@ -275,11 +275,27 @@ describe "Yajl JSON encoder" do
275
275
  expect(safe_encoder.encode("</script>")).to eql("\"<\\/script>\"")
276
276
  end
277
277
 
278
+ it "should not encode characters with entities by default" do
279
+ expect(Yajl.dump("\u2028\u2029><&")).to eql("\"\u2028\u2029><&\"")
280
+ end
281
+
282
+ it "should encode characters with entities when enabled" do
283
+ expect(Yajl.dump("\u2028\u2029><&", entities: true)).to eql("\"\\u2028\\u2029\\u003E\\u003C\\u0026\"")
284
+ end
285
+
278
286
  it "should default to *not* escaping / characters" do
279
287
  unsafe_encoder = Yajl::Encoder.new
280
288
  expect(unsafe_encoder.encode("</script>")).not_to eql("\"<\\/script>\"")
281
289
  end
282
290
 
291
+ it "should encode slashes when enabled" do
292
+ unsafe_encoder = Yajl::Encoder.new(:entities => false)
293
+ safe_encoder = Yajl::Encoder.new(:entities => true)
294
+
295
+ expect(unsafe_encoder.encode("</script>")).not_to eql("\"<\\/script>\"")
296
+ expect(safe_encoder.encode("</script>")).to eql("\"\\u003C\\/script\\u003E\"")
297
+ end
298
+
283
299
  it "return value of #to_json must be a string" do
284
300
  expect {
285
301
  Yajl::Encoder.encode(TheMindKiller.new)
@@ -2,6 +2,13 @@
2
2
  require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
3
3
 
4
4
  describe "One-off JSON examples" do
5
+ it "should not blow up with a bad surrogate trailer" do
6
+ # https://github.com/brianmario/yajl-ruby/issues/176
7
+ bad_json = "{\"e\":{\"\\uD800\\\\DC00\":\"a\"}}"
8
+
9
+ Yajl::Parser.new.parse(bad_json)
10
+ end
11
+
5
12
  it "should parse 23456789012E666 and return Infinity" do
6
13
  infinity = (1.0/0)
7
14
  silence_warnings do
@@ -0,0 +1,41 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
2
+
3
+ require 'benchmark'
4
+ require 'benchmark/memory'
5
+
6
+ describe "file projection" do
7
+ it "projects file streams" do
8
+ schema = {
9
+ "forced" => nil,
10
+ "created" => nil,
11
+ "pusher" => {
12
+ "name" => nil,
13
+ },
14
+ "repository" => {
15
+ "name" => nil,
16
+ "full_name" => nil,
17
+ },
18
+ "ref" => nil,
19
+ "compare" => nil,
20
+ "commits" => {
21
+ "distinct" => nil,
22
+ "message" => nil,
23
+ "url" => nil,
24
+ "id" => nil,
25
+ "author" => {
26
+ "username" => nil,
27
+ }
28
+ }
29
+ }
30
+
31
+ file_path = ENV['JSON_FILE']
32
+ if file_path.nil? || file_path.empty?
33
+ return
34
+ end
35
+
36
+ Benchmark.memory { |x|
37
+ x.report("project (yajl)") { Yajl::Projector.new(File.open(file_path, 'r')).project(schema) }
38
+ x.compare!
39
+ }
40
+ end
41
+ end
@@ -0,0 +1,498 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
2
+
3
+ require 'stringio'
4
+ require 'json'
5
+
6
+ describe "projection" do
7
+ it "should work" do
8
+ stream = StringIO.new('{"name": "keith", "age": 27}')
9
+ projector = Yajl::Projector.new(stream)
10
+ projection = projector.project({"name" => nil})
11
+ expect(projection['name']).to eql("keith")
12
+ end
13
+
14
+ it "should filter" do
15
+ stream = StringIO.new('{"name": "keith", "age": 27}')
16
+ projector = Yajl::Projector.new(stream)
17
+ projection = projector.project({"name" => nil})
18
+ expect(projection['age']).to eql(nil)
19
+ end
20
+
21
+ it "should raise an exception and not leak memory" do
22
+ stream = StringIO.new('foo')
23
+ projector = Yajl::Projector.new(stream)
24
+ expect {
25
+ projector.project({"name" => nil})
26
+ }.to raise_error(Yajl::ParseError)
27
+ end
28
+
29
+ it "should raise an exception and not segv" do
30
+ stream = StringIO.new('[,,,,]')
31
+ projector = Yajl::Projector.new(stream)
32
+ expect {
33
+ projector.project({"name" => nil})
34
+ }.to raise_error(Yajl::ParseError)
35
+ end
36
+
37
+ it "should raise an exception and not segv on colons" do
38
+ stream = StringIO.new('[::::]')
39
+ projector = Yajl::Projector.new(stream)
40
+ expect {
41
+ projector.project({"name" => nil})
42
+ }.to raise_error(Yajl::ParseError)
43
+ end
44
+
45
+ it "should behave the same way as the regular parser on bad tokens like comma" do
46
+ bad_json = '{"name": "keith", "age":, 27}'
47
+ stream = StringIO.new(bad_json)
48
+ projector = Yajl::Projector.new(stream)
49
+ expect {
50
+ projector.project({"name" => nil})
51
+ }.to raise_error(capture_exception_for(bad_json).class)
52
+ end
53
+
54
+ it "should behave the same way as the regular parser on bad tokens like colon" do
55
+ bad_json = '{"name": "keith", "age":: 27}'
56
+ stream = StringIO.new(bad_json)
57
+ projector = Yajl::Projector.new(stream)
58
+ expect {
59
+ projector.project({"name" => nil})
60
+ }.to raise_error(capture_exception_for(bad_json).class)
61
+ end
62
+
63
+ it "should behave the same way as the regular parser on not enough json" do
64
+ bad_json = '{"name": "keith", "age":'
65
+ stream = StringIO.new(bad_json)
66
+ projector = Yajl::Projector.new(stream)
67
+ expect {
68
+ projector.project({"name" => nil})
69
+ }.to raise_error(capture_exception_for(bad_json).class)
70
+ end
71
+
72
+ def capture_exception_for(bad_json)
73
+ Yajl::Parser.new.parse(bad_json)
74
+ rescue Exception => e
75
+ e
76
+ end
77
+
78
+ def project(schema, over: "", json: nil, stream: nil)
79
+ if stream.nil?
80
+ if json.nil?
81
+ json = over.to_json
82
+ end
83
+
84
+ stream = StringIO.new(json)
85
+ end
86
+
87
+ Yajl::Projector.new(stream).project(schema)
88
+ end
89
+
90
+ it "filters arrays" do
91
+ json = {
92
+ "users" => [
93
+ {
94
+ "name" => "keith",
95
+ "company" => "internet plumbing inc",
96
+ "department" => "janitorial",
97
+ },
98
+ {
99
+ "name" => "justin",
100
+ "company" => "big blue",
101
+ "department" => "programming?",
102
+ },
103
+ {
104
+ "name" => "alan",
105
+ "company" => "different colour of blue",
106
+ "department" => "drop bear containment",
107
+ }
108
+ ]
109
+ }.to_json
110
+
111
+ puts json
112
+
113
+ schema = {
114
+ # /users is an array of objects, each having many keys we only want name
115
+ "users" => {
116
+ "name" => nil,
117
+ }
118
+ }
119
+
120
+ expect(project(schema, json: json)).to eql({
121
+ "users" => [
122
+ { "name" => "keith" },
123
+ { "name" => "justin" },
124
+ { "name" => "alan" }
125
+ ]
126
+ })
127
+ end
128
+
129
+ it "filters top level arrays" do
130
+ json = [
131
+ {
132
+ "name" => "keith",
133
+ "personal detail" => "thing",
134
+ },
135
+ {
136
+ "name" => "cory",
137
+ "phone number" => "unknown",
138
+ }
139
+ ]
140
+
141
+ schema = {
142
+ "name" => nil,
143
+ }
144
+
145
+ expect(project(schema, over: json)).to eql([
146
+ { "name" => "keith" },
147
+ { "name" => "cory" },
148
+ ])
149
+ end
150
+
151
+ it "filters nested schemas" do
152
+ json = {
153
+ "foo" => 42,
154
+
155
+ "bar" => {
156
+ "name" => "keith",
157
+ "occupation" => "professional computering",
158
+ "age" => 26,
159
+ "hobbies" => [
160
+ "not computering",
161
+ ]
162
+ },
163
+
164
+ "qux" => {
165
+ "quux" => [
166
+ {
167
+ "name" => "Reactive X",
168
+ "members" => "many",
169
+ },
170
+ {
171
+ "name" => "lstoll",
172
+ "members" => "such",
173
+ },
174
+ {
175
+ "name" => "github",
176
+ "members" => "very",
177
+ },
178
+ {
179
+ "name" => "theleague",
180
+ "members" => "numerous",
181
+ }
182
+ ],
183
+
184
+ "corge" => {
185
+ "name" => "Brighton",
186
+ "address" =>"Buckingham Road",
187
+ },
188
+ },
189
+
190
+ "grault" => nil,
191
+
192
+ "waldo" => true,
193
+ }
194
+
195
+ schema = {
196
+ # include the /foo subtree (is a single number)
197
+ "foo" => nil,
198
+
199
+ # ignore the bar subtree (is an object)
200
+ # "bar" => ???
201
+
202
+ # include some of the /qux subtree (is an object)
203
+ "qux" => {
204
+ # include the whole /qux/quux subtree (is an array of objects)
205
+ "quux" => nil,
206
+
207
+ # include some of the /qux/corge subtree (is another object)
208
+ "corge" => {
209
+ # include name (is a string)
210
+ "name" => nil,
211
+ # include age (is missing from source doc)
212
+ "age" => nil,
213
+ # ignore address
214
+ # "address" => ???
215
+ },
216
+ },
217
+
218
+ # include the /grault subtree (is a null literal)
219
+ "grault" => nil,
220
+
221
+ # include the /waldo subtree (is a boolean literal)
222
+ "waldo" => nil,
223
+ }
224
+
225
+ expect(project(schema, over: json)).to eql({
226
+ "foo" => 42,
227
+
228
+ "qux" => {
229
+ "quux" => [
230
+ {
231
+ "name" => "Reactive X",
232
+ "members" => "many",
233
+ },
234
+ {
235
+ "name" => "lstoll",
236
+ "members" => "such",
237
+ },
238
+ {
239
+ "name" => "github",
240
+ "members" => "very",
241
+ },
242
+ {
243
+ "name" => "theleague",
244
+ "members" => "numerous",
245
+ }
246
+ ],
247
+
248
+ "corge" => {
249
+ "name" => "Brighton",
250
+ },
251
+ },
252
+
253
+ "grault" => nil,
254
+
255
+ "waldo" => true,
256
+ })
257
+ end
258
+
259
+ it "supports incompatible schemas" do
260
+ json = {
261
+ # surprise! the json doesn't include an object under the foo key
262
+ "foo" => 42,
263
+ }
264
+
265
+ schema = {
266
+ # include some of the /foo subtree
267
+ "foo" => {
268
+ # include the whole /foo/baz subtree
269
+ "baz" => nil,
270
+ }
271
+ }
272
+
273
+ # expect the 42 to be pulled out
274
+ expect(project(schema, over: json)).to eql({
275
+ "foo" => 42
276
+ })
277
+ end
278
+
279
+ it "supports nil schema" do
280
+ json = {
281
+ "foo" => "bar",
282
+ }
283
+
284
+ expect(project(nil, over: json)).to eql({
285
+ "foo" => "bar"
286
+ })
287
+ end
288
+
289
+ it "supports empty schema" do
290
+ json = {
291
+ "foo" => "bar",
292
+ }
293
+ expect(project({}, over: json)).to eql({})
294
+ end
295
+
296
+ it "supports object projection" do
297
+ json = {
298
+ "foo" => "bar",
299
+ "qux" => "quux",
300
+ }
301
+
302
+ schema = {
303
+ "foo" => nil,
304
+ }
305
+
306
+ expect(project(schema, over: json)).to eql({
307
+ "foo" => "bar"
308
+ })
309
+ end
310
+
311
+ it "projects the readme example" do
312
+ json = <<-EOJ
313
+ [
314
+ {
315
+ "user": {
316
+ "name": "keith",
317
+ "age": 26,
318
+ "jobs": [
319
+ {
320
+ "title": "director of overworking",
321
+ "company": "south coast software",
322
+ "department": "most"
323
+ },
324
+ {
325
+ "title": "some kind of computering",
326
+ "company": "github the website dot com",
327
+ "department": true
328
+ }
329
+ ]
330
+ },
331
+ "another key": {
332
+
333
+ },
334
+ "woah this document is huge": {
335
+
336
+ },
337
+ "many megabytes": {
338
+
339
+ },
340
+ "etc": {
341
+
342
+ }
343
+ }
344
+ ]
345
+ EOJ
346
+
347
+ schema = {
348
+ "user" => {
349
+ "name" => nil,
350
+ "jobs" => {
351
+ "title" => nil,
352
+ },
353
+ },
354
+ }
355
+
356
+ expect(project(schema, json: json)).to eql([{
357
+ "user" => {
358
+ "name" => "keith",
359
+ "jobs" => [
360
+ { "title" => "director of overworking" },
361
+ { "title" => "some kind of computering" },
362
+ ]
363
+ }
364
+ }])
365
+ end
366
+
367
+ it "errors with invalid json" do
368
+ expect {
369
+ project({"b" => nil}, json: '{"a":, "b": 2}')
370
+ }.to raise_error(StandardError)
371
+ end
372
+
373
+ it "errors with ignored unbalanced object syntax" do
374
+ expect {
375
+ project({"b" => nil}, json: '{"a": {{, "b": 2}')
376
+ }.to raise_error(StandardError)
377
+ end
378
+
379
+ it "errors with accepted unbalanced object tokens" do
380
+ expect {
381
+ project({"a" => nil}, json: '{"a": {"b": 2}')
382
+ }.to raise_error(Yajl::ParseError)
383
+ end
384
+
385
+ it "errors when projecting if an object comma is missing" do
386
+ expect {
387
+ project({"a" => nil}, json: '{"a": 1 "b": 2}')
388
+ }.to raise_error(Yajl::ParseError)
389
+ end
390
+
391
+ it "errors when building if an object comma is missing" do
392
+ expect {
393
+ project(nil, json: '{"a": {"b": 2 "c": 3}}')
394
+ }.to raise_error(Yajl::ParseError)
395
+ end
396
+
397
+ it "errors when eof instead of simple value" do
398
+ expect {
399
+ project(nil, json: '[')
400
+ }.to raise_error(Yajl::ParseError)
401
+ end
402
+
403
+ it "errors when arrays don't have a comma between elements" do
404
+ expect {
405
+ project(nil, json: '[1 2]')
406
+ }.to raise_error(Yajl::ParseError)
407
+ end
408
+
409
+ it "supports parsing empty array" do
410
+ expect(project(nil, json: '[]')).to eql([])
411
+ end
412
+
413
+ it "supports parsing empty object" do
414
+ expect(project(nil, json: '{}')).to eql({})
415
+ end
416
+
417
+ it "reads a full buffer" do
418
+ json = "[" + "1,"*2046 + "1 ]"
419
+ expect(json.size).to eql(4096)
420
+ expect(project(nil, json: json)).to eql(Array.new(2047, 1))
421
+ end
422
+
423
+ it "reads into a second buffer" do
424
+ json = "[" + "1,"*2047 + "1 ]"
425
+ expect(json.size).to eql(4098)
426
+ expect(JSON.parse(json)).to eql(Array.new(2048, 1))
427
+ expect(project(nil, json: json)).to eql(Array.new(2048, 1))
428
+ end
429
+
430
+ it "supports parsing big strings" do
431
+ json = [
432
+ "a",
433
+ "b"*10_000,
434
+ "c",
435
+ ]
436
+ expect(project(nil, over: json)).to eql(json)
437
+ end
438
+
439
+ it "supports bigger read buffers" do
440
+ json = {
441
+ "a"*10_000 => "b"*10_000
442
+ }.to_json
443
+ stream = StringIO.new(json)
444
+ expect(Yajl::Projector.new(stream, 8192).project(nil)).to have_key("a"*10_000)
445
+ end
446
+
447
+ it "errors if starting with closing object" do
448
+ expect {
449
+ project(nil, json: '}')
450
+ }.to raise_error(Yajl::ParseError)
451
+ end
452
+
453
+ it "handles objects with utf16 escape sequences as keys" do
454
+ projection = project(nil, json: '{"\ud83d\ude00": "grinning face"}')
455
+ literal = {"😀" => "grinning face"}
456
+ expect(projection).to eql(literal)
457
+ end
458
+
459
+ it "handles objects with non-ascii utf8 bytes as keys" do
460
+ expect(project(nil, json: '{"😀": "grinning face"}')).to eql({"😀" => "grinning face"})
461
+ end
462
+
463
+ it "handles strings with utf16 escape sequences as object values" do
464
+ expect(project(nil, json: '{"grinning face": "\ud83d\ude00"}')).to eql({"grinning face" => "😀"})
465
+ end
466
+
467
+ it "handles strings with utf16 escape sequences as array values" do
468
+ projection = project(nil, json: '["\ud83d\ude00"]')
469
+ puts projection.first.inspect
470
+ puts projection.first.bytes
471
+
472
+ literal = ["😀"]
473
+ puts literal.first.inspect
474
+ puts literal.first.bytes
475
+
476
+ expect(projection).to eql(literal)
477
+ end
478
+
479
+ it "handles strings with non-ascii utf8 bytes as array values" do
480
+ projection = project(nil, json: '["😀"]')
481
+ puts projection.first.inspect
482
+ puts projection.first.bytes
483
+
484
+ literal = ["😀"]
485
+ puts literal.first.inspect
486
+ puts literal.first.bytes
487
+
488
+ expect(projection).to eql(literal)
489
+ end
490
+
491
+ it "ignores strings with utf16 escape sequences" do
492
+ expect(project({"grinning face with open mouth" => nil}, json: '{"grinning face": "\ud83d\ude00", "grinning face with open mouth": "\ud83d\ude03"}')).to eql({"grinning face with open mouth" => "😃"})
493
+ end
494
+
495
+ it "handles objects whose second key has escape sequences" do
496
+ expect(project(nil, json: '{"foo": "bar", "\ud83d\ude00": "grinning face"}')).to eql({"foo" => "bar", "😀" => "grinning face"})
497
+ end
498
+ end