Перейти к основному содержимому

Сериализация и десериализация карт с Джексоном

· 6 мин. чтения

Задача: Медиана двух отсортированных массивов

Даны два отсортированных массива размерами n и m. Найдите медиану слияния этих двух массивов.
Временная сложность решения должна быть O(log(m + n)) ...

ANDROMEDA

1. Обзор

В этом кратком руководстве мы рассмотрим сериализацию и десериализацию карт Java с помощью Jackson .

Мы покажем, как сериализовать и десериализовать Map<String, String> , Map<Object, String> и Map<Object, Object> в строки в формате JSON и обратно . ``

2. Конфигурация Maven

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.1</version>
</dependency>

Мы можем получить последнюю версию Джексона здесь .

3. Сериализация

Сериализация преобразует объект Java в поток байтов, который можно сохранять или совместно использовать по мере необходимости. Карты Java — это коллекции, которые сопоставляют ключевой объект объекту значения и часто являются наименее интуитивными объектами для сериализации.

3.1. Map<String, String> Сериализация

Для простого случая давайте создадим Map<String, String> и сериализуем его в JSON:

Map<String, String> map = new HashMap<>();
map.put("key", "value");

ObjectMapper mapper = new ObjectMapper();
String jsonResult = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(map);

ObjectMapper — это преобразователь сериализации Джексона. Это позволяет нам сериализовать нашу карту и записать ее в виде красиво напечатанной строки JSON , используя метод toString() в String :

{
"key" : "value"
}

3.2. Map<Object, String> Сериализация

С помощью нескольких дополнительных шагов мы также можем сериализовать карту, содержащую пользовательский класс Java. Давайте создадим класс MyPair для представления пары связанных объектов String .

Примечание: геттеры/сеттеры должны быть общедоступными, и мы аннотируем toString() с помощью @JsonValue , чтобы Джексон использовал этот пользовательский toString() при сериализации:

public class MyPair {

private String first;
private String second;

@Override
@JsonValue
public String toString() {
return first + " and " + second;
}

// standard getter, setters, equals, hashCode, constructors
}

Затем мы расскажем Джексону, как сериализовать MyPair , расширив JsonSerializer Джексона :

public class MyPairSerializer extends JsonSerializer<MyPair> {

private ObjectMapper mapper = new ObjectMapper();

@Override
public void serialize(MyPair value,
JsonGenerator gen,
SerializerProvider serializers)
throws IOException, JsonProcessingException {

StringWriter writer = new StringWriter();
mapper.writeValue(writer, value);
gen.writeFieldName(writer.toString());
}
}

JsonSerializer , как следует из названия, сериализует MyPair в JSON, используя метод toString() MyPair . Кроме того, Джексон предоставляет множество классов Serializer , соответствующих нашим требованиям к сериализации. ``

Затем мы применяем MyPairSerializer к нашей карте Map<MyPair, String> с аннотацией @JsonSerialize . Обратите внимание, что мы рассказали Джексону, как сериализовать MyPair , только потому, что он уже знает, как сериализовать String:

@JsonSerialize(keyUsing = MyPairSerializer.class) 
Map<MyPair, String> map;

Затем давайте проверим сериализацию нашей карты:

map = new HashMap<>();
MyPair key = new MyPair("Abbott", "Costello");
map.put(key, "Comedy");

String jsonResult = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(map);

Сериализованный вывод JSON:

{
"Abbott and Costello" : "Comedy"
}

3.3. Map<Object, Object> Сериализация

Самый сложный случай — это сериализация Map<Object, Object> , но большая часть работы уже сделана. Давайте используем MapSerializer Джексона для нашей карты и MyPairSerializer из предыдущего раздела для типов ключей и значений карты:

@JsonSerialize(keyUsing = MapSerializer.class)
Map<MyPair, MyPair> map;

@JsonSerialize(keyUsing = MyPairSerializer.class)
MyPair mapKey;

@JsonSerialize(keyUsing = MyPairSerializer.class)
MyPair mapValue;

Затем давайте проверим сериализацию нашей Map<MyPair, MyPair> :

mapKey = new MyPair("Abbott", "Costello");
mapValue = new MyPair("Comedy", "1940s");
map.put(mapKey, mapValue);

