Заставить GSON использовать определенный конструктор - PullRequest
20 голосов
/ 31 мая 2011
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) при десериализации. Как мне этого добиться?

Ответы [ 3 ]

22 голосов
/ 31 мая 2011

Вы можете реализовать пользовательский 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());

Имейте в виду, что этот код не был проверен.

13 голосов
/ 17 июня 2011

Другой подход к решению этой проблемы состоит в том, чтобы воспользоваться тем фактом, что во время десериализации Gson будет блокировать любые значения, заданные конструкторами, с новыми значениями, найденными в JSON, и поэтому просто использовать InstanceCreator , которыйсуществует специально для «создания экземпляров класса, который не определяет конструктор без аргументов».Этот подход особенно эффективен, когда используемый конструктор просто присваивает значения параметров полям и не выполняет никаких проверок достоверности или иным образом не выполняет какой-либо значимой обработки на основе состояния.

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

С учетом сказанного, вот такойрабочее решение, которое использует предпочтительный UserAction конструктор, но передает ему только нулевую ссылку.Фактическое значение из JSON устанавливается позже.(Гсону все равно, что поле 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;
  }
}
0 голосов
/ 31 мая 2011
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());
  }
}
...