1 require 'active_support/core_ext/string/starts_ends_with'
2
3 module ActiveSupport
4 module JSON
5 module Backends
6 module Yaml
7 ParseError = ::StandardError
8 extend self
9
10 # Converts a JSON string into a Ruby object.
11 def decode(json)
12 YAML.load(convert_json_to_yaml(json))
13 rescue ArgumentError => e
14 raise ParseError, "Invalid JSON string"
15 end
16
17 protected
18 # Ensure that ":" and "," are always followed by a space
19 def convert_json_to_yaml(json) #:nodoc:
20 require 'strscan' unless defined? ::StringScanner
21 scanner, quoting, marks, pos, times = ::StringScanner.new(json), false, [], nil, []
22 while scanner.scan_until(/(\\['"]|['":,\\]|\\.)/)
23 case char = scanner[1]
24 when '"', "'"
25 if !quoting
26 quoting = char
27 pos = scanner.pos
28 elsif quoting == char
29 if json[pos..scanner.pos-2] =~ DATE_REGEX
30 # found a date, track the exact positions of the quotes so we can
31 # overwrite them with spaces later.
32 times << pos << scanner.pos
33 end
34 quoting = false
35 end
36 when ":",","
37 marks << scanner.pos - 1 unless quoting
38 when "\\"
39 scanner.skip(/\\/)
40 end
41 end
42
43 if marks.empty?
44 json.gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do
45 ustr = $1
46 if ustr.start_with?('u')
47 [ustr[1..-1].to_i(16)].pack("U")
48 elsif ustr == '\\'
49 '\\\\'
50 else
51 ustr
52 end
53 end
54 else
55 left_pos = [-1].push(*marks)
56 right_pos = marks << scanner.pos + scanner.rest_size
57 output = []
58 left_pos.each_with_index do |left, i|
59 scanner.pos = left.succ
60 chunk = scanner.peek(right_pos[i] - scanner.pos + 1)
61 # overwrite the quotes found around the dates with spaces
62 while times.size > 0 && times[0] <= right_pos[i]
63 chunk[times.shift - scanner.pos - 1] = ' '
64 end
65 chunk.gsub!(/\\([\\\/]|u[[:xdigit:]]{4})/) do
66 ustr = $1
67 if ustr.start_with?('u')
68 [ustr[1..-1].to_i(16)].pack("U")
69 elsif ustr == '\\'
70 '\\\\'
71 else
72 ustr
73 end
74 end
75 output << chunk
76 end
77 output = output * " "
78
79 output.gsub!(/\\\//, '/')
80 output
81 end
82 end
83 end
84 end
85 end
86 end
87