|
1
2
3
4
5
6
7
8
9
|
public class Circle { private int radius = 10; private String backgroundColor = "#FF0000"; private String borderColor = "#000000"; private double scaleFactor = 0.5; ... // getter / setter} |
Сериализация (объект Java -> JSON) может быть выполнена следующим образом:
|
01
02
03
04
05
06
07
08
09
10
11
|
Circle circle = new Circle();Gson gson = new Gson();String json = gson.toJson(circle); ==> json is{ "radius": 10, "backgroundColor": "#FF0000", "borderColor": "#000000", "scaleFactor": 0.5, ...} |
Десериализация (JSON -> объект Java) — это всего лишь одна строка кода:
|
1
2
|
Circle circle2 = gson.fromJson(json, Circle.class); ==> circle2 is the same as the circle above |
Все работает как шарм. Есть только одна проблема, с которой я столкнулся с абстрактными классами. Предположим, у нас есть абстрактный класс AbstractElement и многие другие классы, расширяющие этот
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
public abstract class AbstractElement { private String uuid; // getter / setter}public class Circle extends AbstractElement { ...}public class Rectangle extends AbstractElement { ...}public class Ellipse extends AbstractElement { ...} |
Предположим теперь, что мы храним все конкретные классы в списке или карте, параметризованной с помощью AbstractElement
|
1
2
3
4
5
6
|
public class Whiteboard{ private Map<String, AbstractElement> elements = new LinkedHashMap<String, AbstractElement>(); ...} |
Проблема в том, что конкретный класс не раскрывается при десериализации. Это неизвестно в JSON-представлении Whiteboard. Как создать подходящий Java-класс из JSON-представления и поместить в элементы Map <String, AbstractElement>? Я ничего не нашел в документации, что бы решить эту проблему. Очевидно, что нам нужно хранить метаинформацию в представлениях JSON о конкретных классах. Это точно. Gson позволяет регистрировать ваши собственные сериализаторы и десериализаторы. Это мощная особенность Gson. Иногда представление по умолчанию не то, что вы хотите. Это часто имеет место, например, при работе со сторонними библиотечными классами. Существует достаточно примеров написания пользовательских сериализаторов / десериализаторов. Я собираюсь создать класс адаптера, реализующий оба интерфейса JsonSerializer, JsonDeserializer и зарегистрировать его для моего абстрактного класса AbstractElement.
|
1
2
3
|
GsonBuilder gsonBilder = new GsonBuilder();gsonBilder.registerTypeAdapter(AbstractElement.class, new AbstractElementAdapter());Gson gson = gsonBilder.create(); |
А вот и AbstractElementAdapter:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
package com.googlecode.whiteboard.json;import com.google.gson.*;import com.googlecode.whiteboard.model.base.AbstractElement;import java.lang.reflect.Type;public class AbstractElementAdapter implements JsonSerializer<AbstractElement>, JsonDeserializer<AbstractElement> { @Override public JsonElement serialize(AbstractElement src, Type typeOfSrc, JsonSerializationContext context) { JsonObject result = new JsonObject(); result.add("type", new JsonPrimitive(src.getClass().getSimpleName())); result.add("properties", context.serialize(src, src.getClass())); return result; } @Override public AbstractElement deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { JsonObject jsonObject = json.getAsJsonObject(); String type = jsonObject.get("type").getAsString(); JsonElement element = jsonObject.get("properties"); try { return context.deserialize(element, Class.forName("com.googlecode.whiteboard.model." + type)); } catch (ClassNotFoundException cnfe) { throw new JsonParseException("Unknown element type: " + type, cnfe); } }} |
Я добавляю два свойства JSON: одно — «тип», а другое — «свойства». Первое свойство содержит конкретный класс реализации (простое имя) AbstractElement, а второе — сам сериализованный объект. JSON выглядит как
|
01
02
03
04
05
06
07
08
09
10
|
{ "type": "Circle", "properties": { "radius": 10, "backgroundColor": "#FF0000", "borderColor": "#000000", "scaleFactor": 0.5, ... }} |
Мы извлекаем выгоду из свойства «type» во время десериализации. Конкретный класс теперь можно создать с помощью Class.forName («com.googlecode.whiteboard.model. + Type»), где «com.googlecode.whiteboard.model». + type — это полное имя класса. Следующий звонок
|
1
|
public <T> T deserialize(JsonElement json, Type typeOfT) throws JsonParseException |
from JsonDeserializationContext вызывает десериализацию по умолчанию для указанного объекта и завершает задание.
Ссылка: JSON с GSON и абстрактные классы от нашего партнера по JCG Олега Вараксина в блоге Мысли о разработке программного обеспечения .