如何在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
相关产品推荐
相关产品推荐

