基于Gson的嵌套JSON自定义反序列化实现思路确认
我有一个嵌套JSON结构,想把它转换成Java类实例,觉得需要用自定义反序列化器。目前已经能把顶层的response_object JSON字符串转成JsonObject,还能遍历获取files数组的数据,代码如下:
JsonObject robj = value.getAsJsonObject("response_object"); JsonArray uarray = robj.getAsJsonArray("updates"); for(int i1 = 0; i1 < uarray.size(); i1++) { JsonElement jel = uarray.get(i1); JsonObject jobj = jel.getAsJsonObject(); String v = jobj.get("upgrades_to_version").getAsString(); JsonArray files = jobj.getAsJsonArray("files"); for (int ij = 0; ij < files.size(); ij++ ) { JsonElement fel = files.get(ij); JsonObject fobj = fel.getAsJsonObject(); url = fobj.get("url").getAsString(); } }
我的疑问是:能不能写一个从顶层开始的自定义反序列化器,接收response_object的JsonObject,直接返回对应的Java实例?
另外我查资料了解到,可以创建ResponseObject类,实现接收JsonObject的反序列化器,遍历JSON设置属性并创建嵌套类实例(需要定义嵌套类且各自实现反序列化器),这个思路对吗?
对应的JSON结构如下:
"response_object" : { "asset_type" : "software", "asset_virtual_id" : "Instance_Cluster-0001", "category" : "Software", "connect_transaction_id" : "11223344_1714400505026_Event-000001", "efd_transaction_id" : "H17246155", "event_time" : "2024-09-25 21:43:42", "event_time_ms" : 1727300622750, "event_type" : "software_update", "group" : "foo", "instance" : "0123456789abcdef", "response_code" : 200, "response_size" : 997, "software_update_request" : { "asset_event_detail" : { "body" : { "action" : "Order SW", "component" : "compid_xx", "description" : "Orderfix", "efd_product" : "Log Collector", "operation" : "order_software", "request_type" : "specific_fix", "update_ids" : [ "1.0.1.0-fix001" ] }, "header" : { "event_id" : "11223344_1714400505026_SWupdate-000002", "event_time" : "2024-04-29 09:00:00", "event_time_ms" : 1714400505026, "event_type" : "software_update" } } }, "times" : { "efd_delta" : "1.5 seconds", "efd_recv" : 1727300622749, "efd_send" : 1727300621292 }, "type" : "11223344", "updates" : [ { "applies_to_version" : "v1.0.1.0", "description" : "This is the initial release", "files" : [ { "description" : "Runnable Jar File", "descriptor" : "data/installable-unit.fix", "hash" : "AHcK2tBVoETIZcINUefJcLrZmNxFFMyFnjEj4dCcc+8=", "hashAlgorithm" : "SHA-256", "size" : 14787032, "url" : "https://example.com/runnable1.jar", "url_type" : "edge" }, { "description" : "Runnable", "descriptor" : "some_description", "hash" : "foo", "hashAlgorithm" : "SHA-256", "size" : 14787032, "url" : "https://example.com/runnable2.jar", "url_type" : "edge" } ], "id" : "1.0.1.0-fix001", "name" : "Initial Release 001", "release_date" : "2024-04-25T18:42:00.382Z", "status" : "available", "type" : "fix pack", "upgrades_to_version" : "v1.0.1.0" } ] }
1. 思路正确性确认
你的思路完全正确。创建顶层ResponseObject类,配合自定义反序列化器,同时为嵌套结构定义对应的Java类(嵌套类或独立类都可以),并按需实现各自的反序列化逻辑,是处理这种复杂嵌套JSON的常规方案。
2. 具体实现步骤
第一步:定义所有对应Java类
按照JSON结构逐层定义实体类,每个类的字段和JSON键一一对应,可通过@JsonProperty注解处理命名差异(比如JSON的下划线命名转Java驼峰命名)。示例部分关键类:
import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; public class ResponseObject { @JsonProperty("asset_type") private String assetType; @JsonProperty("asset_virtual_id") private String assetVirtualId; @JsonProperty("response_code") private Integer responseCode; @JsonProperty("software_update_request") private SoftwareUpdateRequest softwareUpdateRequest; @JsonProperty("times") private Times times; @JsonProperty("updates") private List<Update> updates; // 省略getter、setter、构造方法 } public class Update { @JsonProperty("applies_to_version") private String appliesToVersion; @JsonProperty("upgrades_to_version") private String upgradesToVersion; @JsonProperty("files") private List<File> files; // 省略其他字段、getter、setter } public class File { @JsonProperty("url") private String url; @JsonProperty("hash") private String hash; // 省略其他字段、getter、setter } // 其他嵌套类如SoftwareUpdateRequest、Times等同理定义
第二步:实现顶层自定义反序列化器
如果需要完全手动控制反序列化逻辑(比如特殊字段转换、自定义校验),可以实现Jackson的JsonDeserializer<ResponseObject>接口,在deserialize方法中遍历JsonObject并构建实例:
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class ResponseObjectDeserializer extends JsonDeserializer<ResponseObject> { @Override public ResponseObject deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { JsonNode rootNode = p.getCodec().readTree(p); ResponseObject responseObject = new ResponseObject(); // 赋值简单字段 responseObject.setAssetType(rootNode.get("asset_type").asText()); responseObject.setAssetVirtualId(rootNode.get("asset_virtual_id").asText()); responseObject.setResponseCode(rootNode.get("response_code").asInt()); // 处理嵌套对象times JsonNode timesNode = rootNode.get("times"); Times times = new Times(); times.setEfdDelta(timesNode.get("efd_delta").asText()); times.setEfdRecv(timesNode.get("efd_recv").asLong()); responseObject.setTimes(times); // 处理updates数组 ArrayNode updatesArray = (ArrayNode) rootNode.get("updates"); List<Update> updates = new ArrayList<>(); for (JsonNode updateNode : updatesArray) { Update update = new Update(); update.setAppliesToVersion(updateNode.get("applies_to_version").asText()); update.setUpgradesToVersion(updateNode.get("upgrades_to_version").asText()); // 处理files子数组 ArrayNode filesArray = (ArrayNode) updateNode.get("files"); List<File> files = new ArrayList<>(); for (JsonNode fileNode : filesArray) { File file = new File(); file.setUrl(fileNode.get("url").asText()); file.setHash(fileNode.get("hash").asText()); files.add(file); } update.setFiles(files); updates.add(update); } responseObject.setUpdates(updates); // 其他嵌套对象(如software_update_request)按相同逻辑处理 return responseObject; } }
第三步:注册反序列化器并使用
在ResponseObject类上添加注解指定反序列化器,之后用Jackson的ObjectMapper完成转换:
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.JsonNode; // 给ResponseObject类加注解 @JsonDeserialize(using = ResponseObjectDeserializer.class) public class ResponseObject { // ... 类内容不变 } // 使用示例 public class Main { public static void main(String[] args) throws IOException { ObjectMapper mapper = new ObjectMapper(); // 假设已拿到包含response_object的JSON字符串 String jsonStr = "..."; JsonNode rootNode = mapper.readTree(jsonStr); JsonNode responseObjectNode = rootNode.get("response_object"); // 转换为Java实例 ResponseObject responseObject = mapper.treeToValue(responseObjectNode, ResponseObject.class); } }
3. 可选优化:利用框架自动反序列化
如果没有特殊反序列化需求,Jackson等框架可以自动完成转换,无需手动遍历。只需确保实体类字段和JSON键通过@JsonProperty正确映射,直接调用ObjectMapper即可:
ObjectMapper mapper = new ObjectMapper(); // 忽略未知字段(避免JSON有额外字段时报错) mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // 直接转换response_object节点 ResponseObject responseObject = mapper.treeToValue(responseObjectNode, ResponseObject.class);
内容的提问来源于stack exchange,提问作者crokett

