How to configure JSON.mapping for Array of Array of Strings to become a Hash?

Viewed 405

I am trying to process the following JSON that I receive from an API.

{"product":"midprice",
"prices":[
  ["APPLE","217.88"],
  ["GOOGLE","1156.05"],
  ["FACEBOOK","160.58"]
]}

I can get a basic mapping working with:

require "json"

message = "{\"product\":\"midprice\",\"prices\":[[\"APPLE\",\"217.88\"],[\"GOOGLE\",\"1156.05\"],[\"FACEBOOK\",\"160.58\"]]}"

class Midprice
  JSON.mapping(
    product: String,
    prices: Array(Array(String)),
  )
end

midprice = Midprice.from_json(message)
p midprice.product # Outputs the String
p midprice.prices # Outputs 

Crystal 0.26.1 Code: https://play.crystal-lang.org/#/r/515o

But ideally I would like prices to be a hash with the stock name as the key and the price as the value. Can this be done with JSON.mapping?

1 Answers

JSON.mapping is going to be removed in favor of JSON::Serializable and annotations. You can use it like:

class Midprice
  include JSON::Serializable

  getter product : String

  @[JSON::Field(converter: StockConverter.new)]
  getter prices : Hash(String, String)
end

You need to use a converter to modify prices into the format that you want.

In this case the input is an Array(Array(String)) and the output is a Hash(String, String) which is a different type. You need to implement a custom from_json method for your converter.

class StockConverter
  def initialize
    @prices = Hash(String, String).new
  end

  def from_json(pull : JSON::PullParser)
    pull.read_array do
      pull.read_array do
        stock = pull.read_string
        price = pull.read_string

        @prices[stock] = price
      end
    end

    @prices
  end
end

Here's the full working code: https://play.crystal-lang.org/#/r/51d9

Related