Search code examples
jsoncrystal-lang

Crystal handle json file of known format but dynamic keys


So I have a JSON file of a somewhat known format { String => JSON::Type, ... }. So it is basically of type Hash(String, JSON::Type). But when I try and read it from file to memory like so: JSON.parse(File.read(@cache_file)).as(Hash(String, JSON::Type)) I always get an exception: can't cast JSON::Any to Hash(String, JSON::Type)

I'm not sure how I am supposed to handle the data if I can't cast it.

What I basically want to do is the following:

  • save JSON::Type data under a String key
  • replace JSON::Type data with other JSON::Type data under a String key

And of course read from / write to file...

Here's the whole thing I've got so far:

class Cache
  def initialize(@cache_file = "/tmp/cache_file.tmp")
  end

  def cache(cache_key : (String | Symbol))
    mutable_cache_data = data
    value = mutable_cache_data[cache_key.to_s] ||= yield.as(JSON::Type)
    File.write @cache_file, mutable_cache_data
    value
  end

  def clear
    File.delete @cache_file
  end

  def data
    unless File.exists? @cache_file
      File.write @cache_file, {} of String => JSON::Type
    end
    JSON.parse(File.read(@cache_file)).as(Hash(String, JSON::Type))
  end
end

puts Cache.new.cache(:something) { 10 } # => 10
puts Cache.new.cache(:something) { 'a' } # => 10

TL;DR I want to read a JSON file into a Hash(String => i_dont_care), replace a value under a given key name and serialize it to file again. How do I do that?


Solution

  • JSON.parse returns an JSON::Any, not a Hash so you can't cast it. You can however access the underlying raw value as JSON.parse(file).raw and cast this as hash.

    Then your code is basically working (I've fixed a few error): https://carc.in/#/r/28c1