Search code examples
jsonserializationcrystal-lang

Get classes from JSON in Crystal


I have some JSON file like this:

{
    "2": {
        "_id": 2,
        "_date": "Mon Apr 05 2021",
        "_timestamp": 1617654662313,
        "description": "Some text",
        "isStarred": true,
        "boards": [
            "@3.0",
            "@Some-day"
        ],
        "_isTask": false,
        "isComplete": false,
        "inProgress": false,
        "priority": 1
    },
    "7": {
        "_id": 7,
        "_date": "Mon Apr 05 2021",
        "_timestamp": 1617658197721,
        "description": "Some text too",
        "isStarred": false,
        "boards": [
            "@Some-day"
        ],
        "_isTask": false
    }

}

and I want to parse it in my class Entry:

  require "json"

  enum Priority
      Low # 1
      Medium # 2
      High # 3
  end

  class Entry
      include JSON::Serializable
      property _id : UInt32
      property _date : Time
      property _timestamp : UInt64
      property description : String
      property isStarred : Bool
      property boards : Array(String)
      property _isTask : Bool
      property isComplete : Bool
      property inProgress : Bool
      property priority : Priority
  end

When I try to parse using Hash(String, Entry).from_json it does not work: Expected BeginObject but was String


Solution

  • I cannot reproduce your error, there's no from_string method in the standard library, so the fault might be within whatever that is.

    However using from_json still requires some adjustments to your example:

    1. The date format in _date is non-standard and requires an explicit Time::Format passed as a field converter using the @[JSON::Field] annotation's converter attribute.
    2. Similarly enums serialize to their name value as a string by default and require Enum::ValueConverter to be set to serialize to their numerical value instead.
    3. Going by your example JSON some of the properties are optional and need to be marked as nilable.

    https://carc.in/#/r/e2e7

    The @[JSON::Field] annotation is also handy to set different external names while using more conventional names on the Crystal side of things.