You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java运行时动态选报表并完整写入JSON文件的实现方案

解决Gson序列化子类字段丢失的问题

问题根源

Gson默认基于编译时类型进行序列化操作。当你用父类Report的引用指向子类实例时,它只会序列化父类中定义的id、value字段,子类的专属字段(比如agreementId、tierId)会被忽略——因为编译时类型是Report,Gson不会主动识别运行时的实际子类类型。

可行解决方案

方案1:使用RuntimeTypeAdapterFactory(推荐支持反序列化的场景)

Gson的gson-extras包提供了RuntimeTypeAdapterFactory,可以自动识别运行时类型并序列化所有字段,还会在JSON中添加类型标识字段,方便后续反序列化。

步骤1:引入依赖

如果用Maven,在pom.xml中添加:

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson-extras</artifactId>
    <version>2.10.1</version> <!-- 使用最新稳定版本 -->
</dependency>

步骤2:代码实现

首先定义父类和子类:

abstract class Report {
    public String id;
    public String value;
    // 构造器、getter/setter按需添加
}

class ReportA extends Report {
    public String agreementId;
}

class ReportB extends Report {
    public String tierId;
}

class ReportC extends Report {
    public String instanceId;
}

然后配置Gson并动态生成报表:

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.typeadapters.RuntimeTypeAdapterFactory;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.charset.StandardCharsets;

public class ReportWriter {
    public static void main(String[] args) throws Exception {
        if (args.length == 0) {
            throw new IllegalArgumentException("请传入报表类型参数:ReportA/ReportB/ReportC");
        }
        String reportType = args[0];

        // 注册子类与类型标识的映射
        RuntimeTypeAdapterFactory<Report> typeFactory = RuntimeTypeAdapterFactory.of(Report.class, "reportType")
                .registerSubtype(ReportA.class, "ReportA")
                .registerSubtype(ReportB.class, "ReportB")
                .registerSubtype(ReportC.class, "ReportC");

        Gson gson = new GsonBuilder()
                .registerTypeAdapterFactory(typeFactory)
                .setPrettyPrinting()
                .create();

        // 根据参数动态创建对应报表实例
        Report report = switch (reportType) {
            case "ReportA" -> {
                ReportA a = new ReportA();
                a.id = "REP-A-001";
                a.value = "月度协议报表";
                a.agreementId = "AG-2024-001";
                yield a;
            }
            case "ReportB" -> {
                ReportB b = new ReportB();
                b.id = "REP-B-001";
                b.value = "层级统计报表";
                b.tierId = "TIER-LEVEL-3";
                yield b;
            }
            case "ReportC" -> {
                ReportC c = new ReportC();
                c.id = "REP-C-001";
                c.value = "实例运行报表";
                c.instanceId = "INST-PROD-007";
                yield c;
            }
            default -> throw new IllegalArgumentException("未知报表类型:" + reportType);
        };

        // 序列化并写入文件
        String json = gson.toJson(report);
        Files.write(Paths.get(reportType + ".json"), json.getBytes(StandardCharsets.UTF_8));
    }
}

序列化后的JSON会包含类型标识字段(比如"reportType": "ReportA"),反序列化时也能自动还原为对应子类实例。

方案2:序列化时指定运行时类型(仅序列化场景首选)

如果只需要序列化(不需要反序列化),可以直接在调用toJson时传入对象的实际类型,无需额外依赖。

修改序列化部分的代码:

// 使用默认Gson实例即可
Gson gson = new GsonBuilder().setPrettyPrinting().create();

// ... 前面创建report的逻辑不变

// 传入report.getClass()指定运行时类型
String json = gson.toJson(report, report.getClass());
Files.write(Paths.get(reportType + ".json"), json.getBytes(StandardCharsets.UTF_8));

这个方案代码最简洁,完全不需要额外配置,Gson会根据传入的实际类型序列化所有字段。

方案3:自定义TypeAdapter(精细控制场景)

如果需要自定义序列化逻辑(比如不想添加类型标识字段),可以编写自定义的TypeAdapter手动处理不同子类的序列化。

import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonWriter;
import com.google.gson.stream.JsonReader;
import java.io.IOException;
import com.google.gson.Gson;

class ReportTypeAdapter extends TypeAdapter<Report> {
    private final Gson gson = new Gson();

    @Override
    public void write(JsonWriter out, Report value) throws IOException {
        // 根据实际类型选择序列化方式
        if (value instanceof ReportA) {
            gson.toJson(value, ReportA.class, out);
        } else if (value instanceof ReportB) {
            gson.toJson(value, ReportB.class, out);
        } else if (value instanceof ReportC) {
            gson.toJson(value, ReportC.class, out);
        } else {
            throw new IOException("无法识别的报表类型");
        }
    }

    @Override
    public Report read(JsonReader in) throws IOException {
        // 若不需要反序列化,可抛出异常或返回null
        throw new UnsupportedOperationException("反序列化未实现");
    }
}

配置Gson时注册该Adapter:

Gson gson = new GsonBuilder()
        .registerTypeAdapter(Report.class, new ReportTypeAdapter())
        .setPrettyPrinting()
        .create();

这个方案适合需要完全控制序列化过程的场景,比如过滤某些字段或自定义JSON结构。

内容的提问来源于stack exchange,提问作者Willy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 23:10:43