Заставить GSON использовать определенный конструктор

#java #constructor #gson #deserialization

#java #конструктор #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 amp;amp; (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, и поэтому просто используйте InstanceCreator, который существует специально «для создания экземпляров класса, который не определяет конструктор без аргументов». Этот подход работает особенно хорошо, когда используемый конструктор просто присваивает значения параметров полям и не выполняет никаких проверок достоверности или иным образом выполняет какую-либо значимую обработку на основе состояния.

Кроме того, этот подход не требует дальнейшей пользовательской десериализации — пользовательская реализация 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;
  }
}
  

Комментарии:

1. Это решило проблему для меня действительно элегантно и без добавления тонны шаблонов. Хороший

Ответ №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());
  }
}
  

Комментарии:

1. Скопируйте и вставьте . Немного хромает, о чем вы меня спрашиваете;)