Dans XStream, existe-t-il un meilleur moyen de marshall / unmarshall de lister les en JSON et Java

J’utilise le sérialiseur Stax JSON de XStream et JETTISON pour envoyer / recevoir des messages aux clients javascripts JSON et aux applications Web Java.

Je veux pouvoir créer une liste d’objects à envoyer au serveur et être correctement classés en Java, mais le format attendu par XStream et JSON est très non intuitif et nécessite que nos bibliothèques javascript sautent à travers des paniers.

[EDIT Problèmes de mise à jour à l’aide de la bibliothèque GSON ] J’ai tenté d’utiliser la bibliothèque GSON, mais celle-ci ne peut pas désérialiser des objects concrets lorsque je la demande uniquement aux super classes génériques (XStream et Jettison s’en chargent car les informations de type sont intégrées à la sérialisation).

La FAQ de GSON indique la limite de collecte :

Collections Limites

Peut sérialiser une collection d’objects arbitraires mais ne peut pas se désérialiser

Parce qu’il n’y a aucun moyen pour l’utilisateur d’indiquer le type de l’object résultant

Lors de la désérialisation, la collection doit être d’un type générique spécifique

J’utilise peut-être de mauvaises pratiques Java, mais comment puis-je créer un cadre de messagerie JSON vers Java qui envoie / reçoit divers objects Message concrets au format JSON?

Par exemple cela échoue:

