You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Crystal中JSON::Serializable无法使用Proc默认值的问题

Crystal JSON序列化:结构体动态默认值反序列化时为nil的解决方案

问题背景

你在定义Crystal结构体并使用JSON::Serializable时,遇到了动态默认值失效的问题:用{ Time.utc }定义的first_seen属性,在反序列化空JSON对象时始终为nil,而静态默认值count : Int64 = 1却能正常生效。

原因分析

Crystal中property first_seen : Time { Time.utc }的语法,是给属性的getter方法设置默认返回值,而非为初始化过程提供默认值。JSON::Serializable宏在处理反序列化时,只会识别初始化方法中的参数默认值,或是通过JSON::Field宏指定的默认逻辑,不会自动调用property的动态默认块。

替代解决方案(除after_initialize外)

方案1:给初始化方法参数添加动态默认值

直接修改结构体的初始化方法,为@first_seen参数设置默认值Time.utc,这样反序列化时如果JSON中没有该字段,就会自动使用这个动态生成的值:

require "json"

struct A
  include JSON::Serializable

  property first_seen : Time
  property count : Int64 = 1

  def initialize(@first_seen : Time = Time.utc, count = 1)
    @count = count.to_i64
  end
end

a = A.from_json("{}")
pp a # 输出:A(@count=1, @first_seen=当前UTC时间)
puts a.to_json # 输出包含first_seen和count的JSON字符串

方案2:使用JSON::Field宏指定动态默认值

通过@[JSON::Field(default: ->{ Time.utc })]注解标记属性,显式告诉序列化宏在缺失字段时执行指定的Proc生成默认值,无需修改初始化方法:

require "json"

struct A
  include JSON::Serializable

  @[JSON::Field(default: ->{ Time.utc })]
  property first_seen : Time
  property count : Int64 = 1

  def initialize(@first_seen : Time, count)
    @count = count.to_i64
  end
end

a = A.from_json("{}")
pp a # 输出:A(@count=1, @first_seen=当前UTC时间)
puts a.to_json # 输出包含first_seen和count的JSON字符串

方案对比

  • 方案1更直观,直接通过初始化逻辑控制默认值,适合简单场景;
  • 方案2通过宏注解分离序列化配置和初始化逻辑,适合需要保持初始化方法签名不变的复杂场景。

内容的提问来源于stack exchange,提问作者ngaumont

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 07:35:10