yajl-ruby 1.3.0 → 1.4.1

Sign up to get free protection for your applications and to get access to all the features.

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