# JSON::Serializable, converters, and nilable values

**URL:** <https://forum.crystal-lang.org/t/json-serializable-converters-and-nilable-values/5828>\
**Category:** Help & Support\
**Created:** [July 13, 2023, 1:45am UTC](https://forum.crystal-lang.org/t/json-serializable-converters-and-nilable-values/5828 "2023-07-13T01:45:45Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![jgaskins](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/jgaskins/32/2449_2.png) [@jgaskins](https://forum.crystal-lang.org/u/jgaskins)\
**Post date:** [July 13, 2023, 1:45am UTC](https://forum.crystal-lang.org/t/json-serializable-converters-and-nilable-values/5828/1 "2023-07-13T01:45:45Z")

</div>

In talking to a third-party API, it’s sending `Time` values serialized as integers, so I’m using [`Time::EpochConverter`](https://crystal-lang.org/api/1.9.0/Time/EpochConverter.html) to deserialize it. , but when that timestamp is nilable and is explicitly returned from the API as `null`, I can’t use it because I get this:

```auto
Expected Int but was Null at line 1, column 222

```

Is there a good pattern for dealing with explicit `null` values when using a converter?

---

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [July 13, 2023, 2:32am UTC](https://forum.crystal-lang.org/t/json-serializable-converters-and-nilable-values/5828/2 "2023-07-13T02:32:14Z")

</div>

Just make your property nilable?

```crystal
require "json"

class Person
  include JSON::Serializable

  @[JSON::Field(converter: Time::EpochConverter)]
  property birth_date : Time?
end

person = Person.from_json(%({"birth_date": null}))
person.birth_date # => nil

```

---

<div class="post-metadata">

**Author:** ![straight-shoota](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/straight-shoota/32/36_2.png) [@straight-shoota](https://forum.crystal-lang.org/u/straight-shoota)\
**Post date:** [July 13, 2023, 9:14am UTC](https://forum.crystal-lang.org/t/json-serializable-converters-and-nilable-values/5828/3 "2023-07-13T09:14:11Z")

</div>

This used to work up until 1.8.2 but it’s broken in 1.9.0. This looks like a regression introduced in one of the `JSON::Serializable` refactors (probably [Simplify implementation of `Serializable#initialize` by straight-shoota · Pull Request #13433 · crystal-lang/crystal · GitHub](https://github.com/crystal-lang/crystal/pull/13433)).

As a workaround, you can make the converter nilable:

```cr
require "json"

module NilableConverter(T)
  def self.from_json(parser : JSON::PullParser)
    parser.read_null_or do
      T.from_json(parser)
    end
  end
end

class Person
  include JSON::Serializable

  @[JSON::Field(converter: NilableConverter(Time::EpochConverter))]
  property birth_date : Time?
end

person = Person.from_json(%({"birth_date": null}))
p person.birth_date # => nil

```

---

<div class="post-metadata">

**Author:** ![straight-shoota](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/straight-shoota/32/36_2.png) [@straight-shoota](https://forum.crystal-lang.org/u/straight-shoota)\
**Post date:** [July 13, 2023, 9:34am UTC](https://forum.crystal-lang.org/t/json-serializable-converters-and-nilable-values/5828/4 "2023-07-13T09:34:36Z")

</div>

> <https://github.com/crystal-lang/crystal/issues/13655>
>
> Some of the refactoring in \`JSON::Serializable\` (probably #13433) breaks reading… \`null\` values on a nilable field when using a converter.
> 
> The following code works fine in 1.8.2 but raises in 1.9.0:
> \`\`\`cr
> require "json"
> 
> class Foo
> include JSON::Serializable
> @\[JSON::Field(converter: Time::EpochConverter)\]
> property bar : Time?
> end
> 
> Foo.from\_json((%({"bar": null})) # JSON::SerializableError: Expected Int but was Null at line 1, column 13 parsing Foo#bar at line 1, column 2
> \`\`\`
> 
> Fields without a converter are unaffected because the deserializer tries to deserialze each of the union types. That includes \`Nil\` and it succeeds returning \`nil\`.
> 
> This was discovered in https://forum.crystal-lang.org/t/json-serializable-converters-and-nilable-values/5828
> 
> A potential workaround is to wrap the converter with explicit \`null\` handling:
> 
> \`\`\`cr
> require "json"
> 
> module NilableConverter(T)
> def self.from\_json(parser : JSON::PullParser)
> parser.read\_null\_or do
> T.from\_json(parser)
> end
> end
> end
> 
> class Foo
> include JSON::Serializable
> @\[JSON::Field(converter: NilableConverter(Time::EpochConverter))\]
> property bar : Time?
> end
> 
> Foo.from\_json((%({"bar": null}))
> \`\`\`
> 
> Perhaps this would even be a better way to implement this, being more explicit and giving full control of \`null\` handling to the converter.
> But it used to work that \`null\` are implicitly read and turned in \`nil\` for nilable fields, so this should be restored.

---

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [July 13, 2023, 12:44pm UTC](https://forum.crystal-lang.org/t/json-serializable-converters-and-nilable-values/5828/5 "2023-07-13T12:44:45Z")

</div>

> [@straight-shoota](#):
>
> This used to work up until 1.8.2 but it’s broken in 1.9.0

Ahh that would explain it 🤦
