提交 011afb70 编写于 作者: J Joshua Peek

Merge branch 'multi_json' of https://github.com/joshk/rails into joshk-multi_json

......@@ -16,4 +16,6 @@
s.files = Dir['CHANGELOG', 'README.rdoc', 'lib/**/*']
s.require_path = 'lib'
s.add_dependency('multi_json', '~> 1.0.0.rc3')
end
require 'json' unless defined?(JSON)
module ActiveSupport
module JSON
module Backends
module JSONGem
ParseError = ::JSON::ParserError
extend self
# Parses a JSON string or IO and convert it into an object
def decode(json)
if json.respond_to?(:read)
json = json.read
end
data = ::JSON.parse(json)
if ActiveSupport.parse_json_times
convert_dates_from(data)
else
data
end
end
private
def convert_dates_from(data)
case data
when nil
nil
when DATE_REGEX
begin
DateTime.parse(data)
rescue ArgumentError
data
end
when Array
data.map! { |d| convert_dates_from(d) }
when Hash
data.each do |key, value|
data[key] = convert_dates_from(value)
end
else
data
end
end
end
end
end
end
require 'yajl' unless defined?(Yajl)
module ActiveSupport
module JSON
module Backends
module Yajl
ParseError = ::Yajl::ParseError
extend self
# Parses a JSON string or IO and convert it into an object
def decode(json)
data = ::Yajl::Parser.new.parse(json)
if ActiveSupport.parse_json_times
convert_dates_from(data)
else
data
end
end
private
def convert_dates_from(data)
case data
when nil
nil
when DATE_REGEX
begin
DateTime.parse(data)
rescue ArgumentError
data
end
when Array
data.map! { |d| convert_dates_from(d) }
when Hash
data.each do |key, value|
data[key] = convert_dates_from(value)
end
else
data
end
end
end
end
end
end
require 'active_support/core_ext/string/starts_ends_with'
module ActiveSupport
module JSON
module Backends
module Yaml
ParseError = ::StandardError
extend self
EXCEPTIONS = [::ArgumentError] # :nodoc:
begin
require 'psych'
EXCEPTIONS << Psych::SyntaxError
rescue LoadError
end
# Parses a JSON string or IO and converts it into an object
def decode(json)
if json.respond_to?(:read)
json = json.read
end
YAML.load(convert_json_to_yaml(json))
rescue *EXCEPTIONS => e
raise ParseError, "Invalid JSON string: '%s'" % json
end
protected
# Ensure that ":" and "," are always followed by a space
def convert_json_to_yaml(json) #:nodoc:
require 'strscan' unless defined? ::StringScanner
scanner, quoting, marks, pos, times = ::StringScanner.new(json), false, [], nil, []
while scanner.scan_until(/(\\['"]|['":,\\]|\\.|[\]])/)
case char = scanner[1]
when '"', "'"
if !quoting
quoting = char
pos = scanner.pos
elsif quoting == char
if valid_date?(json[pos..scanner.pos-2])
# found a date, track the exact positions of the quotes so we can
# overwrite them with spaces later.
times << pos
end
quoting = false
end
when ":",",", "]"
marks << scanner.pos - 1 unless quoting
when "\\"
scanner.skip(/\\/)
end
end
if marks.empty?
json.gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do
ustr = $1
if ustr.start_with?('u')
char = [ustr[1..-1].to_i(16)].pack("U")
# "\n" needs extra escaping due to yaml formatting
char == "\n" ? "\\n" : char
elsif ustr == '\\'
'\\\\'
else
ustr
end
end
else
left_pos = [-1].push(*marks)
right_pos = marks << scanner.pos + scanner.rest_size
output = []
left_pos.each_with_index do |left, i|
scanner.pos = left.succ
chunk = scanner.peek(right_pos[i] - scanner.pos + 1)
if ActiveSupport.parse_json_times
# overwrite the quotes found around the dates with spaces
while times.size > 0 && times[0] <= right_pos[i]
chunk.insert(times.shift - scanner.pos - 1, '! ')
end
end
chunk.gsub!(/\\([\\\/]|u[[:xdigit:]]{4})/) do
ustr = $1
if ustr.start_with?('u')
char = [ustr[1..-1].to_i(16)].pack("U")
# "\n" needs extra escaping due to yaml formatting
char == "\n" ? "\\n" : char
elsif ustr == '\\'
'\\\\'
else
ustr
end
end
output << chunk
end
output = output * " "
output.gsub!(/\\\//, '/')
output
end
end
private
def valid_date?(date_string)
begin
date_string =~ DATE_REGEX && DateTime.parse(date_string)
rescue ArgumentError
false
end
end
end
end
end
end
require 'active_support/core_ext/module/attribute_accessors'
require 'active_support/core_ext/module/delegation'
require 'multi_json'
module ActiveSupport
# Look for and parse json strings that look like ISO 8601 times.
mattr_accessor :parse_json_times
module JSON
# Listed in order of preference.
DECODERS = %w(Yajl Yaml)
class << self
attr_reader :parse_error
delegate :decode, :to => :backend
def decode(json, options ={})
data = MultiJson.decode(json, options)
if ActiveSupport.parse_json_times
convert_dates_from(data)
else
data
end
end
def backend
set_default_backend unless defined?(@backend)
@backend
def engine
MultiJson.engine
end
alias :backend :engine
def backend=(name)
if name.is_a?(Module)
@backend = name
else
require "active_support/json/backends/#{name.to_s.downcase}"
@backend = ActiveSupport::JSON::Backends::const_get(name)
end
@parse_error = @backend::ParseError
def engine=(name)
MultiJson.engine = name
end
alias :backend= :engine=
def with_backend(name)
old_backend, self.backend = backend, name
......@@ -35,15 +34,30 @@ def with_backend(name)
self.backend = old_backend
end
def set_default_backend
DECODERS.find do |name|
def parse_error
MultiJson::DecodeError
end
private
def convert_dates_from(data)
case data
when nil
nil
when DATE_REGEX
begin
self.backend = name
true
rescue LoadError
# Try next decoder.
false
DateTime.parse(data)
rescue ArgumentError
data
end
when Array
data.map! { |d| convert_dates_from(d) }
when Hash
data.each do |key, value|
data[key] = convert_dates_from(value)
end
else
data
end
end
end
......
......@@ -56,12 +56,9 @@ class TestJSONDecoding < ActiveSupport::TestCase
%q({"a":"Line1\u000aLine2"}) => {"a"=>"Line1\nLine2"}
}
# load the default JSON backend
ActiveSupport::JSON.backend = 'Yaml'
backends = %w(Yaml)
backends << "JSONGem" if defined?(::JSON)
backends << "Yajl" if defined?(::Yajl)
backends = [:ok_json]
backends << :json_gem if defined?(::JSON)
backends << :yajl if defined?(::Yajl)
backends.each do |backend|
TESTS.each do |json, expected|
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册