3

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?

Guy C
  • 6,970
  • 5
  • 30
  • 30

1 Answers1

7

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

Johannes Müller
  • 5,581
  • 1
  • 11
  • 25
Serdar Dogruyol
  • 5,147
  • 3
  • 24
  • 32
  • 2
    For the record: You could also use a converter with `JSON.mapping`. But changing to `JSON::Serializable` is a good idea nonetheless. – Johannes Müller Sep 18 '18 at 17:14
  • 6
    But the converter actually could just be a module with a static method. There is no benefit from having a custom instance for that. It just costs an unnecessary heap allocation. https://play.crystal-lang.org/#/r/51dw – Johannes Müller Sep 18 '18 at 17:16