Принудительно использовать GSON для использования конкретного конструктора

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 будет clobber любые значения, установленные конструкторами с новыми значениями, найденными в JSON, и поэтому просто используйте InstanceCreator, который существует специально для создания экземпляров класса, который не определяет конструктор no-args. " Этот подход особенно хорошо работает, когда используемый конструктор просто присваивает значениям параметров полям и не выполняет никаких проверок валидности или иным образом не выполняет какую-либо значимую обработку на основе состояния.

Кроме того, этот подход не требует дополнительной пользовательской десериализации - никакой специальной реализации JsonDeserializer не требуется. Это может быть выгодным для ситуаций, когда введение пользовательского десериализатора для решения одной небольшой проблемы затем требует "ручной" обработки других элементов JSON в непосредственной близости, что может быть нетривиальным.

С учетом сказанного, здесь такое рабочее решение, которое использует предпочтительный конструктор 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;
  }
}

Ответ 3

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