Search code examples
crystal-lang

Unexpected ... Unexpected token exception when writing Crystal JSON converter


Why does usage of this JSON converter/parser always result in an exception (Unexpected token: EOF at 1:98)?

class MyParser
  def self.from_json(value : JSON::PullParser) : String
    "static"
  end
  # ...
end

class User
  JSON.mapping(
    first_name: {type: String, converter: MyParser},
    last_name: {type: String, converter: MyParser}
  )
end

Solution

  • Usage of JSON::PullParser always requires you to consume one entire value. If you don't want to read a json value, you can use JSON::PullParser#skip.

    To visualize why this is, consider a JSON::PullParser as a cursor into the JSON stream. When the from_json method gets called, the cursor is positioned in the stream like "key": |"value" where | is the cursor position (obviously "value" can be an array, another object, or anything). If you read the string by calling read_string on the PullParser, you get to "key": "value"| and the cursor is ready to read a , then the next object key, or } if it's the end of the object. If you don't want to read the value you must call skip to reach the same state as if you'd called read_string, or the parser will be in an invalid state.