Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/jquery/88.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java-如何反序列化为逻辑上相同的类?_Java_Json_Serialization_Jackson - Fatal编程技术网

Java-如何反序列化为逻辑上相同的类?

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

我不想谈太多细节,所以我会尽量把它简单化。我们有一个应用程序(Java,Spring Boot),它生成一些信息,然后将这些信息序列化并存储在数据库中。要序列化它,我们使用ObjectMapper:

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拼接”。我知道,我知道。不是最好的解决方案。