diff options
author | Florian Frank <flori@ping.de> | 2011-08-29 21:31:33 +0200 |
---|---|---|
committer | Florian Frank <flori@ping.de> | 2011-08-29 21:31:33 +0200 |
commit | 2b5de6d03750b113333eded00177b14f8719042e (patch) | |
tree | feb73b6ed464249ef0f004d68ca5f6053ed5311c /lib | |
parent | fe046d68c5ed88b32b1cf3343babcf367b5cc79f (diff) | |
parent | 86bd839402eae91a1703fdfe6e1ae8267f39f030 (diff) | |
download | json-2b5de6d03750b113333eded00177b14f8719042e.tar.gz |
Merge branch 'quirks-mode'
Diffstat (limited to 'lib')
-rw-r--r-- | lib/json/common.rb | 18 | ||||
-rw-r--r-- | lib/json/pure/generator.rb | 17 | ||||
-rw-r--r-- | lib/json/pure/parser.rb | 142 |
3 files changed, 117 insertions, 60 deletions
diff --git a/lib/json/common.rb b/lib/json/common.rb index bc8c952..43e249c 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -198,7 +198,11 @@ module JSON # amount of sanity checks, and the pretty_generate method for some # defaults for pretty output. def generate(obj, opts = nil) - state = SAFE_STATE_PROTOTYPE.dup + if State === opts + state, opts = opts, nil + else + state = SAFE_STATE_PROTOTYPE.dup + end if opts if opts.respond_to? :to_hash opts = opts.to_hash @@ -225,7 +229,11 @@ module JSON # *WARNING*: Be careful not to pass any Ruby data structures with circles as # _obj_ argument because this will cause JSON to go into an infinite loop. def fast_generate(obj, opts = nil) - state = FAST_STATE_PROTOTYPE.dup + if State === opts + state, opts = opts, nil + else + state = FAST_STATE_PROTOTYPE.dup + end if opts if opts.respond_to? :to_hash opts = opts.to_hash @@ -252,7 +260,11 @@ module JSON # The _opts_ argument can be used to configure the generator. See the # generate method for a more detailed explanation. def pretty_generate(obj, opts = nil) - state = PRETTY_STATE_PROTOTYPE.dup + if State === opts + state, opts = opts, nil + else + state = PRETTY_STATE_PROTOTYPE.dup + end if opts if opts.respond_to? :to_hash opts = opts.to_hash diff --git a/lib/json/pure/generator.rb b/lib/json/pure/generator.rb index 3687e30..59df74d 100644 --- a/lib/json/pure/generator.rb +++ b/lib/json/pure/generator.rb @@ -141,6 +141,7 @@ module JSON @array_nl = '' @allow_nan = false @ascii_only = false + @quirks_mode = false configure opts end @@ -165,6 +166,10 @@ module JSON # the generated JSON, max_nesting = 0 if no maximum is checked. attr_accessor :max_nesting + # If this attribute is set to true, quirks mode is enabled, otherwise + # it's disabled. + attr_accessor :quirks_mode + # This integer returns the current depth data structure nesting in the # generated JSON. attr_accessor :depth @@ -188,10 +193,17 @@ module JSON @allow_nan end + # Returns true, if only ASCII characters should be generated. Otherwise + # returns false. def ascii_only? @ascii_only end + # Returns true, if quirks mode is enabled. Otherwise returns false. + def quirks_mode? + @quirks_mode + end + # Configure this State instance with the Hash _opts_, and return # itself. def configure(opts) @@ -203,6 +215,7 @@ module JSON @allow_nan = !!opts[:allow_nan] if opts.key?(:allow_nan) @ascii_only = opts[:ascii_only] if opts.key?(:ascii_only) @depth = opts[:depth] || 0 + @quirks_mode = opts[:quirks_mode] if opts.key?(:quirks_mode) if !opts.key?(:max_nesting) # defaults to 19 @max_nesting = 19 elsif opts[:max_nesting] @@ -218,7 +231,7 @@ module JSON # passed to the configure method. def to_h result = {} - for iv in %w[indent space space_before object_nl array_nl allow_nan max_nesting ascii_only depth] + for iv in %w[indent space space_before object_nl array_nl allow_nan max_nesting ascii_only quirks_mode depth] result[iv.intern] = instance_variable_get("@#{iv}") end result @@ -229,7 +242,7 @@ module JSON # GeneratorError exception. def generate(obj) result = obj.to_json(self) - if result !~ /\A\s*(?:\[.*\]|\{.*\})\s*\Z/m + if !@quirks_mode && result !~ /\A\s*(?:\[.*\]|\{.*\})\s*\Z/m raise GeneratorError, "only generation of JSON objects or arrays allowed" end result diff --git a/lib/json/pure/parser.rb b/lib/json/pure/parser.rb index d612018..5ef1b78 100644 --- a/lib/json/pure/parser.rb +++ b/lib/json/pure/parser.rb @@ -70,40 +70,8 @@ module JSON # * *array_class*: Defaults to Array def initialize(source, opts = {}) opts ||= {} - if defined?(::Encoding) - if source.encoding == ::Encoding::ASCII_8BIT - b = source[0, 4].bytes.to_a - source = case - when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0 - source.dup.force_encoding(::Encoding::UTF_32BE).encode!(::Encoding::UTF_8) - when b.size >= 4 && b[0] == 0 && b[2] == 0 - source.dup.force_encoding(::Encoding::UTF_16BE).encode!(::Encoding::UTF_8) - when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0 - source.dup.force_encoding(::Encoding::UTF_32LE).encode!(::Encoding::UTF_8) - - when b.size >= 4 && b[1] == 0 && b[3] == 0 - source.dup.force_encoding(::Encoding::UTF_16LE).encode!(::Encoding::UTF_8) - else - source.dup - end - else - source = source.encode(::Encoding::UTF_8) - end - source.force_encoding(::Encoding::ASCII_8BIT) - else - b = source - source = case - when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0 - JSON.iconv('utf-8', 'utf-32be', b) - when b.size >= 4 && b[0] == 0 && b[2] == 0 - JSON.iconv('utf-8', 'utf-16be', b) - when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0 - JSON.iconv('utf-8', 'utf-32le', b) - when b.size >= 4 && b[1] == 0 && b[3] == 0 - JSON.iconv('utf-8', 'utf-16le', b) - else - b - end + unless @quirks_mode = opts[:quirks_mode] + source = determine_encoding source end super source if !opts.key?(:max_nesting) # defaults to 19 @@ -113,44 +81,108 @@ module JSON else @max_nesting = 0 end - @allow_nan = !!opts[:allow_nan] - @symbolize_names = !!opts[:symbolize_names] - @create_additions = opts.key?(:create_additions) ? !!opts[:create_additions] : true - @create_id = opts[:create_id] || JSON.create_id - @object_class = opts[:object_class] || Hash - @array_class = opts[:array_class] || Array - @match_string = opts[:match_string] + @allow_nan = !!opts[:allow_nan] + @symbolize_names = !!opts[:symbolize_names] + if opts.key?(:create_additions) + @create_additions = !!opts[:create_additions] + else + @create_additions = true + end + @create_id = @create_additions ? JSON.create_id : nil + @object_class = opts[:object_class] || Hash + @array_class = opts[:array_class] || Array + @match_string = opts[:match_string] end alias source string + def quirks_mode? + !!@quirks_mode + end + + def reset + super + @current_nesting = 0 + end + # Parses the current JSON string _source_ and returns the complete data # structure as a result. def parse reset obj = nil - until eos? - case - when scan(OBJECT_OPEN) - obj and raise ParserError, "source '#{peek(20)}' not in JSON!" - @current_nesting = 1 - obj = parse_object - when scan(ARRAY_OPEN) - obj and raise ParserError, "source '#{peek(20)}' not in JSON!" - @current_nesting = 1 - obj = parse_array - when skip(IGNORE) - ; + if @quirks_mode + while !eos? && skip(IGNORE) + end + if eos? + raise ParserError, "source did not contain any JSON!" else - raise ParserError, "source '#{peek(20)}' not in JSON!" + obj = parse_value + obj == UNPARSED and raise ParserError, "source did not contain any JSON!" end + else + until eos? + case + when scan(OBJECT_OPEN) + obj and raise ParserError, "source '#{peek(20)}' not in JSON!" + @current_nesting = 1 + obj = parse_object + when scan(ARRAY_OPEN) + obj and raise ParserError, "source '#{peek(20)}' not in JSON!" + @current_nesting = 1 + obj = parse_array + when skip(IGNORE) + ; + else + raise ParserError, "source '#{peek(20)}' not in JSON!" + end + end + obj or raise ParserError, "source did not contain any JSON!" end - obj or raise ParserError, "source did not contain any JSON!" obj end private + def determine_encoding(source) + if defined?(::Encoding) + if source.encoding == ::Encoding::ASCII_8BIT + b = source[0, 4].bytes.to_a + source = + case + when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0 + source.dup.force_encoding(::Encoding::UTF_32BE).encode!(::Encoding::UTF_8) + when b.size >= 4 && b[0] == 0 && b[2] == 0 + source.dup.force_encoding(::Encoding::UTF_16BE).encode!(::Encoding::UTF_8) + when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0 + source.dup.force_encoding(::Encoding::UTF_32LE).encode!(::Encoding::UTF_8) + when b.size >= 4 && b[1] == 0 && b[3] == 0 + source.dup.force_encoding(::Encoding::UTF_16LE).encode!(::Encoding::UTF_8) + else + source.dup + end + else + source = source.encode(::Encoding::UTF_8) + end + source.force_encoding(::Encoding::ASCII_8BIT) + else + b = source + source = + case + when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0 + JSON.iconv('utf-8', 'utf-32be', b) + when b.size >= 4 && b[0] == 0 && b[2] == 0 + JSON.iconv('utf-8', 'utf-16be', b) + when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0 + JSON.iconv('utf-8', 'utf-32le', b) + when b.size >= 4 && b[1] == 0 && b[3] == 0 + JSON.iconv('utf-8', 'utf-16le', b) + else + b + end + end + source + end + # Unescape characters in strings. UNESCAPE_MAP = Hash.new { |h, k| h[k] = k.chr } UNESCAPE_MAP.update({ |