Force GSON to use specific constructor

2020-02-01 03:36发布

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 ?

3条回答
在下西门庆
2楼-- · 2020-02-01 04:09

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.

Also, this approach does not require further custom deserialization -- no custom implementation of JsonDeserializer is 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.

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

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;
  }
}
查看更多
够拽才男人
3楼-- · 2020-02-01 04:09
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());
  }
}
查看更多
何必那么认真
4楼-- · 2020-02-01 04:10

You could implement a custom JsonDeserializer and register it with 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.

查看更多
登录 后发表回答