String jsonResult = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(map);

Сериализованный вывод JSON с использованием метода MyPair toString() :

{
"Abbott and Costello" : "Comedy and 1940s"
}

4. Десериализация

Десериализация преобразует поток байтов в объект Java, который мы можем использовать в коде. В этом разделе мы десериализуем ввод JSON в Map с разными подписями.

4.1. Map<String, String> Десериализация

Для простого случая возьмем входную строку в формате JSON и преобразуем ее в коллекцию Map<String, String> Java:

String jsonInput = "{\"key\": \"value\"}";
TypeReference<HashMap<String, String>> typeRef
= new TypeReference<HashMap<String, String>>() {};
Map<String, String> map = mapper.readValue(jsonInput, typeRef);

Мы используем ObjectMapper Джексона , как и для сериализации, используя readValue() для обработки ввода. Кроме того, обратите внимание на то, что мы используем TypeReference Джексона , который мы будем использовать во всех наших примерах десериализации для описания типа нашего назначения Map . Вот представление toString() нашей карты:

{key=value}

4.2. Map<Object, String> Десериализация

Теперь давайте изменим наш входной JSON и TypeReference нашего места назначения на Map<MyPair, String> :

String jsonInput = "{\"Abbott and Costello\" : \"Comedy\"}";

TypeReference<HashMap<MyPair, String>> typeRef
= new TypeReference<HashMap<MyPair, String>>() {};
Map<MyPair,String> map = mapper.readValue(jsonInput, typeRef);

Нам нужно создать конструктор для MyPair , который берет String с обоими элементами и анализирует их на элементы MyPair :

public MyPair(String both) {
String[] pairs = both.split("and");
this.first = pairs[0].trim();
this.second = pairs[1].trim();
}

toString() нашего объекта Map<MyPair,String> :

{Abbott and Costello=Comedy}

Есть еще один вариант, когда мы десериализуем в класс Java, содержащий Map; мы можем использовать класс KeyDeserializer Джексона , один из многих классов десериализации , которые предлагает Джексон. Давайте аннотируем наш ClassWithAMap с помощью @JsonCreator , @JsonProperty и @JsonDeserialize:

public class ClassWithAMap {

@JsonProperty("map")
@JsonDeserialize(keyUsing = MyPairDeserializer.class)
private Map<MyPair, String> map;

@JsonCreator
public ClassWithAMap(Map<MyPair, String> map) {
this.map = map;
}

// public getters/setters omitted
}

Здесь мы говорим Джексону десериализовать Map<MyPair, String>, содержащуюся в ClassWithAMap , поэтому нам нужно расширить KeyDeserializer , чтобы описать, как десериализовать ключ карты, объект MyPair , из входной String :

public class MyPairDeserializer extends KeyDeserializer {

@Override
public MyPair deserializeKey(
String key,
DeserializationContext ctxt) throws IOException,
JsonProcessingException {

return new MyPair(key);
}
}

Затем мы можем протестировать десериализацию с помощью readValue :

String jsonInput = "{\"Abbott and Costello\":\"Comedy\"}";

ClassWithAMap classWithMap = mapper.readValue(jsonInput,
ClassWithAMap.class);

Опять же, метод toString() нашей карты ClassWithAMap дает нам ожидаемый результат:

{Abbott and Costello=Comedy}

4.3. Карта<Объект,Объект> Десериализация

Наконец, давайте изменим наш входной JSON и TypeReference нашего пункта назначения на Map<MyPair, MyPair> :

String jsonInput = "{\"Abbott and Costello\" : \"Comedy and 1940s\"}";
TypeReference<HashMap<MyPair, MyPair>> typeRef
= new TypeReference<HashMap<MyPair, MyPair>>() {};
Map<MyPair,MyPair> map = mapper.readValue(jsonInput, typeRef);

toString() нашего объекта Map<MyPair, MyPair> :

{Abbott and Costello=Comedy and 1940s}

5. Вывод

В этой краткой статье мы узнали, как сериализовать и десериализовать карты Java в строки в формате JSON и из них.

Как всегда, приведенный в этой статье пример доступен в репозитории GitHub .