Подтвердить что ты не робот

Сериализация карты <Date, String> с помощью Jackson

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

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

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

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

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

Спасибо за любой намек.

4b9b3361

Ответ 1

Сериализатором ключей карты по умолчанию является 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()));
  }
}

Ответ 2

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

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

SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS

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

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

Ответ 3

Так как 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);