使用Gson fromJson()解析含动态键的JSON时,动态@SerializedName值的处理方案咨询
Gson fromJson()解析含动态键的JSON时,动态@SerializedName值的处理方案咨询
看起来你遇到的是Gson解析动态键JSON的典型问题——因为transactions里的键是从API返回的found_detail字段动态生成的,没法提前写死@SerializedName的值,对吧?我来给你拆解几个可行的方案,比反射修改注解要靠谱得多:
方案一:修正Map类型的使用方式(最简便)
你之前尝试用Map但没成功,大概率是字段定义或者Gson配置的问题。正确的做法是直接用Map<String, EFDTransaction>接收动态键的内容,同时对应好JSON的层级结构:
修正后的类结构
// 对应JSON里的transactions对象 public class EFDTransactions { @Expose private Map<String, EFDTransaction> transactionMap; // 用Map接收动态键的内容 public EFDTransactions() {} public Map<String, EFDTransaction> getTransactionMap() { return transactionMap; } public void setTransactionMap(Map<String, EFDTransaction> transactionMap) { this.transactionMap = transactionMap; } }
解析代码
要确保Gson配置保留@Expose注解的字段,然后直接解析transactions节点:
// 从已有的responsejobj中提取transactions节点 JsonObject transactionsObj = responsejobj.getAsJsonObject("transactions"); Gson rspgson = new GsonBuilder() .excludeFieldsWithoutExposeAnnotation() .create(); // 解析成EFDTransactions对象 EFDTransactions trans = rspgson.fromJson(transactionsObj, EFDTransactions.class); // 之后可以通过Map获取动态键对应的内容,比如结合found_detail提取的键 String dynamicKey = "Request ID 000002"; // 实际从found_detail中提取 EFDTransaction targetTrans = trans.getTransactionMap().get(dynamicKey);
方案二:自定义JsonDeserializer(更灵活)
如果需要自动从found_detail提取动态键并直接返回目标对象,可以写一个自定义反序列化器,完全掌控解析逻辑:
自定义反序列化器实现
public class EFDTransactionsDeserializer implements JsonDeserializer<EFDTransactions> { // 可以传入提前从found_detail提取的动态键,也可以在内部解析 private final String dynamicKey; public EFDTransactionsDeserializer(String dynamicKey) { this.dynamicKey = dynamicKey; } @Override public EFDTransactions deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { JsonObject transactionsObj = json.getAsJsonObject(); EFDTransactions transactions = new EFDTransactions(); // 直接根据动态键提取并解析目标对象 JsonElement transElement = transactionsObj.get(dynamicKey); EFDTransaction transaction = context.deserialize(transElement, EFDTransaction.class); // 封装到EFDTransactions中(如果需要单个对象而非Map) Map<String, EFDTransaction> transMap = new HashMap<>(); transMap.put(dynamicKey, transaction); transactions.setTransactionMap(transMap); return transactions; } }
注册并使用反序列化器
// 先从found_detail提取动态键 String foundDetail = responsejobj.get("found_detail").getAsString(); Pattern pattern = Pattern.compile("\\[(.*?)\\]"); Matcher matcher = pattern.matcher(foundDetail); String dynamicKey = matcher.find() ? matcher.group(1) : ""; // 注册自定义反序列化器并解析 Gson rspgson = new GsonBuilder() .excludeFieldsWithoutExposeAnnotation() .registerTypeAdapter(EFDTransactions.class, new EFDTransactionsDeserializer(dynamicKey)) .create(); EFDTransactions trans = rspgson.fromJson(responsejobj.getAsJsonObject("transactions"), EFDTransactions.class);
方案三:手动提取动态键后解析(场景简单时最直接)
如果你的场景里transactions下永远只有一个动态键,完全可以跳过复杂的类结构,直接提取键后解析目标对象:
// 1. 从found_detail提取动态键 String foundDetail = responsejobj.get("found_detail").getAsString(); String dynamicKey = Pattern.compile("\\[(.*?)\\]").matcher(foundDetail).group(1); // 2. 直接解析目标EFDTransaction JsonObject transactionsObj = responsejobj.getAsJsonObject("transactions"); EFDTransaction transaction = rspgson.fromJson(transactionsObj.get(dynamicKey), EFDTransaction.class); // 3. 如需封装到EFDTransactions类,手动创建对象即可 EFDTransactions trans = new EFDTransactions(); Map<String, EFDTransaction> transMap = new HashMap<>(); transMap.put(dynamicKey, transaction); trans.setTransactionMap(transMap);
为什么不推荐反射修改@SerializedName?
反射修改注解不仅实现复杂,而且Gson初始化时会缓存注解信息,运行时修改大概率不生效,还会引入性能开销和兼容性风险,完全没必要用这种绕远路的方式。
总结下来,方案一最适合大多数场景,改改类定义就能用;如果需要定制解析逻辑选方案二;场景简单的话方案三最直接。
内容来源于stack exchange
相关产品推荐
相关产品推荐

