web-dev-qa-db-fra.com

Désérialisation de JSON avec Jackson - Pourquoi JsonMappingException "Pas de constructeur approprié"?

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;
}
23
Ted

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.)

31
Ola Herrdahl

Dans ce cas, vous pouvez ajouter une annotation @JsonCreator Au constructeur. Il y a deux façons de procéder:

  • Si vous ajoutez uniquement cette annotation, l'ensemble du JSON correspondant est d'abord lié au type du seul argument (`DataPacket '). Je suppose que vous ne voulez pas faire ça.
  • Si vous ajoutez également l'annotation @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.

15
StaxMan

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.

2
Badal

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.

1
Tiina

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.

1
vks