J'ai un problème de désérialisation d'une chaîne JSON à l'aide de Jackson (mais je n'ai aucun problème à sérialiser un objet en JSON).
Ci-dessous, je présente les classes que j'utilise. Le problème survient lorsque je reçois une chaîne JSON (un ProtocolContainer qui a été sérialisé ailleurs et récupéré via le service Web) et que je souhaite la désérialiser:
Chaîne JSON:
{"DataPacketJSONString": null, "DataPacketType": "MyPackage.DataPackets.LoginRequestRhness", "MessageId": 6604, "SenderUsername": null, "SubPacket": {"__ type": "LoginRequestRhness: # MyPackage.DataPackets Motif ":" Passe ou nom d'utilisateur incorrect "," Succès ": faux," Nom d'utilisateur ":" Utilisateur1 "}}
J'essaye de désérialiser comme ceci:
ProtocolContainer ret = ProtocolContainer.Create(jsonString);
et le code qui s'exécute dans ProtocolContainer peut être vu ci-dessous. L'éxéption:
org.codehaus.jackson.map.JsonMappingException: aucun constructeur approprié trouvé pour le type [type simple, classe MyPackage.ProtocolContainer]: ne peut pas instancier à partir de l'objet JSON (besoin d'ajouter/activer les informations de type?) à [Source: Java.io. StringReader @ 4059dcb0; ligne: 1, colonne: 2]
ProtocolContainer.Java - une classe de conteneur qui encapsule mes "SubPackets":
import Java.io.IOException;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import MyPackage.DataPackets.*;
public class ProtocolContainer
{
public String SenderUsername;
public String DataPacketType;
public long MessageId;
public String DataPacketJSONString;
public DataPacket SubPacket;
public ProtocolContainer(DataPacket dp)
{
DataPacketType = dp.getClass().toString().substring(6);
SubPacket = dp;
}
public String toJSON()
{
try {
if (SubPacket != null)
this.DataPacketJSONString = ProtocolContainer.mapper.writeValueAsString(SubPacket);
return ProtocolContainer.mapper.writeValueAsString(this);
} catch (JsonGenerationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JsonMappingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
public static ObjectMapper mapper = new ObjectMapper();
public static ProtocolContainer Create(String jsonString)
{
ProtocolContainer pc = null;
try {
pc = mapper.readValue(jsonString, ProtocolContainer.class); // error here!
} catch (JsonParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JsonMappingException e) {
// TODO Auto-generated catch block
e.printStackTrace(); // Exception when deserializing
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try
{
if (pc != null && pc.DataPacketType == "LoginRequest")
pc.SubPacket = mapper.readValue(jsonString, LoginRequest.class);
}
catch (JsonParseException e)
{
e.printStackTrace();
}
catch (JsonMappingException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
return pc;
}
}
DataPacket.Java - une superclasse pour tous mes paquets de données
public class DataPacket
{
}
LoginRequestRhness.Java - un DataPacket
package MyPackage.DataPackets;
import MyPackage.DataPacket;
public class LoginRequestReply extends DataPacket
{
public boolean LoginOK;
public int UserId;
}
Les messages d'erreur en disent long, votre ProtocolContainer n'a pas de constructeur par défaut, Jackson n'est donc pas en mesure d'en créer une instance. (Étant donné que la seule façon actuelle de créer un ProtocolContainer est de passer un DataPacket.)
Dans ce cas, vous pouvez ajouter une annotation @JsonCreator
Au constructeur. Il y a deux façons de procéder:
@JsonProperty
Avant l'argument, la propriété JSON correspondant à ce nom est transmise au constructeur (l'annotation est obligatoire car Java ne contient PAS le nom de la méthode ou du constructeur) arguments) - Je suppose que vous voulez @JsonProperty("SubPacket")
Cela fonctionne si les informations nécessaires pour le constructeur proviennent de JSON. Sinon, vous devez ajouter un autre constructeur sans argument.
Par ailleurs, le message d'erreur ne sonne pas correctement dans ce cas. Il ne doit être donné que si les données JSON correspondent à la valeur attendue s'il s'agit d'une chaîne JSON.
Thumb Rule: Ajoutez un constructeur par défaut pour chaque classe que vous avez utilisée comme classe de mappage. Vous avez raté cela et le problème se pose!
Ajoutez simplement un constructeur par défaut et cela devrait fonctionner.
Une autre possibilité si vous utilisez Lombok! Je n'ai pas trouvé la raison.
@Getter
@NoArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
public Car implements Serializable {
Map<String, Object> basicInfo;
CarEnums.TypeEnum type;
List<Maintenance> maintenances;
public void addMaintenance(Maintenance m) {
// initialize if null
maintenances.add(m);
}
// must be static or jackson throws "inner class cannot be static" exception. Yes you see it right.
public static class Maintenance {
private Long id;
public class Maintenance(Long id) { // constructor causes the exception
this.id = id;
}
}
...
}
Si l'annotation du constructeur lombok est utilisée au niveau de la classe externe, la classe interne même si on écrit tous les constructeurs args manuellement, elle se plaint toujours que le constructeur est introuvable. Si tu utilises @AllArgsConstructor
on Maintenance
au lieu d'écrire le vôtre, jackson se désérialise avec succès. J'ai eu la même expérience aujourd'hui, en ajoutant @AllArgsConstructor
le résout.
J'étais confronté au problème et aucune des réponses n'a fonctionné pour moi. il semble que l'exception levée soit très générique et soit levée pour n nombre de causes. Une solution peut donc ne pas fonctionner pour tout le monde. Mon cas: nous avons une réponse JSON dans laquelle la carte de crédit est un type complexe mais facultatif. quand il n'y a pas de données de carte de crédit, nous obtenions une chaîne vide en réponse:
"carte de crédit":""
Mais la carte de crédit est un type complexe pour nous:
<xs:element name="CC" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="aaa" type="xs:string" minOccurs="0"/>
<xs:element name="bbb" type="xs:string" minOccurs="0"/>
<xs:element name="ccc" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:element>
Nous avons compris que s'il n'y a pas de données de carte de crédit, nous devrions avoir quelque chose comme ça dans la réponse json:
"carte de crédit":{}
et non "carte de crédit": ""
il a résolu ce problème.