使用GSON解析JSON至JPA持久化模型时遇异常,求解决方案
GSON解析JSON配合JPA持久化的反序列化异常修复
问题场景
需要解析外部API返回的JSON数据,通过JPA注解持久化到数据库,使用GSON作为解析器并自定义TypeAdapter,当前代码运行抛出UnsupportedOperationException: JsonObject异常,相关代码如下:
测试JSON(test.json)
{"workID": 12355,"contributors":[{"fullName": "UNKNOWN 1","workContributorID": 1100933315,"workID": 12355},{"fullName": "UNKNOWN 2","workContributorID": 1100933314,"workID": 12355}]}
原JPA实体类
Contributor.java
import jakarta.persistence.*; import lombok.Getter; import lombok.Setter; @Getter @Setter @Entity @Table(name = "contributor") public class Contributor { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private long workContributorID; private String fullName; @ManyToOne @JoinColumn(name = "work_id", referencedColumnName = "id") private WorkSP workID; }
WorkSP.java
import jakarta.persistence.*; import lombok.Getter; import lombok.Setter; import java.util.ArrayList; @Getter @Setter @Entity @Table(name = "work_sp") public class WorkSP { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private long workID; private ArrayList<Contributor> contributors; public WorkSP(long asLong) { this.workID = asLong; } }
原测试类(App.java)
import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonDeserializer; import com.google.gson.stream.JsonReader; import java.io.FileNotFoundException; import java.io.FileReader; public class App { public static void main( String[] args ) throws FileNotFoundException { JsonReader readerTest = new JsonReader(new FileReader("test.json")); Gson gSonInstance = new GsonBuilder(). registerTypeAdapter(WorkSP.class, (JsonDeserializer<WorkSP>) (json, type, jsonDeserializationContext) -> { return new WorkSP(json.getAsJsonObject().getAsLong()); }). create(); WorkSP work = gSonInstance.fromJson(readerTest, WorkSP.class); } }
抛出的异常
Exception in thread "main" java.lang.UnsupportedOperationException: JsonObject at com.google.gson.JsonElement.getAsLong(JsonElement.java:294) at org.example.App.lambda$main$0(App.java:21) at com.google.gson.internal.bind.TreeTypeAdapter.read(TreeTypeAdapter.java:95) at com.google.gson.Gson.fromJson(Gson.java:1361) at com.google.gson.Gson.fromJson(Gson.java:1306) at org.example.App.main(App.java:24)
错误原因
- WorkSP反序列化逻辑错误:自定义反序列化器中调用
json.getAsJsonObject().getAsLong(),但getAsJsonObject()返回的是包含workID和contributors的完整JSON对象,直接对JsonObject调用getAsLong()会触发类型不匹配异常。 - 实体类缺少无参构造:GSON和JPA都要求实体类必须有公共无参构造函数,原WorkSP类只有带参构造,会导致GSON无法实例化对象。
- JPA关联关系注解不完整:WorkSP与Contributor是一对多关系,原代码未添加
@OneToMany注解及关联配置,持久化时会出现关联异常。 - Contributor的workID字段解析问题:JSON中Contributor的
workID是long值,但实体类中是WorkSP类型,GSON无法自动完成类型转换,需要自定义反序列化逻辑。
修复方案
1. 修正JPA实体类
调整WorkSP.java
import jakarta.persistence.*; import lombok.Getter; import lombok.Setter; import java.util.List; @Getter @Setter @Entity @Table(name = "work_sp") public class WorkSP { @Id // 若JSON中的workID是业务主键,不需要数据库自增则删除@GeneratedValue // @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private long workID; // 配置一对多关联,cascade确保级联操作,mappedBy对应Contributor中的关联字段 @OneToMany(mappedBy = "workID", cascade = CascadeType.ALL, orphanRemoval = true) private List<Contributor> contributors; // 添加GSON和JPA必需的无参构造 public WorkSP() {} public WorkSP(long workID) { this.workID = workID; } }
调整Contributor.java
import jakarta.persistence.*; import lombok.Getter; import lombok.Setter; import com.google.gson.annotations.JsonDeserialize; @Getter @Setter @Entity @Table(name = "contributor") public class Contributor { @Id // 若workContributorID是业务主键,删除@GeneratedValue // @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private long workContributorID; private String fullName; @ManyToOne @JoinColumn(name = "work_id", referencedColumnName = "id") // 指定自定义反序列化器,将long值转为WorkSP对象 @JsonDeserialize(using = WorkSPDeserializer.class) private WorkSP workID; }
2. 编写Contributor的workID反序列化器
创建WorkSPDeserializer.java,处理JSON中long类型的workID到WorkSP对象的转换:
import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonParseException; import java.lang.reflect.Type; public class WorkSPDeserializer implements JsonDeserializer<WorkSP> { @Override public WorkSP deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { // 从JSON中取出long类型的workID,创建对应的WorkSP对象 long workId = json.getAsLong(); return new WorkSP(workId); } }
3. 修正测试类的GSON反序列化逻辑
修改App.java,正确解析WorkSP的完整JSON结构:
import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonDeserializer; import com.google.gson.stream.JsonReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.util.List; public class App { public static void main(String[] args) throws FileNotFoundException { JsonReader readerTest = new JsonReader(new FileReader("test.json")); Gson gSonInstance = new GsonBuilder() .registerTypeAdapter(WorkSP.class, (JsonDeserializer<WorkSP>) (json, type, context) -> { WorkSP workSP = new WorkSP(); // 解析workID字段 workSP.setWorkID(json.getAsJsonObject().get("workID").getAsLong()); // 调用context解析contributors列表,自动使用Contributor的反序列化器处理每个元素 workSP.setContributors(context.deserialize(json.getAsJsonObject().get("contributors"), List.class)); return workSP; }) .create(); WorkSP work = gSonInstance.fromJson(readerTest, WorkSP.class); // 测试输出验证 System.out.println("WorkID: " + work.getWorkID()); for (Contributor c : work.getContributors()) { System.out.println(String.format("贡献者:%s,关联WorkID:%d", c.getFullName(), c.getWorkID().getWorkID())); } } }
关键说明
- 若JSON中的
workID和workContributorID是业务主键,需删除实体类中的@GeneratedValue(strategy = GenerationType.IDENTITY),避免数据库自增覆盖传入的业务ID。 - 持久化时,可通过JPA的EntityManager将解析后的WorkSP对象存入数据库,
cascade = CascadeType.ALL会自动级联保存关联的Contributor数据。
内容的提问来源于stack exchange,提问作者J Robes
相关产品推荐
相关产品推荐

