如何让Json4s或其他Scala JSON库生成Java集合?
问题背景
你在使用依赖Java集合(比如java.util.ArrayList)的Java库时,遇到了一个棘手的问题:因为输入JSON是动态结构的,你只能用Json4s的parse(json).extract[Map[String, Any]]来解析,结果调用Java库时抛出了java.lang.ClassCastException: scala.collection.immutable.$colon$colon cannot be cast to java.util.ArrayList异常。
问题根源
你的猜测完全正确:Json4s默认会把JSON数组(JArray)解析成Scala的不可变List(也就是scala.collection.immutable.List,运行时类型是scala.collection.immutable.$colon$colon),而Java库期望的是java.util.ArrayList这类Java集合类型,直接强制转换自然会失败。
解决方案
下面提供几种实用的解决思路,你可以根据自己的场景选择:
1. 手动转换Scala集合为Java集合
如果已经拿到了Map[String, Any],可以递归遍历这个Map,把所有Scala List转换成Java ArrayList。比如写一个工具方法:
import scala.jdk.CollectionConverters._ import java.util.ArrayList def convertScalaCollectionsToJava(obj: Any): Any = obj match { case list: List[_] => new ArrayList(list.map(convertScalaCollectionsToJava).asJava) case map: Map[String, _] => map.mapValues(convertScalaCollectionsToJava).asJava case other => other } // 使用方式 val parsedMap = parse(json).extract[Map[String, Any]] val javaFriendlyMap = convertScalaCollectionsToJava(parsedMap).asInstanceOf[java.util.Map[String, Any]]
这样处理后,所有Scala List都会被转换成ArrayList,Java库就能正常识别了。(注:Scala 2.13+推荐用scala.jdk.CollectionConverters,旧版本可以用scala.collection.JavaConverters)
2. 让Json4s直接解析为Java集合类型
如果不需要通用的Map[String, Any],可以定义一个更贴合Java库需求的case class,指定用Java集合类型:
import java.util.ArrayList import org.json4s.DefaultFormats // 根据你的JSON结构定义对应case class case class ApiResponse(data: ArrayList[String], metadata: ArrayList[Map[String, String]]) implicit val formats = DefaultFormats val response = parse(json).extract[ApiResponse]
Json4s会自动把JSON数组解析成ArrayList,直接传给Java库即可。
3. 自定义Json4s的提取器
如果必须用Map[String, Any],可以自定义一个提取器,让Json4s在解析数组时直接生成Java ArrayList:
import org.json4s._ import org.json4s.JsonAST._ import java.util.ArrayList class JavaCollectionExtractors extends DefaultFormats { override def extract(jv: JValue, mf: Manifest[_]): Any = jv match { case JArray(arr) if mf.runtimeClass == classOf[ArrayList[_]] => val list = new ArrayList[Any]() arr.foreach(item => list.add(extract(item, mf.typeArguments.head))) list case _ => super.extract(jv, mf) } } // 使用方式 implicit val formats = new JavaCollectionExtractors val parsedMap = parse(json).extract[Map[String, ArrayList[Any]]]
这种方式能让Json4s直接生成Java ArrayList,避免后续转换步骤。
内容的提问来源于stack exchange,提问作者Tonny Tc

