Как правильно использовать Gson для десериализации объекта универсального типа с помощью статического служебного метода? - PullRequest
0 голосов
/ 28 февраля 2019

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

public final class Utils {

    public static final Gson sGson = new Gson();

    public static synchronized <T> T fromJson(String doc) {
        return sGson.fromJson(doc, new TypeToken<T>(){}.getType());
    }
}

Простой класс для тестирования:

public class TestDocument {
    public TestDocument(String test) {
        this.test = test;
    }

    public String test;
}

Это хорошо работает:

assertEquals(
   new TestDocument("test").test, 
   ((TestDocument) Utils.sGson.fromJson(
                      "{\"test\": \"test\"}", 
                      new TypeToken<TestDocument>(){}.getType())).test);

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

assertEquals(
   new TestDocument("test").test, 
   Utils.<TestDocument>fromJson("{\"test\": \"test\"}").test);

Выдает следующее исключение:

java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap не может быть приведен к TestDocument

Есть ли способ заставить его работать через универсальный метод?

Ответы [ 3 ]

0 голосов
/ 28 февраля 2019

Если бы это было возможно, Gson, вероятно, добавил бы уже этот метод, и он мог бы выглядеть так:

TestDocument document = gson.<TestDocument>fromJson(json);

Метод с такой подписью:

public <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException

включилJavaDoc:

Этот метод десериализует указанный Json в объект указанного класса.Он не подходит для использования, если указанный класс является универсальным типом, поскольку он не будет иметь информацию об универсальном типе из-за функции стирания типа в Java .Следовательно, этот метод не следует использовать, если требуемый тип является универсальным типом.Обратите внимание, что этот метод работает нормально, если любое из полей указанного объекта является универсальным, просто сам объект не должен быть универсальным типом.Для случаев, когда объект имеет универсальный тип, вызовите fromJson(String, Type).Если у вас есть Json в Reader вместо String, используйте fromJson(Reader, Class) вместо.

Даже второе имя параметра classOfT имеет значение the class of T.

0 голосов
/ 28 февраля 2019

Существует несколько советов по использованию типов:

  • Использование <T> без передачи фактического типа является обманом из-за стирания общих типов.
  • Передача типа как Class<T> не очень хорошая идея, потому что ###.class просто представляет класс, загруженный JVM (кроме примитивных типов).Имея это, Class<List<String>> и Class<List<Map<Integer, ?>>> абсолютно одинаковы List.class параметризация с потерей типа, поэтому заставляет Gson (de) сериализовать работу без учета надлежащих типов (например, LinkedHashTreeMap - хороший пример, если я помню).
  • Gson в основном работает с Type, который является интерфейсом супертипа для любого типа, который может быть представлен системой типов Java (включая классы, ParameterizedType и т. Д.).См. https://google.github.io/gson/apidocs/com/google/gson/Gson.html#fromJson-java.lang.String-java.lang.reflect.Type-
  • TypeToken - хороший пример держателя универсального типа в Java, включая получение правильной информации о типе в зависимости от того, как он был собран.Его можно использовать для обеспечения безопасности типа вашего метода: public static <T> T fromJson(String doc, TypeToken<? extends T> typeToken) { return sGson.fromJson(doc, typeToken.getType()); }.Токены типа могут быть кэшированы в общедоступные (да) статические конечные поля, содержащие реальную параметризацию, поскольку они неизменяемы и поточно-ориентированы во всех потоках.

Бонус:

  • Нет synchronizedтам необходимо: Gson экземпляры тоже поточно-ориентированы.
0 голосов
/ 28 февраля 2019

Похоже, что это невозможно сделать в Java без явной передачи фактического типа, чтобы он был известен во время сборки.

Вот одно из решений:

    public static synchronized <T> T fromJson(String doc, Class<T> type) {
        return sGson.fromJson(doc, type);
    }

Тогдатесты пройдены:

    assertEquals((new TestDocument("test").test), Utils.<TestDocument>fromJson("{\"test\": \"test\"}", TestDocument.class).test);

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...