2011-12-03 33 views
23

Ho un problema deserializzare una stringa JSON usando Jackson (ma non ho problemi nel serializzare un oggetto su JSON).Deserializzazione JSON con Jackson - Perché JSONMappingException "Nessun costruttore adatto"?

Di seguito vi presento le classi che uso. Il problema nasce quando ho rececive un JSON-stringa (una ProtocolContainer che è stato serializzato altrove e recuperato tramite webservice) e voglio deserializzare esso:

JSON-string:

{"DataPacketJSONString":null,"DataPacketType":"MyPackage.DataPackets.LoginRequestReply","MessageId":6604,"SenderUsername":null,"SubPacket":{"__type":"LoginRequestReply:#MyPackage.DataPackets","Reason":"Wrong pass or username","Success":false,"Username":"User1"}}

cerco di deserializzare come questo:

ProtocolContainer ret = ProtocolContainer.Create(jsonString); 

e il codice che si esegue in ProtocolContainer può essere visto di seguito. L'eccezione:

org.codehaus.jackson.map.JsonMappingException: No suitable constructor found for type [simple type, class MyPackage.ProtocolContainer]: can not instantiate from JSON object (need to add/enable type information?) at [Source: [email protected]; line: 1, column: 2]

Mi piacerebbe davvero apprezzare un certo input qui =) Thx!

ProtocolContainer.java - una classe contenitore che racchiude in sé i miei "sottopacchetti":

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 - una superclasse per tutti i miei datapackets

public class DataPacket 
{ 

} 

LoginRequestReply. java - a DataPacket

package MyPackage.DataPackets; 

import MyPackage.DataPacket; 

public class LoginRequestReply extends DataPacket 
{ 
    public boolean LoginOK; 
    public int UserId; 
} 
+1

Follow-up: Dopo alcuni problemi, non viene visualizzato il seguente errore: * JsonMappingException: impossibile creare un'istanza del valore di tipo [tipo semplice, classe MyPackage.ProtocolContainer] da JSON String; nessun metodo costruttore/factory a stringa singola *. Se aggiungo un costruttore che accetta una stringa, quindi nessun errore, ma l'oggetto è "vuoto" ... Non penso che sia necessario aggiungere il supporto all'implementazione nel costruttore. ** Come dovrei risolvere questo? ** – Ted

risposta

31

I messaggi di errore dicono tutto, il tuo ProtocolContainer non ha un costruttore predefinito quindi Jackson non è in grado di crearne un'istanza. (Poiché l'unico modo attuale di creare un ProtocolContainer è passare in un DataPacket.)

+1

sì, ho aggiunto un costruttore vuoto. È strano, perché ho avuto un altro errore prima, quando avevo un costruttore vuoto ... Ad ogni modo, questo particolare problema è stato risolto ... non ho a che fare con la cosa "__type" che .NET aggiunge sulla serializzazione ... – Ted

+0

Hmm, quindi l'errore è tornato, una specie di. Ora ho capito: * JsonMappingException: impossibile istanziare il valore di tipo [tipo semplice, classe MyPackage.ProtocolContainer] da JSON String; nessun metodo costruttore/factory a stringa singola *. Se aggiungo un costruttore che accetta una stringa, quindi nessun errore ma l'oggetto è "vuoto" ... Non penso che sia necessario aggiungere il supporto all'implementazione nel costruttore ... – Ted

+0

Ho dimenticato di mettere il costruttore predefinito, ora funziona, grazie ^^ – bzhWarrior

15

In questo caso, è possibile aggiungere l'annotazione @JsonCreator al costruttore. Ci sono due modi che si poteva fare:

  • Se si aggiunge solo che l'annotazione, l'intero JSON corrispondente viene prima legato al tipo di l'unico argomento (`datapacket '). Presumo che tu non voglia farlo.
  • Se anche si aggiunge @JsonProperty annotazione prima che l'argomento, poi JSON proprietà corrispondenza tale nome viene passato al costruttore (annotazione è obbligatoria perché Java bytecode non contiene il nome del metodo o costruttore argomenti) - Ho il sospetto che si desidera @JsonProperty("SubPacket")

Questo funziona se le informazioni necessarie per il costruttore provengono da JSON. In caso contrario, è necessario aggiungere un costruttore alternativo no-arg.

A proposito, il messaggio di errore sembra errato in questo caso. Deve essere fornito solo se i dati JSON corrispondono al valore previsto se una stringa JSON.

2

Thumb Rule: aggiungere un costruttore predefinito per ogni classe utilizzata come classe di mapping. Ti sei perso questo e il problema sorge!

Basta aggiungere un costruttore predefinito e dovrebbe funzionare.

+0

Nessuna delle risposte sopra sono esatte, l'unica cosa che ha funzionato per me è stato \t ConsolidationUserAndManagerCredentialsRequest pubblico() { \t \t super(); \t} Senza 'super()' non funziona nulla. – Siddharth

1

Ero di fronte al problema e nessuna delle risposte ha funzionato per me. sembra che l'eccezione generata sia generica e generata per n numero di cause. Quindi una correzione potrebbe non funzionare per tutti. Il mio caso: abbiamo una risposta JSON in cui la carta di credito è un tipo complesso ma facoltativo. quando non ci sono dati carta di credito, stavamo ottenendo una stringa vuota in risposta:

"carta di credito": ""

Ma carta di credito è un tipo complesso per noi:

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

Abbiamo capito , se non ci sono dati di carta di credito, dovremmo avere qualcosa di simile in risposta jSON:

"carta di credito": {}

e non "carta di credito": ""

risolto questo problema.

1

Un'altra possibilità se si utilizza Lombok! Non ho scoperto la ragione.

@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; 
     } 
    } 
    ... 
} 

Se Lombok costruttore annotazione è utilizzata in classe esterna, la classe interna, anche se uno scrive tutto args costruttore manualmente, lamenta ancora il costruttore non può essere trovato. Se si utilizza @AllArgsConstructor su Maintenance anziché scrivere il proprio, Jackson si deserializza correttamente. Ho avuto la stessa esperienza oggi, aggiungendo @AllArgsConstructor risolve.

Problemi correlati