如何使用 JSON-B 将 JSON 字符串反序列化为非 public class?
How to deserialize a JSON string to a non-public class using JSON-B?
我创建了一个简单的 Java 9 Maven 应用程序,其中包含两个 classes 以使用 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>
应用程序运行良好,但如果我将 class Widget 的访问级别从 public 更改为什么都没有(即 "package private")
调用 Jsonb.fromJson():
时抛出 IllegalAccessException
Exception in thread "main" javax.json.bind.JsonbException: Can't
create instance at
org.eclipse.yasson.internal.ReflectionUtils.lambda$createNoArgConstructorInstance(ReflectionUtils.java:191)
at java.base/java.security.AccessController.doPrivileged(Native
Method) at
org.eclipse.yasson.internal.ReflectionUtils.createNoArgConstructorInstance(ReflectionUtils.java:186)
at
org.eclipse.yasson.internal.serializer.ObjectDeserializer.getInstance(ObjectDeserializer.java:92)
at
org.eclipse.yasson.internal.serializer.AbstractContainerDeserializer.deserialize(AbstractContainerDeserializer.java:62)
at
org.eclipse.yasson.internal.Unmarshaller.deserializeItem(Unmarshaller.java:57)
at
org.eclipse.yasson.internal.Unmarshaller.deserialize(Unmarshaller.java:50)
at
org.eclipse.yasson.internal.JsonBinding.deserialize(JsonBinding.java:45)
at
org.eclipse.yasson.internal.JsonBinding.fromJson(JsonBinding.java:52)
at com.jsonbdemos.App.main(App.java:15) Caused by:
java.lang.IllegalAccessException: class
org.eclipse.yasson.internal.ReflectionUtils cannot access a member of
class com.jsonbdemos.Widget with modifiers "public" at
java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:361)
at
java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:589)
at
java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:479)
at
org.eclipse.yasson.internal.ReflectionUtils.lambda$createNoArgConstructorInstance(ReflectionUtils.java:189)
... 9 more
我在规范 (JSR 367:"JSON-B: Java™ API for JSON Binding") 中没有看到任何内容(在 3.7 Java Class 部分)需要 public class 用于反序列化。
关于如何使用 JSON-B 反序列化为非 public 的 class 实例的任何建议?
更新 (5/2/18):
JSR 367 声明“传递给反序列化操作的任何实例必须具有 public 或受保护的无参数构造函数”,但同样的错误也会发生如果构造函数是 protected 而不是 public。
我已报告该问题:Deserialization still not working with a protected no-arg constructor #118
我测试了一些变体,结果如下:
独立class(自己的源文件):
- class=public, ctor=public = 成功
- class=public, ctor=protected = 成功
- class=public, ctor=pkg-protected = 非法访问
- class=pkg-protected, ctor=public = 非法访问
静态内部class:
- class=public, ctor=public = 成功
- class=受保护,ctor=public = 成功
- class=pkg-protected, ctor=public = 非法访问
- class=public, ctor=protected = 成功
- class=public, ctor=pkg-protected = 非法访问
非静态内部class:
- class=public, ctor=public = 非法访问
由此得出的要点是:
- Public 和 protected 有效,但 package-protected 或更少不起作用(class 和 ctor 的可见性必须等于或高于 protected)。
- 静态内部 classes 与独立 classes
具有相同的行为
- 非静态内部 classes 不可访问,因为它们需要外部 class 的实例来实例化
我创建了一个简单的 Java 9 Maven 应用程序,其中包含两个 classes 以使用 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>
应用程序运行良好,但如果我将 class Widget 的访问级别从 public 更改为什么都没有(即 "package private") 调用 Jsonb.fromJson():
时抛出 IllegalAccessExceptionException in thread "main" javax.json.bind.JsonbException: Can't create instance at org.eclipse.yasson.internal.ReflectionUtils.lambda$createNoArgConstructorInstance(ReflectionUtils.java:191) at java.base/java.security.AccessController.doPrivileged(Native Method) at org.eclipse.yasson.internal.ReflectionUtils.createNoArgConstructorInstance(ReflectionUtils.java:186) at org.eclipse.yasson.internal.serializer.ObjectDeserializer.getInstance(ObjectDeserializer.java:92) at org.eclipse.yasson.internal.serializer.AbstractContainerDeserializer.deserialize(AbstractContainerDeserializer.java:62) at org.eclipse.yasson.internal.Unmarshaller.deserializeItem(Unmarshaller.java:57) at org.eclipse.yasson.internal.Unmarshaller.deserialize(Unmarshaller.java:50) at org.eclipse.yasson.internal.JsonBinding.deserialize(JsonBinding.java:45) at org.eclipse.yasson.internal.JsonBinding.fromJson(JsonBinding.java:52) at com.jsonbdemos.App.main(App.java:15) Caused by: java.lang.IllegalAccessException: class org.eclipse.yasson.internal.ReflectionUtils cannot access a member of class com.jsonbdemos.Widget with modifiers "public" at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:361) at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:589) at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:479) at org.eclipse.yasson.internal.ReflectionUtils.lambda$createNoArgConstructorInstance(ReflectionUtils.java:189) ... 9 more
我在规范 (JSR 367:"JSON-B: Java™ API for JSON Binding") 中没有看到任何内容(在 3.7 Java Class 部分)需要 public class 用于反序列化。
关于如何使用 JSON-B 反序列化为非 public 的 class 实例的任何建议?
更新 (5/2/18):
JSR 367 声明“传递给反序列化操作的任何实例必须具有 public 或受保护的无参数构造函数”,但同样的错误也会发生如果构造函数是 protected 而不是 public。
我已报告该问题:Deserialization still not working with a protected no-arg constructor #118
我测试了一些变体,结果如下:
独立class(自己的源文件):
- class=public, ctor=public = 成功
- class=public, ctor=protected = 成功
- class=public, ctor=pkg-protected = 非法访问
- class=pkg-protected, ctor=public = 非法访问
静态内部class:
- class=public, ctor=public = 成功
- class=受保护,ctor=public = 成功
- class=pkg-protected, ctor=public = 非法访问
- class=public, ctor=protected = 成功
- class=public, ctor=pkg-protected = 非法访问
非静态内部class:
- class=public, ctor=public = 非法访问
由此得出的要点是:
- Public 和 protected 有效,但 package-protected 或更少不起作用(class 和 ctor 的可见性必须等于或高于 protected)。
- 静态内部 classes 与独立 classes 具有相同的行为
- 非静态内部 classes 不可访问,因为它们需要外部 class 的实例来实例化