我正在调整这段杰克逊代码:
@JsonDeserialize(as = EntityImpl.class)
public interface Entity { ... }
Run Code Online (Sandbox Code Playgroud)
即使对于嵌套的Entity对象,原始代码也能很好地工作.
如何使用新的json-b规范?我尝试使用@JsonbTypeDeserializer但是
javax.json.bind.JsonbException:无法推断用于解组的类型:Entity
这是我的反序列化代码:
public class EntityDeserializer implements JsonbDeserializer<Entity> {
@Override
public Entity deserialize(JsonParser parser, DeserializationContextdeserializationContext, Type runtimeType) {
Class<? extends Entity> entityClass = EntityImpl.class.asSubclass(Entity.class);
return deserializationContext.deserialize(entityClass, parser);
}
}
Run Code Online (Sandbox Code Playgroud)
任何提示或帮助非常感谢:-)
JSON-B没有声明序列化多态类型的标准方法.但您可以使用自定义序列化器和反序列化器手动实现它.我将在一个简单的样本上解释它.
想象一下,你有Shape接口和两个类Square并Circle实现它.
public interface Shape {
double surface();
double perimeter();
}
public static class Square implements Shape {
private double side;
public Square() {
}
public Square(double side) {
this.side = side;
}
public double getSide() {
return side;
}
public void setSide(double side) {
this.side = side;
}
@Override
public String toString() {
return String.format("Square[side=%s]", side);
}
@Override
public double surface() {
return side * side;
}
@Override
public double perimeter() {
return 4 * side;
}
}
public static class Circle implements Shape {
private double radius;
public Circle() {
}
public Circle(double radius) {
this.radius = radius;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
@Override
public String toString() {
return String.format("Circle[radius=%s]", radius);
}
@Override
public double surface() {
return Math.PI * radius * radius;
}
@Override
public double perimeter() {
return 2 * Math.PI * radius;
}
}
Run Code Online (Sandbox Code Playgroud)
您需要序列化和反序列化可以包含任何Shape实现的List .
序列化开箱即用:
JsonbConfig config = new JsonbConfig().withFormatting(true);
Jsonb jsonb = JsonbBuilder.create(config);
// Create a sample list
List<SerializerSample.Shape> shapes = Arrays.asList(
new SerializerSample.Square(2),
new SerializerSample.Circle(5));
// Serialize
String json = jsonb.toJson(shapes);
System.out.println(json);
Run Code Online (Sandbox Code Playgroud)
结果将是:
[
{
"side": 2.0
},
{
"radius": 5.0
}
]
Run Code Online (Sandbox Code Playgroud)
没关系,但如果你尝试反序列化它就行不通.在反序列化期间,JSON-B需要创建一个实例,Square或者Circle在JSON文档中没有关于对象类型的信息.
为了解决这个问题,我们需要在那里手动添加这些信息.序列化器和反序列化器将有所帮助.我们可以创建一个序列化器,它将一种序列化对象放在JSON文档和反序列化器中,然后读取它并创建一个合适的实例.它可以这样做:
public static class ShapeSerializer implements JsonbSerializer<SerializerSample.Shape> {
@Override
public void serialize(SerializerSample.Shape shape, JsonGenerator generator, SerializationContext ctx) {
generator.writeStartObject();
ctx.serialize(shape.getClass().getName(), shape, generator);
generator.writeEnd();
}
}
public static class ShapeDeserializer implements JsonbDeserializer<SerializerSample.Shape> {
@Override
public SerializerSample.Shape deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) {
parser.next();
String className = parser.getString();
parser.next();
try {
return ctx.deserialize(Class.forName(className).asSubclass(Shape.class), parser);
} catch (ClassNotFoundException e) {
e.printStackTrace();
throw new JsonbException("Cannot deserialize object.");
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在我们需要将其插入JSON-B引擎并尝试序列化.在序列化/反序列化期间,您不应忘记将泛型类型传递给JSON-B引擎.否则它将无法正常工作.
// Create JSONB engine with pretty output and custom serializer/deserializer
JsonbConfig config = new JsonbConfig()
.withFormatting(true)
.withSerializers(new SerializerSample.ShapeSerializer())
.withDeserializers(new SerializerSample.ShapeDeserializer());
Jsonb jsonb = JsonbBuilder.create(config);
// Create a sample list
List<SerializerSample.Shape> shapes = Arrays.asList(
new SerializerSample.Square(2),
new SerializerSample.Circle(5));
// Type of our list
Type type = new ArrayList<SerializerSample.Shape>() {}.getClass().getGenericSuperclass();
// Serialize
System.out.println("Serialization:");
String json = jsonb.toJson(shapes);
System.out.println(json);
Run Code Online (Sandbox Code Playgroud)
序列化的结果将是:
[
{
"jsonb.sample.SerializerSample$Square": {
"side": 2.0
}
},
{
"jsonb.sample.SerializerSample$Circle": {
"radius": 5.0
}
}
Run Code Online (Sandbox Code Playgroud)
]
您会看到添加了对象类型ShapeSerializer.现在让我们尝试反序列化它并打印结果:
// Deserialize
List<SerializerSample.Shape> deserializedShapes = jsonb.fromJson(json, type);
// Print results
System.out.println("Deserialization:");
for (SerializerSample.Shape shape : deserializedShapes) {
System.out.println(shape);
}
Run Code Online (Sandbox Code Playgroud)
结果是:
Square[side=2.0]
Circle[radius=5.0]
Run Code Online (Sandbox Code Playgroud)
所以,它完美有效.希望能帮助到你.:)
编辑:JSON-B 3.0 直接支持多态类型。看这里。
\n@Dmitry 给出的答案对我帮助很大,但它有两个缺陷:
\n1:使用 JSON 中的完整类名是一个严重的安全问题。攻击者可能会让您反序列化任意类,并且某些类可能会导致远程代码执行。您必须使用映射(或将允许的子类列入白名单)。例如:
\n[\n {\n "square": {\n "side": 2.0\n }\n },\n {\n "circle": {\n "radius": 5.0\n }\n }\n]\nRun Code Online (Sandbox Code Playgroud)\n2:将实际对象包装在类型中可能不是我们想要的 JSON 的样子。或者,当我们从不同的系统接收 JSON 时,我们通常会得到不同的结构,例如带有字段@type。并且JSON中没有定义字段顺序;制片人有时可能会发送@type最后一个。例如
[\n {\n "@type":"square",\n "side": 2.0\n },\n {\n "radius": 5.0,\n "@type":"circle"\n }\n]\nRun Code Online (Sandbox Code Playgroud)\n我找到的解决方案是这样的:
\npublic class ShapeDeserializer implements JsonbDeserializer<Shape> {\n @Override public Shape deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) {\n JsonObject value = parser.getObject();\n String type = value.getString("@type", "null");\n return JSONB.fromJson(value.toString(), classFor(type));\n }\n\n private Class<? extends Shape> classFor(String type) {\n switch (type) {\n case "circle":\n return Circle.class;\n case "square":\n return Square.class;\n default:\n throw new JsonbException("unknown shape type " + type);\n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n请注意,从Parser光标处读取;但我们需要重新读取完整的对象 \xe2\x80\x93 记住:可能@type不是第一个字段。由于没有用于重置光标的 API,我通过调用生成一个新的 JSON 字符串,toString并使用它来启动一个新的解析器。这并不完美,但对性能的影响应该是可以接受的。YMMV。
我渴望看到 JSON-B 直接支持多态类型,如此处所述。
\n