Java项目中Play Framework Json.fromJson返回Scala对象引发类型转换异常的解决方法
解决Play框架中Json.fromJson解析Scala列表到Java List的ClassCastException问题
这个问题在Java+Play的项目里挺常见——Play框架默认会用Scala集合来解析JSON数组,当你直接把解析结果转成Map.class时,里面的列表其实是Scala的scala.collection.immutable.List(也就是报错里的$colon$colon),自然没法直接强转成java.util.List。下面给你几个实用的解决方案:
方案一:用JsonNode直接处理(快速解决)
不用转成Map,直接用Play的JsonNode来提取数据,它会自动帮你把Scala集合转换成Java的List:
final Result result = controller.emailFilterSave(Helpers.fakeRequest().bodyJson(Json.toJson(request)).build()); // 解析成JsonNode而不是Map JsonNode responseNode = Json.parse(Helpers.contentAsString(result)); assertEquals(HTTP_BAD_REQUEST, result.status()); assertEquals("email", responseNode.get("field").asText()); // 直接提取成Java List<String> List<String> errors = responseNode.get("errors").findValuesAsText(); assertEquals("Email address is not valid", errors.get(0));
findValuesAsText()方法会遍历JSON数组,把每个元素转成字符串并返回Java的List<String>,完美避开类型转换问题。
方案二:创建Java DTO类(类型安全推荐)
如果你的响应结构固定,最好定义一个对应的Java DTO类,让Play的Json库直接解析到这个类里,它会自动处理Scala到Java集合的映射:
首先创建一个错误响应的DTO:
import java.util.List; public class ValidationErrorResponse { private String field; private List<String> errors; // 必须要有无参构造函数,Play Json解析需要 public ValidationErrorResponse() {} // Getter和Setter方法 public String getField() { return field; } public void setField(String field) { this.field = field; } public List<String> getErrors() { return errors; } public void setErrors(List<String> errors) { this.errors = errors; } }
然后修改测试代码,直接解析到DTO:
final Result result = controller.emailFilterSave(Helpers.fakeRequest().bodyJson(Json.toJson(request)).build()); ValidationErrorResponse response = Json.fromJson(Json.parse(Helpers.contentAsString(result)), ValidationErrorResponse.class); assertEquals(HTTP_BAD_REQUEST, result.status()); assertEquals("email", response.getField()); assertEquals("Email address is not valid", response.getErrors().get(0));
这种方法不仅解决了类型转换问题,还让代码更具可读性和类型安全性,后期维护也更方便。
方案三:配置Jackson强制使用Java集合(底层配置)
如果上面的方案都不适用,你可以配置Play的Jackson解析器,强制它把JSON数组解析成Java集合:
在conf/application.conf中添加如下配置:
play.json.jackson.config = { com.fasterxml.jackson.databind.DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY = true }
或者在代码中自定义ObjectMapper:
import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; // ... final Result result = controller.emailFilterSave(Helpers.fakeRequest().bodyJson(Json.toJson(request)).build()); ObjectMapper mapper = new ObjectMapper(); // 强制使用Java数组/集合来解析JSON数组 mapper.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true); JsonNode responseNode = Json.parse(Helpers.contentAsString(result)); Map<String, Object> response = mapper.treeToValue(responseNode, Map.class); assertEquals(HTTP_BAD_REQUEST, result.status()); assertEquals("email", response.get("field")); assertEquals("Email address is not valid", ((List<String>)response.get("errors")).get(0));
这个配置会让Jackson默认用Java的集合类型来解析JSON数组,这样再转成Map时,里面的列表就是java.util.List了。
内容的提问来源于stack exchange,提问作者Jamie G
相关产品推荐
相关产品推荐

