2016-09-15 1 views
1

Je suis en train de sérialiser/désérialiser un Map<?, ?> avec un objet arbitraire en tant que clés avec Jackson version 2.8. La contrepartie JSON doit être un tableau de couples, soit donnéComment désérialiser une carte <?, ?> avec Jackson?

public class Foo { 
    public String foo; 
    public Foo(String foo) { 
     this.foo = foo; 
    } 
} 

public class Bar { 
    public String bar; 
    public Bar(String bar) { 
     this.bar = bar; 
    } 
} 

puis

Map<Foo, Bar> map; 
map.put(new Foo("foo1"), new Bar("bar1")); 
map.put(new Foo("foo2"), new Bar("bar2")); 

devrait être représenté par cette JSON

[ 
    [ { "foo": "foo1" }, { "bar": "bar1" } ], 
    [ { "foo": "foo2" }, { "bar": "bar2" } ] 
] 

J'ai donc fait la partie sérialiseur comme

public class MapToArraySerializer extends JsonSerializer<Map<?, ?>> { 

    @Override 
    public void serialize(Map<?, ?> value, JsonGenerator gen, SerializerProvider serializers) 
     throws IOException, JsonProcessingException { 
     gen.writeStartArray(); 
     for (Map.Entry<?, ?> entry : value.entrySet()) { 
      gen.writeStartArray(); 
      gen.writeObject(entry.getKey()); 
      gen.writeObject(entry.getValue()); 
      gen.writeEndArray(); 
     } 
     gen.writeEndArray(); 
    } 

} 

mais j'ai aucune idée de comment écrire un JsonDeserializer pour faire le travail inverse. Aucune suggestion?

Note: Je dois la notation [ [ "key1", "value1" ], [ "key2", "value2" ] ] pour être en mesure de consommer que JSON en JavaScript et une new Map(...)JSON.stringify(map) produirait aussi que la notation (voir https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map).

Pour clarifier, une telle carte serait un champ d'autres classes, par ex.

public class Baz { 

    @JsonSerialize(using = MapToArraySerializer.class) 
    @JsonDeserialize(using = ArrayToMapDeserializer.class, keyAs = Foo.class, contentAs = Bar.class) 
    Map<Foo, Bar> map; 

} 

et ArrayToMapDeserializer extends JsonDeserializer<Map<?, ?>> est où je demande de l'aide.

+0

Je pense que vous êtes sur le compliquer. Votre JSON n'est pas une carte. Pourquoi utilisez-vous une 'Map' pour le représenter? Est-ce que 'Foo' et' Bar' ont des attributs qui les rendent différents? –

+0

Dans les scénarios de cas d'utilisation réelle, Foo et Bar sont des objets complexes et une carte avec Foo comme type de clé peut être un champ d'un autre objet. J'ai besoin de porter ces cartes Java en JavaScript, qui a son propre 'Map' qui sérialise sous cette forme (voir le lien vers MDN dans la question). –

+0

Oui, ils pourraient être des objets totalement différents (n'importe quel objet Java). J'ai mis à jour la question pour que ce soit clair! –

Répondre

3

je suis venu avec cette solution:

public class ArrayToMapDeserializer extends JsonDeserializer<SortedMap<Object, Object>> 
    implements ContextualDeserializer { 

    private Class<?> keyAs; 

    private Class<?> contentAs; 

    @Override 
    public Map<Object, Object> deserialize(JsonParser p, DeserializationContext ctxt) 
     throws IOException, JsonProcessingException { 
     return this.deserialize(p, ctxt, new HashMap<>()); 
    } 

    @Override 
    public Map<Object, Object> deserialize(JsonParser p, DeserializationContext ctxt, 
     Map<Object, Object> intoValue) throws IOException, JsonProcessingException { 
     JsonNode node = p.readValueAsTree(); 
     ObjectCodec codec = p.getCodec(); 
     if (node.isArray()) { 
      node.forEach(entry -> { 
       try { 
        JsonNode keyNode = entry.get(0); 
        JsonNode valueNode = entry.get(1); 
        intoValue.put(keyNode.traverse(codec).readValueAs(this.keyAs), 
         valueNode.traverse(codec).readValueAs(this.contentAs)); 
       } catch (NullPointerException | IOException e) { 
        // skip entry 
       } 
      }); 
     } 
     return intoValue; 
    } 

    @Override 
    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) 
     throws JsonMappingException { 
     JsonDeserialize jsonDeserialize = property.getAnnotation(JsonDeserialize.class); 
     this.keyAs = jsonDeserialize.keyAs(); 
     this.contentAs = jsonDeserialize.contentAs(); 
     return this; 
    } 

} 

qui peut être utilisé comme ceci:

public class Baz { 

    @JsonSerialize(using = MapToArraySerializer.class) 
    @JsonDeserialize(using = ArrayToMapDeserializer.class, 
     keyAs = Foo.class, contentAs = Bar.class) 
    Map<Foo, Bar> map; 

} 
0

Voici le deserialize:

@Override 
public Map<?, ?> deserialize(JsonParser p, DeserializationContext ctxt) 
     throws IOException, JsonProcessingException { 
    Map map = new LinkedHashMap(); 

    ObjectCodec oc = p.getCodec(); 
    JsonNode anode = oc.readTree(p); 

    for (int i = 0; i < anode.size(); i++) { 
     JsonNode node = anode.get(i); 
     map.put(node.get(0), node.get(1)); 
    } 

    return map; 
} 

J'ai ajouté quelques cas de test, avec une nouvelle mise en œuvre Oson, à la solution originale, dans laquelle je Oson pour faire la conversion, mais avec un autre convension: carte à JSON: {key1: valeur1, key2: valeur2, ...}, de sorte que la sortie JSON devient:

{ 
    { 
    "foo": "foo1" 
    }: { 
    "bar": "bar1" 
    }, 
    { 
    "foo": "foo2" 
    }: { 
    "bar": "bar2" 
    } 
} 

Vous pouvez consulter la source code!

+0

J'ai demandé Jackson et une sortie spécifique, vous fournissez un outil différent et une sortie différente. S'il vous plaît vérifier cela: http: // stackoverflow.com/help/comment-répondre –

+0

sans configuration correcte, jackson a ceci: {"[email protected]": {"bar": "bar1"}, "ca.oson.json. [email protected] ": {" bar ":" bar2 "}} –

+0

ArrayToMapDeserializer ne devrait pas trop difficile à mettre en œuvre, quand j'ai le temps le soir je vais essayer un pour vous –