Topic: unable to use json

Hi Folks,
            I am a new bie to Json, and tried implementing in my rails app.
I installed the pure ruby version of json(json_pure 1.1.2) and my rails is 2.0
In my environment.rb, I have

require 'json/pure'

In my controller, when I tried this simple bit of code,

item = Item.find(1)
item.to_jon

I got the following error:
ArgumentError (wrong number of arguments (2 for 1)):
    c:/ruby/lib/ruby/gems/1.8/gems/json_pure-1.1.2/lib/json/pure/generator.rb:251:in `to_json'
    c:/ruby/lib/ruby/gems/1.8/gems/json_pure-1.1.2/lib/json/pure/generator.rb:251:in `json_transform'
    c:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/attribute_methods.rb:160:in `map'
    c:/ruby/lib/ruby/gems/1.8/gems/json_pure-1.1.2/lib/json/pure/generator.rb:245:in `each'
    c:/ruby/lib/ruby/gems/1.8/gems/json_pure-1.1.2/lib/json/pure/generator.rb:245:in `map'
    c:/ruby/lib/ruby/gems/1.8/gems/json_pure-1.1.2/lib/json/pure/generator.rb:245:in `json_transform'
    c:/ruby/lib/ruby/gems/1.8/gems/json_pure-1.1.2/lib/json/pure/generator.rb:218:in `to_json'
    c:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/serializers/json_serializer.rb:67:in `serialize'
    c:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/serialization.rb:91:in `to_s'
    c:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/serializers/json_serializer.rb:57:in `to_json'

What might be th reason? Any help much appreciated...

thanks and regards
Venkat

Re: unable to use json

I got the same error when trying something like this:

people = Person.find(:all)
people.to_json

So people is an array of Person objects.  The problem is, the Person object, which is a subclass of ActiveRecord::Base, has a to_json method that allows only one argument.  Array.to_json, which comes from json_pure, tries to call a to_json method on each of the Array's elements, but it tries to pass in two arguments, state and depth - both of which the ActiveRecord to_json doesn't use.

I don't know if this is your situation, but the solution for me was to override the to_json method in my Person class.  This allows the to_json method to be called with two arguments, but passes neither of them along to the super (which is the original to_json):

class Person < ActiveRecord::Base
  def to_json(state, depth)
    super()
  end
end

I just read of a potential other solution, but haven't tried it yet:  instead of require 'json/pure' try require 'json/add/rails'.

Re: unable to use json

If you are using rails 2 then i think you don't have use pure ruby version of json

http://weblog.rubyonrails.com/2007/9/30 … ew-release

Just comment this line
require 'json/pure'

and try

Re: unable to use json

We are having the same problem. We are using rails 2.1.0 and we have installed the json gem in order to use from_json outside of ActiveRecord. Unfortunately, this function is not included in Rails. As a result, my coworkers are having the exact same error as described in this thread, in certain cases when to_json is called. We do not experience this on our OS X machines, but do on our Windows machines. The difference is that the OS X machines use the C version of the JSON gem and the windows machines use JSON pure. Any help in figuring this out would be great. Or perhaps a way to use the JSON gem only for from_json and always use the rails version for to_json. BTW, our require state is
require 'json/add/rails'
and always has been.

Re: unable to use json

i'm seeing a similar problem with json. figured i'd add to this thread if anyone knows what's going on.

stuff  = Stuff.find(:all)
things = Things.find(:all)
stuff_and_things = { :stuff => stuff, :things => things }

stuff.to_json and things.to_json works fine. but stuff_and_things.to_json causes
"TypeError: wrong argument type Hash (expected Data)"

i tried  equire 'json/pure' but get
"ArgumentError: wrong number of arguments (2 for 0)"

then tried the to_json override but get
"ArgumentError: wrong number of arguments (2 for 1)"

any ideas...

Re: unable to use json

Hi, I'm suffering from the exact same problem, and I just wanted to share what I've found out so far, in case someone knows better.

It seems that json gem has defined to_json(*) method which takes multiple arguments, and is using that to call in structured objects like Array or Hash. Now, it tried to define for lots of Objects, like Time, DateTime, Date stuff, but what Rails(2.1.0) is actually using is ActiveSupport::TimeWithZone, which is NOT defined.

so, when you call Person.first.to_json, which probably has a created_at, it tries to call TimeWithZone.to_json(nil, 1). Since TimeWithZone is not defined in json gem, it goes to ActiveRecord implementation, but there only to_json(arg) is defined. It cannot accept to_json with two arguments, so it raises an ArgumentError.

