Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>The first chunk is for Rails 3.1 (older versions will be pretty much the same); the second chunk is for the standard non-Rails JSON. Skip to the end if tl;dr.</p> <hr> <p>Your problem is that Rails does this:</p> <pre><code>[Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass| klass.class_eval &lt;&lt;-RUBY, __FILE__, __LINE__ # Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info. def to_json(options = nil) ActiveSupport::JSON.encode(self, options) end RUBY end </code></pre> <p>in <code>active_support/core_ext/object/to_json.rb</code>. In particular, that changes Hash's <code>to_json</code> method into just an <code>ActiveSupport::JSON.encode</code> call.</p> <p>Then, looking at <code>ActiveSupport::JSON::Encoding::Encoder</code>, we see this:</p> <pre><code>def encode(value, use_options = true) check_for_circular_references(value) do jsonified = use_options ? value.as_json(options_for(value)) : value.as_json jsonified.encode_json(self) end end </code></pre> <p>So all the Rails JSON encoding goes through <code>as_json</code>. But, you're not defining your own <code>as_json</code> for Set, you're just setting up <code>to_json</code> and getting confused when Rails ignores something that it doesn't use.</p> <p>If you set up your own <code>Set#as_json</code>:</p> <pre><code>class Set def as_json(options = { }) { "json_class" =&gt; self.class.name, "data" =&gt; { "elements" =&gt; self.to_a } } end end </code></pre> <p>then you'll get what you're after in the Rails console and Rails in general:</p> <pre><code>&gt; require 'set' &gt; s = Set.new([1,2,3]) &gt; s.to_json =&gt; "{\"json_class\":\"Set\",\"data\":{\"elements\":[1,2,3]}}" &gt; h = { :set =&gt; s } &gt; h.to_json =&gt; "{\"set\":{\"json_class\":\"Set\",\"data\":{\"elements\":[1,2,3]}}}" </code></pre> <p>Keep in mind that <code>as_json</code> is used to prepare an object for JSON serialization and then <code>to_json</code> produces the actual JSON string. The <code>as_json</code> methods generally return simple serializable data structures, such as Hash and Array, and have direct analogues in JSON; then, once you have something that is structured like JSON, <code>to_json</code> is used to serialize it into a linear JSON string.</p> <hr> <p>When we look at the standard non-Rails JSON library, we see things like this:</p> <pre><code>def to_json(*a) as_json.to_json(*a) end </code></pre> <p>monkey patched into the basic classes (Symbol, Time, Date, ...). So once again, <code>to_json</code> is generally implemented in terms of <code>as_json</code>. In this environment, we need to include the standard <code>to_json</code> as well as the above <code>as_json</code> for Set:</p> <pre><code>class Set def as_json(options = { }) { "json_class" =&gt; self.class.name, "data" =&gt; { "elements" =&gt; self.to_a } } end def to_json(*a) as_json.to_json(*a) end def self.json_create(o) new o["data"]["elements"] end end </code></pre> <p>And we include your <code>json_create</code> class method for the decoder. Once that's all properly set up, we get things like this in <code>irb</code>:</p> <pre><code>&gt;&gt; s = Set.new([1,2,3]) &gt;&gt; s.as_json =&gt; {"json_class"=&gt;"Set", "data"=&gt;{"elements"=&gt;[1, 2, 3]}} &gt;&gt; h = { :set =&gt; s } &gt;&gt; h.to_json =&gt; "{"set":{"json_class":"Set","data":{"elements":[1,2,3]}}}" </code></pre> <hr> <p><strong>Executive Summary</strong>: If you're in Rails, don't worry about doing anything with <code>to_json</code>, <code>as_json</code> is what you want to play with. If you're not in Rails, implement most of your logic in <code>as_json</code> (despite what the documentation says) and add the standard <code>to_json</code> implementation (<code>def to_json(*a);as_json.to_json(*a);end</code>) as well.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload