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

如何在Json4s解析JSON时定位缺失的必填字段

用Json4s定位解析时缺失的必填字段

针对你遇到的问题,有几种可靠的方式可以准确定位缺失的必填字段:

方式一:直接解析默认异常信息(快速但依赖内部实现)

Json4s在提取缺失字段时抛出的IllegalArgumentException,默认消息格式为No usable value for [字段名]。你可以直接从消息中提取字段名,无需复杂处理:

修改你的异常处理逻辑:

def parseJson[M](json: String)(implicit m: Manifest[M]): Either[ErrorResponse, M] = {
    try
      Right(JsonHelper.parse(json).extract[M])
    catch {
      case NonFatal(th) =>
        th.getCause.getCause match {
          case e: IllegalArgumentException =>
            val fieldName = e.getMessage.replaceFirst("No usable value for ", "")
            val msg = s"Missing required field: $fieldName"
            error(s"Invalid JSON - $json", e)
            Left(handle(exception = EmptyFieldException(msg)))
          case _ =>
            error(s"Invalid JSON - $json", th)
            Left(handle(exception = new IllegalArgumentException("Invalid Json", th)))
        }
    }
  }

这种方式简单快捷,但依赖Json4s的异常消息格式,若后续版本修改消息内容则会失效。

方式二:反射校验字段存在性(通用可靠)

通过反射获取case class的所有字段,提前检查JSON中是否包含这些必填字段,主动抛出明确的缺失字段信息:

首先添加一个工具方法获取case class的字段名:

import scala.reflect.runtime.universe._

def getCaseClassFields[T: TypeTag]: List[String] = {
  typeOf[T].members.collect {
    case m: MethodSymbol if m.isCaseAccessor => m.name.toString
  }.toList
}

然后修改parseJson方法,先校验字段存在性再提取:

def parseJson[M: Manifest: TypeTag](json: String): Either[ErrorResponse, M] = {
  try {
    val jValue = JsonHelper.parse(json)
    // 获取case class所有必填字段(可根据需求调整,比如过滤可选字段)
    val requiredFields = getCaseClassFields[M]
    // 匹配JSON中的字段名(支持DefaultFormats的命名映射,比如驼峰转下划线)
    val missingFields = requiredFields.filter { field =>
      val jsonFieldName = JsonHelper.formats.nameMapper(field)
      jValue \ jsonFieldName == JNothing
    }

    if (missingFields.nonEmpty) {
      val msg = s"Missing required fields: ${missingFields.mkString(", ")}"
      error(s"Invalid JSON - $json", new IllegalArgumentException(msg))
      Left(handle(exception = EmptyFieldException(msg)))
    } else {
      Right(jValue.extract[M])
    }
  } catch {
    case NonFatal(th) =>
      error(s"Invalid JSON - $json", th)
      Left(handle(exception = new IllegalArgumentException("Invalid Json", th)))
  }
}

这种方式无需为每个case class编写自定义逻辑,支持命名策略(如SnakeCase),通用性强且不依赖内部异常格式。

方式三:自定义Format显式处理(精确可控)

对于重要的大型case class,可自定义Format显式解析每个字段,直接在缺失时抛出明确错误:

以你的示例case class为例:

case class Person(name: String, profession: String)

object Person {
  implicit val formats: Format[Person] = new Format[Person] {
    override def reads(json: JValue): Person = {
      // 解析必填字段,缺失时直接抛出明确异常
      val name = (json \ "name").extractOpt[String].getOrElse {
        throw new IllegalArgumentException("Missing required field: name")
      }
      val profession = (json \ "profession").extractOpt[String].getOrElse {
        throw new IllegalArgumentException("Missing required field: profession")
      }
      Person(name, profession)
    }

    override def writes(person: Person): JValue = {
      JObject(
        "name" -> JString(person.name),
        "profession" -> JString(person.profession)
      )
    }
  }
}

这种方式可以精确控制每个字段的解析逻辑和错误信息,但需要为每个case class编写自定义Format,适合对错误信息要求极高的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 13:55:37