json4s部分反序列化问题:JSON对象与Person样例类字段不匹配
解决JSON对象数组与Scala样例类字符串序列的序列化适配问题
你遇到的是JSON结构与Scala样例类字段类型不匹配的典型问题:JSON里的addresses是包含street和apt的对象数组,但你的Person样例类把这个字段定义成了Option[Seq[String]],默认序列化器没法自动完成这种类型转换,导致反序列化/序列化失败。下面基于你提到的json4s(Serialization.form属于该库)给出完整解决方案:
核心思路:自定义序列化器
我们需要实现一个自定义序列化器,手动处理Seq[String](及包装它的Option)和JSON对象数组之间的双向转换:
- 反序列化:把每个JSON地址对象转换成你需要的字符串格式(比如拼接
street和apt,或直接转成JSON字符串) - 序列化:把字符串再转回原来的JSON地址对象结构(保证双向转换的一致性)
完整代码实现
1. 导入依赖与包
先确保项目引入json4s的jackson模块(比如sbt依赖:"org.json4s" %% "json4s-jackson" % "4.0.6"),然后导入相关包:
import org.json4s._ import org.json4s.JsonDSL._ import org.json4s.jackson.Serialization import org.json4s.jackson.Serialization.{read, write}
2. 保留原有样例类
保持你定义的Person样例类不变:
case class Person( name: Option[String] = None, addresses: Option[Seq[String]] = Some(Seq.empty) )
3. 实现自定义序列化器
这里我们把每个地址对象拼接成"街道, 公寓号"的格式,你可以根据需求调整字符串规则:
class AddressesSerializer extends CustomSerializer[Seq[String]](format => ( // 反序列化:JSON数组 → 字符串序列 { case JArray(addressObjs) => addressObjs.map { // 解析每个地址对象的street和apt字段 case JObject(fields) => val street = fields.find(_._1 == "street").map(_._2.extract[String]).getOrElse("") val apt = fields.find(_._1 == "apt").map(_._2.extract[String]).getOrElse("") s"$street, Apt $apt" // 自定义字符串格式 // 处理非预期的JSON类型 case _ => "" } // 处理JSON中addresses为null的情况 case JNull => Seq.empty }, // 序列化:字符串序列 → JSON数组 { case addresses: Seq[String] => JArray(addresses.map { addrStr => // 根据反序列化的格式拆分字符串,还原成地址对象 val parts = addrStr.split(", Apt ") val street = if (parts.length > 0) parts(0) else "" val apt = if (parts.length > 1) parts(1) else "" // 构建JSON对象 ("street" -> street) ~ ("apt" -> apt) }) } ))
4. 配置格式并测试
把自定义序列化器加入formats配置,然后测试双向转换:
// 配置formats,加入自定义序列化器,关闭类型提示 implicit val formats: Formats = Serialization.formats(NoTypeHints) + new AddressesSerializer // 测试用JSON字符串 val jsonStr = """{ "name": "Chaitanya", "addresses": [ { "street": "20 ABC", "apt": "10" }, { "street": "10 XYZ", "apt": "D3" } ] }""" // 反序列化JSON到Person对象 val person: Person = read[Person](jsonStr) println(person) // 输出:Person(Some(Chaitanya),Some(List(20 ABC, Apt 10, 10 XYZ, Apt D3))) // 序列化Person对象回JSON val serializedJson = write(person) println(serializedJson) // 输出:{"name":"Chaitanya","addresses":[{"street":"20 ABC","apt":"10"},{"street":"10 XYZ","apt":"D3"}]}
关键说明
- 如果不需要把字符串序列回原JSON结构,可以只实现反序列化逻辑,但建议保持双向一致性,避免后续问题。
- json4s会自动处理
Option类型:若JSON中addresses缺失或为null,会自动映射为None;样例类的默认值Some(Seq.empty)也会正常生效。 - 你可以根据业务需求调整字符串转换格式,比如把地址对象直接转成JSON字符串(用
write(JObject(fields))),这样后续还原时更准确。
内容的提问来源于stack exchange,提问作者Chaitanya Chandurkar
相关产品推荐
相关产品推荐

