如何在Java Jackson中创建从多个文件读取/写入的自定义(反)序列化程序?

How to create customized (de-)serializer in Java Jackson that read/writes from multiple files?

提问人:Łukasz Wiecheć 提问时间:11/3/2023 最后编辑:Łukasz Wiecheć 更新时间:11/15/2023 访问量:32

问:

我试图把我的想法总结在以下几点上:我想使用Jackson(反)序列化来自多个文件的POJO - 序列化程序会写一个“每个类的文件”。

我尝试序列化的对象包含对其他对象的引用:

public class DumpObject {
  String field1;
  SomeClass field2;
  SomeOtherClass field3;
}

最后,我希望序列化写入 3 个文件: ,如下所示:dumpobject.json

{ "field1": "value1" }

然后用序列化的内容和类似的 for .someclass.jsonSomeClassSomeOtherClass

我正在努力评估最好的方法是否是“告诉”在这种“每个类的文件”场景中创建(反)序列化程序/ObjectMapper

到目前为止,我已经尝试了将所有 JSON 转储到单个文件的简单(反)序列化程序。

更新我想问题是是否可以根据当前正在序列化的类配置为将序列化更改为多个 s / 'JsonGenerator's。ObjectMapperObjectWriter

更新 2我已经“修复”了这个问题,方法是将 的实例传递给序列化程序,然后在其中为我想在另一个文件中序列化的列表的每个元素创建:ZipFOutputStreamZipEntries

public class CustomDBDumpListSerializer extends StdSerializer<DBDumpList> {

    /** the name of the 'main' entry in the ZIP file*/
    public static final String MAIN_ENTRY = "DBDumpList.json";

    private final ZipOutputStream zos;

    public CustomDBDumpListSerializer(ZipOutputStream zos) {
        this(null, zos);
    }

    public CustomDBDumpListSerializer(Class<DBDumpList> t, ZipOutputStream zos) {
        super(t);
        this.zos = zos;
    }

    @Override
    public void serialize(DBDumpList toSerialize, JsonGenerator jgen, SerializerProvider provider)
            throws IOException, JsonProcessingException {

        List<String> repositories = new ArrayList<>(); // will hold repositories

        // the entries need to be processed in 'natural order', here following the DBDumpDto's
        toSerialize.sort(Comparator.comparing(DBDumpDto::getSyncOrder));
        int i = 1;

        for(DBDumpDto dto : toSerialize) {
            // we need to maintain the uniqueness of the zip entries; repos can be
            ZipEntry ze = new ZipEntry(i + "_" + dto.getRepository() + ".json");
            zos.putNextEntry(ze);
            JsonGenerator gen2 = jgen.getCodec().getFactory().createGenerator(zos);
            gen2.writePOJO(dto);
            log.debug("wrote to zip entry {}", ze);
            repositories.add(ze.getName());
            i++;
        }

        ZipEntry zz = new ZipEntry(MAIN_ENTRY);
        zos.putNextEntry(zz);
        jgen.writeStartObject();
        provider.defaultSerializeField("dateLastCheck", toSerialize.getDateLastCheck(), jgen);
        provider.defaultSerializeField("repositories", repositories, jgen);
        jgen.writeEndObject();
    }
}

反序列化程序的构造类似:传递一个实例,以便它可以在 zip 文件中找到序列化的 JSON,同时遍历存储库列表:ZipFile

public class CustomDBDumpListDeserializer extends StdDeserializer<DBDumpList> {

    private final ZipFile zipfile;

    public CustomDBDumpListDeserializer(ZipFile zipfile) {
        this(null, zipfile);
    }

    public CustomDBDumpListDeserializer(Class<DBDumpList> t, ZipFile zipfile) {
        super(t);
        this.zipfile = zipfile;
    }

    @Override
    public DBDumpList deserialize(JsonParser aParser, DeserializationContext aContext) throws IOException, JsonProcessingException {
        DBDumpList ret = new DBDumpList();

        ObjectCodec codec = aParser.getCodec();
        JsonNode node = codec.readTree(aParser);
        JsonNode dateNode = node.get("dateLastCheck");

        ret.setDateLastCheck(LocalDateTime.parse(dateNode.asText()));

        // alternative: deserialize from the list of filenames that
        JsonNode repositoriesNode = node.get("repositories");
        if(!repositoriesNode.isNull() && repositoriesNode.isArray()) {
            repositoriesNode.iterator().forEachRemaining(Errors.rethrow().wrap(r ->  {
                        String rSanitized = r.toString().replace("\"", "");
                        log.debug("trying to deser entry {}", rSanitized);
                        if(rSanitized.endsWith("Repository.json")) {
                            // now we should be able to find the ZipEntry with the name
                            ZipEntry zz = zipfile.getEntry(rSanitized);
                            if(zz == null) {
                                throw new IOException("ZipEntry " + rSanitized + " cannot be found");
                            }
                            try(InputStream is = zipfile.getInputStream(zz)) {
                                JsonParser p = codec.getFactory().createParser(is);
                                ret.add(p.readValueAs(DBDumpDto.class));
                            }
                        }}));
        }
        return ret;
    }
}

然后我可以像这样序列化我的对象:

    public File saveDBDumpPackage(DBDumpPackage dbDumpPackage, String filenamePrefix) throws IOException {
        File zipFile = fileHelper.createTempFile(filenamePrefix, ".zip");

        try(ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile))) {

            // 1. configure jackson's ObjectMapper with custom (de)serializers
            SimpleModule dbDumplListModule = new SimpleModule();
            dbDumplListModule.addSerializer(DBDumpList.class, new CustomDBDumpListSerializer(zos));

            ObjectMapper om = JsonMapper.builder()
                              .addModule(new JavaTimeModule())
                              .addModule(dbDumplListModule).build();
            // better format for the dates
            om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
            om.writeValue(zos, dbDumpPackage);
        }

        return zipFile;
    }

并像这样反序列化:

    public DBDumpPackage getDBDumpPackageFromFile(File file) throws IOException {
        if (file==null)
            return null;

        ZipFile zf = new ZipFile(file);

        SimpleModule dbDumplListModule = new SimpleModule();
        dbDumplListModule.addDeserializer(DBDumpList.class, new CustomDBDumpListDeserializer(zf));

        ObjectMapper om = JsonMapper.builder()
                          .addModule(new JavaTimeModule())
                          .addModule(dbDumplListModule).build();
        // better format for the dates
        om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

        // 4. check if we can deserialize: first position in the stream
        ZipEntry zz = zf.getEntry(CustomDBDumpListSerializer.MAIN_ENTRY);
        if(zz == null ) {
            throw new IOException("cound not find the main entity");
        }

        //System.out.println("found 'main' DBDumpList.json zipentry, we can proceed");
        try(InputStream zis = zf.getInputStream(zz)) {
            return om.readValue(zis, DBDumpPackage.class);
        }
    }
java json 序列化 Jackson 反序列化

评论

0赞 Thilo Schwarz 11/3/2023
也许我不明白你的问题。你有 3 个 json 文件。因此,您可以为每个文件调用该方法。#readValue
0赞 Łukasz Wiecheć 11/3/2023
@ThiloSchwarz感谢您的评论。我在想是否有办法根据要反序列化的类进行配置以执行此操作ObjectMapper

答: 暂无答案