Search code examples
crystal-lang

how to iterate and statically type values using JSON::Serializable?


I have the followng code

require "json"

struct TreeData
  include JSON::Serializable
  include JSON::Serializable::Unmapped

  @increased_hp_percent : Float32?
  @value : Float32?
  @increased_strength : Float32?

end


puts TreeData.from_json(%({"1":{},"2":{"modifications":{"increased_hp_percent":5},"value":5},"3":{"value":10},"4":{"modifications":{"increased_hp_percent":15,"increased_strength":20}}}))

Which produces the following output:

TreeData(@json_unmapped={"1" => {}, "2" => {"modifications" => {"increased_hp_percent" => 5_i64}, "value" => 5_i64}, "3" => {"value" => 10_i64}, "4" => {"modifications" => {"increased_hp_percent" => 15_i64, "increased_strength" => 20_i64}}}, @increased_hp_percent=nil, @value=nil, @increased_strength=nil)

Basically, I want to set the values of the keys I specified (increased_hp_percent, value, and increased_strength) to Float32.

However, they are not getting set to the right type. I believe it's because they are inside another json object.

also I am trying to iterate through the json. If I were to use JSON.parse, then do json.as_h.each it would work fine. I don't know how to do this using the new JSON::Serializable

You may ask why don't I just use JSON.parse? Well, because I want to statically type my json to the correct types. But I also want to be able to enumerate through it like before.


Solution

  • You have to properly create types for your nested data structure, it doesn't try to dig them out for you:

    require "json"
    
    struct Modifications
      include JSON::Serializable
    
      getter increased_hp_percent : Float32?
      getter increased_strength : Float32?
    end
    
    struct TreeData
      include JSON::Serializable
    
      getter modifications : Modifications?
      getter value : Float32?
    end
    
    data = Hash(String, TreeData).from_json(%({"1":{},"2":{"modifications":{"increased_hp_percent":5},"value":5},"3":{"value":10},"4":{"modifications":{"increased_hp_percent":15,"increased_strength":20}}}))
    
    data.each_value do |node|
      pp node
    end
    

    Output:

    TreeData(@modifications=nil, @value=nil)
    TreeData(
     @modifications=
      Modifications(@increased_hp_percent=5.0_f32, @increased_strength=nil),
     @value=5.0_f32)
    TreeData(@modifications=nil, @value=10.0_f32)
    TreeData(
     @modifications=
      Modifications(@increased_hp_percent=15.0_f32, @increased_strength=20.0_f32),
     @value=nil)