web-dev-qa-db-fra.com

Sérialisation de la carte <Date, String> avec Jackson

Je veux sérialiser une carte avec Jackson . La date doit être sérialisée comme un horodatage, comme toutes mes autres dates.

Le code suivant rend les clés sous la forme "Mar 11 Mars 00:00:00 CET 1952" (qui est Date.toString ()) à la place de l'horodatage.

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

Je suppose que cela est dû au type effacement et que jackson ne sait pas au moment de l'exécution que la clé est une date. Mais je n'ai pas trouvé le moyen de passer un TypeReference à une méthode writeValue.

Existe-t-il un moyen simple d'obtenir le comportement souhaité ou toutes les clés sont-elles toujours rendues sous forme de chaîne par jackson?

Merci pour tout conseil.

28
Florian Gutmann

Le sérialiseur de clé de carte par défaut est StdKeySerializer, et il le fait simplement.

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

Vous pouvez utiliser la fonctionnalité SimpleModule et spécifier un sérialiseur de clé personnalisé à l'aide de la méthode addKeySerializer.


Et voici comment cela pourrait être fait.

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()));
  }
}

Mise à jour pour le dernier 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()));
  }
}
42
Programmer Bruce

Comme d'habitude, la réponse de Bruce est exacte.

Une idée supplémentaire est qu’étant donné qu’il existe un paramètre global pour la sérialisation des valeurs de date sous forme d’horodatages (SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS), cela devrait peut-être également s’appliquer ici. Et/ou au moins utilisez le format standard ISO-8601 pour le texte . Le principal problème pratique est la compatibilité en amont; Cependant, je doute que l'utilisation actuelle de plain toString () soit très utile car elle n'est ni efficace ni pratique (pour relire la valeur).

Donc, si vous voulez, vous voudrez peut-être déposer une demande de fonctionnalité; cela ressemble à une gestion sous-optimale des touches de la carte par Jackson.

4
StaxMan

Depuis Jackson 2.0 (peut-être aussi 1.9), WRITE_DATE_KEYS_AS_TIMESTAMPS peut être utilisé pour modifier ce comportement.

Exemple d'utilisation de ObjectMapper:

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

et pour ObjectWriter:

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