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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6187724/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 14:45:50  来源:igfitidea点击:

Force GSON to use specific constructor

javaconstructorgsondeserialization

提问by frail

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;
    }
}

I am using Gson to serilize and deserialize this class. As today I had to add a final UUID in this object. I have no problem serializing. I need to force gson to use public UserAction(UUID uuid)constructor when deserializing. How can I achieve that ?

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

回答by Tomasz B?achowicz

You could implement a custom JsonDeserializerand register it with GSON.

您可以实现自定义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());

Bear in mind that this code has not been tested.

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

回答by Programmer Bruce

Another approach to solve this problem would be to take advantage of the fact that during deserialization Gson will clobber any values set by constructors with new values found in the JSON, and so just use an InstanceCreator, which exists specifically "to create instances of a class that does not define a no-args constructor." This approach works especially well when the constructor to be used just assigns parameter values to fields, and doesn't perform any validity checks or otherwise perform any meaningful state-based processing.

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

Also, this approach does not require further custom deserialization -- no custom implementation of JsonDeserializeris necessary. This can be advantageous to situations where introducing a custom deserializer to solve one small issue then necessitates "manual" processing of other JSON elements in close proximity, which could be non-trivial.

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

With that said, here's such a working solution that uses the preferred UserActionconstructor, but passes it only a null reference. The actual value from the JSON is later set. (Gson doesn't care that the uuidfield is supposed to be final.)

话虽如此,这里有一个使用首选UserAction构造函数的工作解决方案,但只传递一个空引用。来自 JSON 的实际值稍后设置。(Gson 不在乎该uuid领域是否应该是最终的。)

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;
  }
}

回答by dupdup

gson.registerTypeAdapter(DateTime.class, new DateTimeDeserializer());

private class DateTimeDeserializer implements JsonDeserializer<DateTime> {
  public DateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
      throws JsonParseException {
    return new DateTime(json.getAsJsonPrimitive().getAsString());
  }
}