What I thought was, is that since require method is ignored after it has been called once, it won't be a problem when I call it later again, in some different Controller or something. So I required json in environment.rb, before the Initializer ran.

Having done only this, it actually worked, not emitting the ArgumentError exception. But surprisingly, if I require 'json' (or even 'json/add/rails') after the ActiveRecord was required, it makes that ArgumentError again. I don't know why this happens. I thought it would be OK to require afterwards, since I've done it once already.

Anybody know a better way?
Thanks in advance.

Re: unable to use json

The problem is that the JSON gem blows.  Doesn't work well on Windows, it's slow, incomplete, and buggy.  "gem remove json" to be sure you never use accidentally use it.  Now, if you eradicate all "require 'json'" lines from your Rails app, you'll never see this problem again.

How do you decode/encode JSON without the gem?  Call ActiveSupport::JSON.decode(string) to decode and obj.to_json to encode.

If you want to do this in a script outside of Rails, you can just copy-n-paste Rails's code.  So, to decode, paste the following to the top of your file (itself copy-n-pasted from vendor/rails/activesupport/lib/active_support/json/decoding.rb):


# copy-n-paste ActiveSupport's JSON code so this script will work anywhere
require 'yaml'
require 'strscan'

module ActiveSupport
  module JSON
    class ParseError < StandardError
    end

    class << self
      # Converts a JSON string into a Ruby object.
      def decode(json)
        YAML.load(convert_json_to_yaml(json))
      rescue ArgumentError => e
        raise ParseError, "Invalid JSON string"
      end

      protected
      # matches YAML-formatted dates
      DATE_REGEX = /^\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[ \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?)?$/

      # Ensure that ":" and "," are always followed by a space
      def convert_json_to_yaml(json) #:nodoc:
        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 json[pos..scanner.pos-2] =~ DATE_REGEX
                # found a date, track the exact positions of the quotes so we can remove them later.
                # oh, and increment them for each current mark, each one is an extra padded space that bumps
                # the position in the final YAML output
                total_marks = marks.size
                times << pos+total_marks << scanner.pos+total_marks
              end
              quoting = false
            end
          when ":",","
            marks << scanner.pos - 1 unless quoting
          end
        end

        if marks.empty?
          json.gsub(/\\\//, '/')
        else
          left_pos  = [-1].push(*marks)
          right_pos = marks << json.length
          output    = []
          left_pos.each_with_index do |left, i|
            output << json[left.succ..right_pos[i]]
          end
          output = output * " "

          times.each { |i| output[i-1] = ' ' }
          output.gsub!(/\\\//, '/')
          output
        end
      end
    end
  end
end

Re: unable to use json

sabr wrote:

How do you decode/encode JSON without the gem?  Call ActiveSupport::JSON.decode(string) to decode and obj.to_json to encode.

actually, the problem was that ActiveSupport was not so great at decoding unicode strings.

That's why I had to use external gem at the first place.

Re: unable to use json

@sabr

thz 4 suggestion. It works fine as u said !
In my case anyway, the only drawback is that many other libs need json gem to work. As an example :

# config.gem 'gnip-gnip', :lib => 'gnip', :version => '2.0.6'
# config.gem 'dj2-postrank', :lib => 'postrank', :version => '0.9.1'

Any other suggestion ?
thz in advance
luca

Re: unable to use json

I had a similar problem.  I am using ActiveRecord in a Sinatra app.  One of the needed gems requires the json gem and is loaded before activerecord.  I was trying to output json for a two element hash object, status => string and data => hash.  Inside the data hash were a number of string elements, a number of floats and an array of strings.  The to_json method raised TypeError: wrong argument type Hash (expected JSON::Ext::Generator::State) in gems/activesupport-2.2.2/lib/active_support/json/encoding.rb:21:in 'to_json'.

What transpires (as lizardwalker above points out) is that when encoding reached the array, the method being called was the array to_json from the json gem and not the enumerable to_json from activesupport.  The parameters passed into the to_json methods by activesupport is an options hash whereas the json gem to_hash method is expecting a State and int - hence the exception.

My solution was to create a small ArrayAsEnumerable class that mixed in the Enumerable module and provided an each method.  I stored a new instance of this class in place of the array and passed the array in as a parameter.  Now the activesupport enumerable.to_json IS being called.  This allows me to use the options hash to specify exclude, only and methods.

Incidentally moving the activerecord require to before the gem that loads json does not make any difference.