如何使用JSON-B将JSON字符串反序列化至非公共Java类?
嘿,我来帮你拆解这个问题——你遇到的情况确实有点绕:JSR 367规范没说反序列化的类必须是公共的,但用Yasson(JSON-B的参考实现)时,把Widget改成包私有或者把无参构造器改成protected就触发了异常。咱们一步步来理清楚:
为啥会报错?
虽然JSR 367没强制要求类必须是公共的,但Yasson依赖Java反射来实例化对象,而Java反射本身有严格的访问控制规则:
- 如果你的
Widget类是包私有(无访问修饰符),哪怕构造器是public,Yasson的ReflectionUtils类在跨包场景下,也没法通过反射创建它的实例 - 要是构造器改成
protected,同样因为跨包的原因,反射调用会被Java的访问权限机制拦住
你贴的报错信息也印证了这点:class org.eclipse.yasson.internal.ReflectionUtils cannot access a member of class com.jsonbdemos.Widget,本质就是Java反射的访问限制在起作用。
可行的解决办法
1. 用Java 9模块系统开放权限(推荐,符合Java 9+规范)
因为你用的是Java 9,直接在模块描述符module-info.java里给Yasson开放反射权限就行:
module com.jsonbdemos { // 开放包给Yasson做反射访问 opens com.jsonbdemos to org.eclipse.yasson; }
opens关键字专门用来允许其他模块对指定包进行反射操作,这样Yasson就能合法地实例化你的包私有类或者调用protected构造器了。
2. 开启Yasson的不安全反序列化配置(仅测试环境使用)
Yasson有个内部配置可以绕过反射权限检查,但这个选项会降低安全性,只建议在测试环境试试:
修改你的App类代码,创建Jsonb时加上这个配置:
JsonbConfig config = new JsonbConfig() .withProperty("org.eclipse.yasson.enableUnsafeDeserialization", true); Jsonb jsonb = JsonbBuilder.create(config);
注意:这个依赖Yasson的内部属性,后续版本可能会移除,禁止在生产环境使用。
3. 调整类/构造器的访问级别(最稳妥的兼容方案)
如果不想折腾模块或者内部配置,最直接的方式就是:
- 保持
Widget类为public,或者 - 把无参构造器改成
public(哪怕类是包私有,只要构造器public,再配合模块开放也能行,但不如直接保持类public简单)
关于规范的补充
你说JSR 367的3.7节没找到相关规定,其实规范里提到的是字段、方法的访问规则,没明确类本身的访问级别,但规范也说了实现可以依赖Java的反射机制——而Java反射对包私有类和protected构造器的限制是平台级的,所以Yasson的行为其实符合Java规则,只是规范没强制要求实现必须处理这种边缘场景。
你提交的Issue #118很有价值,说不定后续Yasson会优化对非公共类/构造器的支持,但在那之前,上面的办法应该能解决你的问题。
内容的提问来源于stack exchange,提问作者skomisa

