Карта сериализациис Джексоном - PullRequest
30 голосов
/ 04 июля 2011

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

Следующий код отображает ключи в форме «Вт 11 марта 00:00:00 CET 1952» (то есть Date.toString ()) вместо временной метки.

Map<Date, String> myMap = new HashMap<Date, String>();
...
ObjectMapper.writeValue(myMap)

Я предполагаю, что это из-за стирания типа, и Джексон не знает во время выполнения, что ключ является датой.Но я не нашел способа передать TypeReference любому методу writeValue.

Есть ли простой способ добиться желаемого поведения или все ключи всегда отображаются в виде строк Джексоном?

Спасибо за любую подсказку.

Ответы [ 3 ]

43 голосов
/ 04 июля 2011

Сериализатором ключей карты по умолчанию является StdKeySerializer, и он просто делает это.

String keyStr = (value.getClass() == String.class) ? ((String) value) : value.toString();
jgen.writeFieldName(keyStr);

Вы можете использовать функцию SimpleModule и указать пользовательский сериализатор ключей, используя метод addKeySerializer.


А вот как это можно сделать.

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.Version;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.ObjectWriter;
import org.codehaus.jackson.map.SerializerProvider;
import org.codehaus.jackson.map.module.SimpleModule;
import org.codehaus.jackson.map.type.MapType;
import org.codehaus.jackson.map.type.TypeFactory;

public class CustomKeySerializerDemo
{
  public static void main(String[] args) throws Exception
  {
    Map<Date, String> myMap = new HashMap<Date, String>();
    myMap.put(new Date(), "now");
    Thread.sleep(100);
    myMap.put(new Date(), "later");

    ObjectMapper mapper = new ObjectMapper();
    System.out.println(mapper.writeValueAsString(myMap));
    // {"Mon Jul 04 11:38:36 MST 2011":"now","Mon Jul 04 11:38:36 MST 2011":"later"}

    SimpleModule module =  
      new SimpleModule("MyMapKeySerializerModule",  
          new Version(1, 0, 0, null));
    module.addKeySerializer(Date.class, new DateAsTimestampSerializer());

    MapType myMapType = TypeFactory.defaultInstance().constructMapType(HashMap.class, Date.class, String.class);

    ObjectWriter writer = new ObjectMapper().withModule(module).typedWriter(myMapType);
    System.out.println(writer.writeValueAsString(myMap));
    // {"1309806289240":"later","1309806289140":"now"}
  }
}

class DateAsTimestampSerializer extends JsonSerializer<Date>
{
  @Override
  public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider) 
      throws IOException, JsonProcessingException
  {
    jgen.writeFieldName(String.valueOf(value.getTime()));
  }
}

Обновление для последней версии Jackson (2.0.4):

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.type.MapType;
import com.fasterxml.jackson.databind.type.TypeFactory;

public class CustomKeySerializerDemo
{
  public static void main(String[] args) throws Exception
  {
    Map<Date, String> myMap = new HashMap<Date, String>();
    myMap.put(new Date(), "now");
    Thread.sleep(100);
    myMap.put(new Date(), "later");

    ObjectMapper mapper = new ObjectMapper();
    System.out.println(mapper.writeValueAsString(myMap));
    // {"2012-07-13T21:14:09.499+0000":"now","2012-07-13T21:14:09.599+0000":"later"}

    SimpleModule module = new SimpleModule();
    module.addKeySerializer(Date.class, new DateAsTimestampSerializer());

    MapType myMapType = TypeFactory.defaultInstance().constructMapType(HashMap.class, Date.class, String.class);

    ObjectWriter writer = new ObjectMapper().registerModule(module).writerWithType(myMapType);
    System.out.println(writer.writeValueAsString(myMap));
    // {"1342214049499":"now","1342214049599":"later"}
  }
}

class DateAsTimestampSerializer extends JsonSerializer<Date>
{
  @Override
  public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider) 
      throws IOException, JsonProcessingException
  {
    jgen.writeFieldName(String.valueOf(value.getTime()));
  }
}
5 голосов
/ 05 июля 2011

Как обычно, ответ Брюса прямо на месте.

Еще одна мысль состоит в том, что, поскольку существует глобальная настройка для сериализации Date значений как отметок времени :

SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS

Может быть, это должно быть применимо и здесь. И / или, по крайней мере, используйте для текста стандартный ISO-8601 формат. Основная практическая проблема заключается в обратной совместимости ; однако я сомневаюсь, что текущее использование простого toString() очень полезно, поскольку оно не является ни эффективным, ни удобным (для чтения значения).

Так что, если вы хотите, вы можете подать запрос на функцию; это звучит как неоптимальная обработка ключей карты Джексоном.

1 голос
/ 26 ноября 2015

Начиная с версии Jackson 2.0 (возможно, и 1.9), WRITE_DATE_KEYS_AS_TIMESTAMPS может использоваться для изменения этого конкретного поведения.

Пример использования ObjectMapper:

ObjectMapper m = new ObjectMapper().configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, true);

и для ObjectWriter:

ObjectWriter w = mapper.with(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS);
...