强制 GSON 使用特定的构造函数

2022-09-01 22:57:56
public class UserAction {
    private final UUID uuid;
    private String userId;
    /* more fields, setters and getters here */

    public UserAction(){
        this.uuid = UUID.fromString(new com.eaio.uuid.UUID().toString());
    }

    public UserAction(UUID uuid){
        this.uuid = uuid;
    }
    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final UserAction other = (UserAction) obj;
        if (this.uuid != other.uuid && (this.uuid == null || !this.uuid.equals(other.uuid))) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 53 * hash + (this.uuid != null ? this.uuid.hashCode() : 0);
        return hash;
    }
}

我正在使用Gson来序列化和反序列化这个类。与今天一样,我必须在此对象中添加最终的UUID。我序列化没有问题。我需要强制gson在反序列化时使用构造函数。我怎样才能做到这一点?public UserAction(UUID uuid)


答案 1

您可以实现自定义 JsonDeserializer 并将其注册到 GSON。

class UserActionDeserializer implements JsonDeserializer<UserAction> {
    public UserAction deserialize(JsonElement json, Type typeOfT,
        JsonDeserializationContext context) throws JsonParseException {
        return new UserAction(UUID.fromString(json.getAsString());
}

GsonBuilder gson = new GsonBuilder();
gson.registerTypeAdapter(UserAction.class, new UserActionDeserializer());

请记住,此代码尚未经过测试。


答案 2

解决这个问题的另一种方法是利用这样一个事实,即在反序列化期间,Gson将使用JSON中找到的新值来破坏构造函数设置的任何值,因此只需使用实例创建器,该实例创建器专门存在“以创建不定义no-args构造函数的类的实例”。当要使用的构造函数仅将参数值分配给字段,而不执行任何有效性检查或以其他方式执行任何有意义的基于状态的处理时,此方法特别有效。

此外,此方法不需要进一步的自定义反序列化 -- 不需要 自定义实现。这对于引入自定义反序列化程序来解决一个小问题,然后需要“手动”处理其他JSON元素的情况有利,这可能不平凡。JsonDeserializer

话虽如此,这是一个使用首选构造函数的工作解决方案,但仅将其传递给空引用。JSON 中的实际值稍后设置。(Gson并不关心这个领域应该是最终的。UserActionuuid

import java.lang.reflect.Type;
import java.util.UUID;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.InstanceCreator;

public class Foo
{
  public static void main(String[] args)
  {
    UserAction action = new UserAction(UUID.randomUUID());
    action.setUserId("user1");

    String json = new Gson().toJson(action);
    System.out.println(json);

    GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.registerTypeAdapter(UserAction.class, new UserActionInstanceCreator());
    Gson gson = gsonBuilder.create();
    UserAction actionCopy = gson.fromJson(json, UserAction.class);
    System.out.println(gson.toJson(actionCopy));
  }
}

class UserActionInstanceCreator implements InstanceCreator<UserAction>
{
  @Override
  public UserAction createInstance(Type type)
  {
    return new UserAction(null);
  }
}

class UserAction
{
  private final UUID uuid;
  private String userId;

  public UserAction()
  {
    throw new RuntimeException("this constructor is not used");
  }

  public UserAction(UUID uuid)
  {
    this.uuid = uuid;
  }

  void setUserId(String userId)
  {
    this.userId = userId;
  }
}