File: active_support/json/backends/yaml.rb

Overview
Module Structure
Code

Overview

Module Structure

  module: <Toplevel Module>
  module: ActiveSupport#3
  module: JSON#4
  module: Backends#5
  module: Yaml#6
extends
  Yaml ( ActiveSupport::JSON::Backends )
has properties
constant: ParseError #7
method: decode / 1 #11
method: convert_json_to_yaml / 1 #19

Code

   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