Java-如何反序列化为逻辑上相同的类?
我不想谈太多细节,所以我会尽量把它简单化。我们有一个应用程序(Java,Spring Boot),它生成一些信息,然后将这些信息序列化并存储在数据库中。要序列化它,我们使用ObjectMapper:Java-如何反序列化为逻辑上相同的类?,java,json,serialization,jackson,Java,Json,Serialization,Jackson,我不想谈太多细节,所以我会尽量把它简单化。我们有一个应用程序(Java,Spring Boot),它生成一些信息,然后将这些信息序列化并存储在数据库中。要序列化它,我们使用ObjectMapper: final ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule()); mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMEST
final ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper.enableDefaultTyping(); // default to using DefaultTyping.OBJECT_AND_NON_CONCRETE
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
manifestJson = mapper.writeValueAsString(manifest);
然后JSON存储在数据库中。哎呀<代码>清单是一个复杂的对象,包含许多用于创建最终产品的其他对象和信息。问题是当我们尝试使用这些信息来生成一个新对象时。原始类的FQN与信息一起存储(有意义)。但信息的读回是在不同的位置/应用程序中进行的。因此,虽然与json一起存储的原始名称空间是类型a.b.c.d.e.f.class1,但我们现在尝试将其读回类a.b.c.g.h.i.f.class1中。。。从逻辑上讲,class1在两个名称空间中是相同的,实际上它是直接从一个名称空间复制到另一个名称空间的。唯一的区别是中间的包名
当然,当我们尝试反序列化JSON时,它会抛出一个关于找不到类型的错误,原始类型在原始项目/应用程序中,“新”类型在当前项目中。我们可以从辅助应用程序中引用原始应用程序,但出于功能原因,我们正在尝试保持两者之间的解耦
所以问题是,如何从一个项目中获取一个序列化的对象,然后将其反序列化到另一个逻辑相同的类中?默认情况下,当不使用键入时,您可以生成一个
JSON
负载,您可以稍后将其反序列化到适合此JSON
的任何模型。或者,如果您根本不想创建模型,甚至可以将对象映射到或/和列表中
在您的情况下,当两个具有两种不同模型的不同应用程序使用JSON
有效负载时,您不应该附加类信息,因为它不会为其他模型提供任何额外信息。一个JSON
可以通过多种方式进行反序列化,并用于不同的目的,因此将其与一个模型链接会导致您在示例中注意到的问题。因此,如果可能,请禁用键入并显式提供所有信息,以便稍后从JSON
重新创建同一对象
如果不可能,只需提供自定义的TypeIdResolver
,并将一个类从一个模型映射到另一个模型中的类似类。一个简单的例子展示了一个想法:
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.DatabindContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver;
import com.fasterxml.jackson.databind.jsontype.impl.ClassNameIdResolver;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.type.SimpleType;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class JsonApp {
public static void main(String[] args) throws Exception {
SimpleModule model2Module = new SimpleModule("Model2");
model2Module.setMixInAnnotation(PojoA2.class, CustomJsonTypeIdResolverMixIn.class);
model2Module.setMixInAnnotation(PojoB2.class, CustomJsonTypeIdResolverMixIn.class);
model2Module.setMixInAnnotation(PojoC2.class, CustomJsonTypeIdResolverMixIn.class);
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
mapper.registerModule(model2Module);
String json = mapper.writeValueAsString(new PojoA());
System.out.println(json);
System.out.println(mapper.readValue(json, PojoA.class));
System.out.println(mapper.readValue(json, PojoA2.class));
}
}
@JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM)
@JsonTypeIdResolver(value = Model2ClassNameIdResolver.class)
interface CustomJsonTypeIdResolverMixIn { }
class Model2ClassNameIdResolver extends ClassNameIdResolver {
private final Map<String, JavaType> types = new HashMap<>();
public Model2ClassNameIdResolver() {
super(null, null);
types.put("com.celoxity.PojoA", SimpleType.constructUnsafe(PojoA2.class));
types.put("com.celoxity.PojoB", SimpleType.constructUnsafe(PojoB2.class));
types.put("com.celoxity.PojoC", SimpleType.constructUnsafe(PojoC2.class));
}
@Override
public JavaType typeFromId(DatabindContext context, String id) throws IOException {
JavaType javaType = types.get(id);
if (javaType != null) {
return javaType;
}
return super.typeFromId(context, id);
}
}
class PojoA2 {
private PojoB2 b;
}
class PojoB2 {
private List<PojoC2> c;
}
class PojoC2 {
private String s;
private int i;
}
class PojoA {
private PojoB b;
}
class PojoB {
private List<PojoC> c;
}
class PojoC {
private String s;
private int i;
}
后来:
PojoA{b=PojoB{c=[PojoC{s='Vika', i=22}]}}
PojoA2{b=PojoB2{c=[PojoC2{s='Vika', i=22}]}}
默认情况下,当不使用键入时,您可以生成一个JSON
有效负载,稍后您可以将其反序列化到适合此JSON
的任何模型。或者,如果您根本不想创建模型,甚至可以将对象映射到或/和列表中
在您的情况下,当两个具有两种不同模型的不同应用程序使用JSON
有效负载时,您不应该附加类信息,因为它不会为其他模型提供任何额外信息。一个JSON
可以通过多种方式进行反序列化,并用于不同的目的,因此将其与一个模型链接会导致您在示例中注意到的问题。因此,如果可能,请禁用键入并显式提供所有信息,以便稍后从JSON
重新创建同一对象
如果不可能,只需提供自定义的TypeIdResolver
,并将一个类从一个模型映射到另一个模型中的类似类。一个简单的例子展示了一个想法:
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.DatabindContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver;
import com.fasterxml.jackson.databind.jsontype.impl.ClassNameIdResolver;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.type.SimpleType;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class JsonApp {
public static void main(String[] args) throws Exception {
SimpleModule model2Module = new SimpleModule("Model2");
model2Module.setMixInAnnotation(PojoA2.class, CustomJsonTypeIdResolverMixIn.class);
model2Module.setMixInAnnotation(PojoB2.class, CustomJsonTypeIdResolverMixIn.class);
model2Module.setMixInAnnotation(PojoC2.class, CustomJsonTypeIdResolverMixIn.class);
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
mapper.registerModule(model2Module);
String json = mapper.writeValueAsString(new PojoA());
System.out.println(json);
System.out.println(mapper.readValue(json, PojoA.class));
System.out.println(mapper.readValue(json, PojoA2.class));
}
}
@JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM)
@JsonTypeIdResolver(value = Model2ClassNameIdResolver.class)
interface CustomJsonTypeIdResolverMixIn { }
class Model2ClassNameIdResolver extends ClassNameIdResolver {
private final Map<String, JavaType> types = new HashMap<>();
public Model2ClassNameIdResolver() {
super(null, null);
types.put("com.celoxity.PojoA", SimpleType.constructUnsafe(PojoA2.class));
types.put("com.celoxity.PojoB", SimpleType.constructUnsafe(PojoB2.class));
types.put("com.celoxity.PojoC", SimpleType.constructUnsafe(PojoC2.class));
}
@Override
public JavaType typeFromId(DatabindContext context, String id) throws IOException {
JavaType javaType = types.get(id);
if (javaType != null) {
return javaType;
}
return super.typeFromId(context, id);
}
}
class PojoA2 {
private PojoB2 b;
}
class PojoB2 {
private List<PojoC2> c;
}
class PojoC2 {
private String s;
private int i;
}
class PojoA {
private PojoB b;
}
class PojoB {
private List<PojoC> c;
}
class PojoC {
private String s;
private int i;
}
后来:
PojoA{b=PojoB{c=[PojoC{s='Vika', i=22}]}}
PojoA2{b=PojoB2{c=[PojoC2{s='Vika', i=22}]}}
你解决这个问题了吗?我的回答有用吗?@MichałZiober-不幸的是,由于时间限制,我无法充分关注您的解决方案以满足我们的需要,这还意味着重构许多类,并可能影响我们的插件架构。我们最终尝试了很多方法,但最终,我们尝试了一些“简单”但有点粗糙的方法-我们通过执行String.Replace将旧名称空间更改为加载数据所需的新名称空间来进行“DNA拼接”。我知道,我知道。不是最好的解决方案。你解决这个问题了吗?我的回答有用吗?@MichałZiober-不幸的是,由于时间限制,我无法充分关注您的解决方案以满足我们的需要,这还意味着重构许多类,并可能影响我们的插件架构。我们最终尝试了很多方法,但最终,我们尝试了一些“简单”但有点粗糙的方法-我们通过执行String.Replace将旧名称空间更改为加载数据所需的新名称空间来进行“DNA拼接”。我知道,我知道。不是最好的解决方案。