如何使用 JSON-B 将 JSON 字符串反序列化为非公共类?

Posted

技术标签:

【中文标题】如何使用 JSON-B 将 JSON 字符串反序列化为非公共类?【英文标题】:How to deserialize a JSON string to a non-public class using JSON-B? 【发布时间】:2018-06-15 22:01:32 【问题描述】:

我创建了一个简单的 Java 9 Maven 应用程序,其中包含两个类来使用 JSON-B 测试 JSON 的序列化和反序列化。代码如下:

package com.jsonbdemos;
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import javax.json.bind.JsonbConfig;

public class App 

    public static void main(String[] args) 
        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig());
        String jsonData = "\"creationDate\":\"2018-01-05\"";

        // Create Widget object from JSON string.
        Widget widget = jsonb.fromJson(jsonData, Widget.class);
        System.out.println("JSON => object: " + widget.toString());

        // Serialize Widget object to JSON string.
        String jsonFromObject = jsonb.toJson(widget);
        System.out.println("object => JSON: " + jsonFromObject);
    


package com.jsonbdemos;
import java.time.LocalDate;

public class Widget  // IllegalAccessException if "public" is removed.
    private LocalDate creationDate;
    public Widget() 

    @Override
    public String toString()  return "creationDate=" + creationDate; 
    public LocalDate getCreationDate()  return creationDate; 
    public void setCreationDate(LocalDate creationDate)  this.creationDate = creationDate; 

在 pom.xml 中有 JSON-B (Eclipse Yasson) 的最新版本参考实现的依赖:

<dependency>
  <groupId>org.glassfish</groupId>
  <artifactId>javax.json</artifactId>
  <version>[1.1.2,)</version>
</dependency>
<dependency>
  <groupId>javax.json.bind</groupId>
  <artifactId>javax.json.bind-api</artifactId>
  <version>[1.0,)</version>
</dependency>
<dependency>
  <groupId>org.eclipse</groupId>
  <artifactId>yasson</artifactId>
  <version>[1.0.0,)</version>
</dependency>

应用程序运行良好,但如果我将类 Widget 的访问级别从 public 更改为无(即“包私有”) 调用Jsonb.fromJson()时抛出IllegalAccessException:

线程“主”javax.json.bind.JsonbException 中的异常:不能 创建实例 org.eclipse.yasson.internal.ReflectionUtils.lambda$createNoArgConstructorInstance$1(ReflectionUtils.java:191) 在 java.base/java.security.AccessController.doPrivileged(本机 方法)在 org.eclipse.yasson.internal.ReflectionUtils.createNoArgConstructorInstance(ReflectionUtils.java:186) 在 org.eclipse.yasson.internal.serializer.ObjectDeserializer.getInstance(ObjectDeserializer.java:92) 在 org.eclipse.yasson.internal.serializer.AbstractContainerDeserializer.deserialize(AbstractContainerDeserializer.java:62) 在 org.eclipse.yasson.internal.Unmarshaller.deserializeItem(Unmarshaller.java:57) 在 org.eclipse.yasson.internal.Unmarshaller.deserialize(Unmarshaller.java:50) 在 org.eclipse.yasson.internal.JsonBinding.deserialize(JsonBinding.java:45) 在 org.eclipse.yasson.internal.JsonBinding.fromJson(JsonBinding.java:52) 在 com.jsonbdemos.App.main(App.java:15) 原因: java.lang.IllegalAccessException:类 org.eclipse.yasson.internal.ReflectionUtils 无法访问的成员 类 com.jsonbdemos.Widget 带有修饰符“public” at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:361) 在 java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:589) 在 java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:479) 在 org.eclipse.yasson.internal.ReflectionUtils.lambda$createNoArgConstructorInstance$1(ReflectionUtils.java:189) ... 9 更多

我在规范 (JSR 367:"JSON-B: Java™ API for JSON Binding")(在 3.7 Java 类部分)中没有看到任何需要公共类进行反序列化的内容。

关于如何使用 JSON-B 反序列化为不公开的类实例有什么建议吗?

更新(2018 年 5 月 2 日):

JSR 367 声明“传递给反序列化操作的任何实例都必须具有公共或受保护的无参数构造函数”,但如果构造函数受到受保护 而不是 public

我已经报告了这个问题:Deserialization still not working with a protected no-arg constructor #118

【问题讨论】:

问题已解决。 这里有同样的问题。对我来说,它与 WildFly 10.1.0.Final & Java 8 一起工作,并停止与 WildFly 18.0.0.Final & Java 13 一起工作???? 【参考方案1】:

我对此进行了一些变体测试,结果如下:

独立类(自己的源文件):

class=公开,ctor=公开=成功 class=公开,ctor=protected = 成功 class=public,ctor=pkg-protected = 非法访问 class=pkg-protected,ctor=public = 非法访问

静态内部类:

class=公开,ctor=公开=成功 class=受保护,ctor=public = 成功 class=pkg-protected,ctor=public = 非法访问 class=公开,ctor=protected = 成功 class=public,ctor=pkg-protected = 非法访问

非静态内部类:

class=public,ctor=public = 非法访问

其中的关键点是:

    公共和受保护的作品,但包保护或更少的作品不起作用(类和 ctor 的可见性必须与受保护的相同或更高)。 静态内部类与独立类具有相同的行为 非静态内部类不可访问,因为它们需要外部类的实例来实例化

【讨论】:

除非我理解错了,否则第 3 条不太正确。我没有尝试过,但是从外部类绑定一个嵌套类,你必须有一个外部类的实例,应该可以工作。这在JSR 367 section "3.7.2 Nested Classes" 中有说明:“实现必须支持公共和受保护的嵌套类的绑定...”。无论如何,我接受你的回答,因为其他一切都符合规范,你的回答让我重新审视这个问题。我不再有例外。大概这是我使用的早期版本的 Yasson 中的一个错误。 仅供参考,我尝试使用嵌套类进行绑定。 Jsonb.toJson() 有效,但 Jsonb.fromJson() 失败并出现 JsonbException: Can't create instance of a class: ... No default constructor found. Jsonb 找不到默认构造函数的原因嵌套类,即使我提供了一个 is given here:“即使是默认的无参数构造函数......也成为嵌套类的单参数构造函数”。另见eclipse.org/forums/index.php/t/1086296

以上是关于如何使用 JSON-B 将 JSON 字符串反序列化为非公共类?的主要内容,如果未能解决你的问题,请参考以下文章

JSON-B和Yasson详解

如何正确使用javascript反序列化将json字符串转换为复杂对象?

如何使用 System.Text.Json 将 json 字符串或流反序列化到 Dictionary<string,string>

JSON-B 精选课程,入门级菜鸟必读!

将JSON字符串反序列化为指定的.NET对象类型

如何将对象的json数组反序列化为字典[重复]