Gson десериализация мультикарты с абстрактными классами - PullRequest
0 голосов
/ 14 ноября 2018

Не могу понять, как десериализовать это:

{
  "c8147c8a-09c3-4165-b5c2-ce72e2c97100": {
    "pets": {
      "BOOST": [
        {
          "mcmmoBoost": 15.0,
          "owner": "c8147c8a-09c3-4165-b5c2-ce72e2c97100",
          "entityType": "IRON_GOLEM",
          "health": 150.0,
          "tier": 1,
          "alive": true
        }
      ]
    },
    "uuid": "c8147c8a-09c3-4165-b5c2-ce72e2c97100"
  }
}

в Map<UUID, PetPlayer> с PetPlayer, содержащим мультикарту под названием «домашние животные», структурированную следующим образом;Multimap<PetType, Pet>.PetType - здесь перечисление, а Pet - абстрактный класс, имеющий несколько реализаций.

Я пытался использовать эти два сериализатора и десериализатора.

Первый: открытый финальный класс HashMultimapAdapter реализует JsonSerializer>, JsonDeserializer> {private static final PetAdapter petAdapter = new PetAdapter ();

    @Override
    public JsonElement serialize(Multimap<PetType, Pet> src, Type typeOfSrc, JsonSerializationContext context) {
        return context.serialize(src.asMap());
    }

    @Override
    public Multimap<PetType, Pet> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        Map<PetType, Collection<JsonElement>> asMap = context.deserialize(json, new TypeToken<Map<PetType, Collection<JsonElement>>>(){{}}.getType());
        Multimap<PetType, Pet> multimap = ArrayListMultimap.create();

        for (Map.Entry<PetType, Collection<JsonElement>> entry : asMap.entrySet()) {
            entry.getValue().forEach(jsonElement -> {
                multimap.put(entry.getKey(), petAdapter.deserialize(jsonElement, Pet.class, context));
            });
        }

        return multimap;
    }
}

Секунда:

public class PetAdapter implements JsonSerializer<Pet>, JsonDeserializer<Pet> {
    @Override
    public Pet deserialize(JsonElement jsonElement, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        EntityType entityType = EntityType.valueOf(jsonElement.getAsJsonObject().get("entityType").getAsString());

        switch (entityType) {
            case IRON_GOLEM:
                return context.deserialize(jsonElement, EcoPet.class);
            case WOLF:
                return context.deserialize(jsonElement, BoostPet.class);
            case MAGMA_CUBE:
                return context.deserialize(jsonElement, CombatPet.class);
            default:
                throw new JsonParseException("Invalid PetType");

        }
    }

    @Override
    public JsonElement serialize(Pet src, Type typeOfSrc, JsonSerializationContext context) {
        return context.serialize(src);
    }
}

Это привело к переполнению стека.

java.lang.StackOverflowError at com.google.gson.internal.$Gson$Types.resolve($Gson$Types.java:375) ~[PaperSpigot-1.8.8-R0.1.jar:git-PaperSpigot-"4c7641d"]

Я очень ценю любую помощь:)

1 Ответ

0 голосов
/ 21 ноября 2018

Я действительно не знаю, как это сделать с помощью gson без ручного или хакерского поведения. Это слишком большой текст, чтобы его можно было использовать в качестве комментария, поэтому я оставлю его здесь в качестве ответа, чтобы помочь вам.

Во-первых, вы получаете переполнение стека, потому что вы вызываете context.deserialize с теми же параметрами, что вызывает gson для вызова того же десериализатора, который будет вызывать снова context.deserialize и т. Д., Пока переполнение стека.

Вы столкнетесь с той же проблемой при сериализации, потому что вы также просто делаете context.serialize.

Чтобы избежать этого, вам нужно избегать повторения gson вызова методов сериализатора / десериализатора. Этого очень легко добиться, создав еще один экземпляр gson без адаптеров:

public class PetAdapter 
           implements JsonSerializer<Pet>, JsonDeserializer<Pet> {
  private final Gson gson = new Gson();

  @Override
  public Pet deserialize(JsonElement jsonElement, Type typeOfT, 
      JsonDeserializationContext context) throws JsonParseException {
     EntityType entityType = EntityType.valueOf(jsonElement.getAsJsonObject().get("entityType").getAsString());

      switch (entityType) {
        case IRON_GOLEM:
            return gson.fromJson(jsonElement, EcoPet.class);
        case WOLF:
            return gson.fromJson(jsonElement, BoostPet.class);
        case MAGMA_CUBE:
            return gson.fromJson(jsonElement, CombatPet.class);
        default:
            throw new JsonParseException("Invalid PetType");
      }
  }

  @Override
  public JsonElement serialize(Pet src, Type typeOfSrc, JsonSerializationContext context) {
    return gson.toJson(src);
  }
}

Это работает, но только если ваши реализации Pet не зависят от других пользовательских сериализаторов / десериализаторов. Итак, как вы можете себе представить, это довольно нахально.

Другой подход - десериализация вручную. Это означает, что вам нужно пройти через элемент json и прочитать свойства, такие как вы читаете entityType, и вручную построить ваши объекты.

Очень похоже, я думаю (я не проверял это), вы могли бы сначала использовать context, чтобы десериализовать каждого питомца в Map объектов и позволить каждому питомцу реализовать статический метод, который создает экземпляр конкретного животное с этой карты. Что-то вроде:

public class IronGolem extends Pet {
   public static IronGolem from(Map<String, Object> deserializedPet) {
      // here check the map for each thing you need
      return new IronGolem(/*pass in every attribute*/);
   }
}

Надеюсь, это поможет.

...