public static void main(Ssortingng[] args) { Gson gson = new Gson(); MockMessage mock1 = new MockMessage(); MockMessage mock2 = new MockMessage(); MockMessageOther mock3 = new MockMessageOther(); List messages = new ArrayList(); messages.add(mock1); messages.add(mock2); messages.add(mock3); Ssortingng jsonSsortingng = gson.toJson(messages); //JSON list format is non-intuitive single element array with class name fields System.out.println(jsonSsortingng); List gsonJSONUnmarshalledMessages = (List)gson.fromJson(jsonSsortingng, List.class); //This will print 3 messages unmarshalled System.out.println("XStream format JSON Number of messages unmarshalled: " + gsonJSONUnmarshalledMessages.size()); } [{"val":1},{"val":1},{"otherVal":1,"val":1}] Exception in thread "main" com.google.gson.JsonParseException: The JsonDeserializer com.google.gson.DefaultTypeAdapters$CollectionTypeAdapter@638bd7f1 failed to deserialized json object [{"val":1},{"val":1},{"otherVal":1,"val":1}] given the type interface java.util.List 

Voici un exemple, je veux envoyer une liste de 3 objects Message, 2 sont du même type et le 3ème est d’un type différent.

 import java.util.ArrayList; import java.util.List; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver; class MockMessage { int val = 1; } class MockMessageOther { int otherVal = 1; } public class TestJSONXStream { public static void main(Ssortingng[] args) { JettisonMappedXmlDriver xmlDriver = new JettisonMappedXmlDriver(); XStream xstream = new XStream(xmlDriver); MockMessage mock1 = new MockMessage(); MockMessage mock2 = new MockMessage(); MockMessageOther mock3 = new MockMessageOther(); List messages = new ArrayList(); messages.add(mock1); messages.add(mock2); messages.add(mock3); Ssortingng jsonSsortingng = xstream.toXML(messages); //JSON list format is non-intuitive single element array with class name fields System.out.println(jsonSsortingng); List xstreamJSONUnmarshalledMessages = (List)xstream.fromXML(jsonSsortingng); //This will print 3 messages unmarshalled System.out.println("XStream format JSON Number of messages unmarshalled: " + xstreamJSONUnmarshalledMessages.size()); //Attempt to deserialize a reasonable looking JSON ssortingng Ssortingng jsonTest = "{"+ "\"list\" : ["+ "{"+ "\"MockMessage\" : {"+ "\"val\" : 1"+ "}"+ "}, {"+ "\"MockMessage\" : {"+ "\"val\" : 1"+ "}"+ "}, {"+ "\"MockMessageOther\" : {"+ "\"otherVal\" : 1"+ "}"+ "} ]"+ "};"; List unmarshalledMessages = (List)xstream.fromXML(jsonTest); //We expect 3 messages but XStream only deserializes one System.out.println("Normal format JSON Number of messages unmarshalled: " + unmarshalledMessages.size()); } } 

Intuitivement, je m’attends à ce que le XStream JSON soit sérialisé (et puisse se désérialiser correctement) à partir du format suivant:

 { "list" : [ { "MockMessage" : { "val" : 1 } }, { "MockMessage" : { "val" : 1 } }, { "MockMessageOther" : { "otherVal" : 1 } } ] } 

À la place, XStream crée une liste d’éléments unique avec des champs nommés noms de classe et tableaux nesteds d’objects du même type.

 { "list" : [ { "MockMessage" : [ { "val" : 1 }, { "val" : 1 } ], "MockMessageOther" : { "otherVal" : 1 } } ] } 

Le problème peut être causé par l’utilisation du XStream XML CollectionConverter ?

Quelqu’un a-t-il une suggestion pour une sérialisation correcte des objects Java JSON qui vous permet de lire / écrire des objects Java arbitraires? J’ai consulté le processeur JSON Java de Jackson, mais lorsque vous lisiez des objects dans un stream, vous deviez spécifier le type d’object différent de XStream, où il serait lu dans n’importe quel object (car le JSON XStream sérialisé contient des informations de nom de classe).

Je suis d’accord avec les autres postulants sur le fait que XStream n’est pas un bon choix – c’est un OXM (Object / Xml Mapper), et JSON est traité comme un format de sortie secondaire utilisant un chemin de traitement XML. C’est pourquoi une “convention” (expliquant comment convertir un modèle XML hierarchich en un modèle de graphe object de JSON et inversement) est nécessaire; et votre choix revient à utiliser ce qui est le moins intrusif de choix sous-optimaux. Cela fonctionne bien si XML est votre format de données principal et que vous avez simplement besoin d’une prise en charge rudimentaire de type JSON.

Pour obtenir une bonne prise en charge JSON, nous envisagerions d’utiliser une bibliothèque de traitement JSON qui effectue un mappage OJM réel (je suppose que Svenson en fait de même, mais en plus), telle que:

  • Jackson
  • Google-gson

En outre, même si vous devez prendre en charge XML et JSON, il est préférable pour OMI d’utiliser des bibliothèques distinctes pour ces tâches – les objects (beans) à utiliser côté serveur ne doivent pas nécessairement être différents, mais uniquement les bibliothèques de sérialisation converties vers / depuis. XML et JSON.

Je sais que cela n’est pas dans le sujet, mais j’aimerais présenter une solution dans Svenson JSON .

Avez-vous vraiment besoin de champs publics dans vos classes de domaine? En plus d’avoir à utiliser des propriétés, svenson peut gérer de tels cas avec une sortie JSON plus simple avec une propriété discriminante

 class Message { // .. your properties with getters and setters .. // special property "type" acts a signal for conversion } class MessageOther { ... } List list = new ArrayList(); list.add(new Message()); list.add(new MessageOther()); list.add(new Message()); Ssortingng jsonDataSet = JSON.defaultJSON().forValue(list); 

sortirait JSON comme

 [ {"type":"message", ... }, {"type":"message_other", ... }, {"type":"message", ... } ] 

qui pourrait être analysé à nouveau avec un code comme celui-ci

  // configure reusable parse instance JSONParser parser = new JSONParser(); // type mapper to map to your types PropertyValueBasedTypeMapper mapper = new PropertyValueBasedTypeMapper(); mapper.setParsePathInfo("[]"); mapper.addFieldValueMapping("message", Message.class); mapper.addFieldValueMapping("message_other", MessageOther.class); parser.setTypeMapper(mapper); List list = parser.parse(List.class, jsonDataset); 

Un mappeur de type svenson basé sur le nom complet de la classe ressemblerait à ceci:

 public class ClassNameBasedTypeMapper extends PropertyValueBasedTypeMapper { protected Class getTypeHintFromTypeProperty(Ssortingng value) throws IllegalStateException { try { return Class.forName(value); } catch (ClassNotFoundException e) { throw new IllegalStateException(value + " is no valid class", e); } } } 

ce qui n’est pas une implémentation idéale car il hérite de la configuration de PropertyValueBasedTypeMapper sans en avoir vraiment besoin. (devrait inclure une version plus propre dans svenson)

La configuration est très semblable à celle ci-dessus

 JSONParser parser = new JSONParser(); ClassNameBasedTypeMapper mapper = new ClassNameBasedTypeMapper(); mapper.setParsePathInfo("[]"); parser.setTypeMapper(mapper); List foos = parser .parse( List.class, "[{\"type\":\"package.Foo\"},{\"type\":\"package.Bar\"}]");