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

使用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)

错误原因

  1. WorkSP反序列化逻辑错误:自定义反序列化器中调用json.getAsJsonObject().getAsLong(),但getAsJsonObject()返回的是包含workID和contributors的完整JSON对象,直接对JsonObject调用getAsLong()会触发类型不匹配异常。
  2. 实体类缺少无参构造:GSON和JPA都要求实体类必须有公共无参构造函数,原WorkSP类只有带参构造,会导致GSON无法实例化对象。
  3. JPA关联关系注解不完整:WorkSP与Contributor是一对多关系,原代码未添加@OneToMany注解及关联配置,持久化时会出现关联异常。
  4. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 13:09:51