ti-enxame.com

Desserializando JSON com Jackson - Por que JsonMappingException "Nenhum construtor adequado"?

Tenho um problema ao desserializar uma string JSON usando Jackson (mas não tenho problema em serializar um objeto para JSON).

Abaixo, apresento as aulas que uso. O problema ocorre quando recebo uma string JSON (um ProtocolContainer que foi serializado em outro local e recuperado via webservice) e quero desserializá-lo:

Cadeia JSON:

{"DataPacketJSONString": null, "DataPacketType": "MyPackage.DataPackets.LoginRequestReply", "MessageId": 6604, "SenderUsername": null, "SubPacket": {"__ type": "LoginRequestReply: # MyPackage.DataPackets", " Motivo ":" Senha ou nome de usuário incorretos "," Sucesso ": false," Nome de usuário ":" Usuário1 "}}

Eu tento desserializar assim:

ProtocolContainer ret = ProtocolContainer.Create(jsonString);

e o código que é executado no ProtocolContainer pode ser visto abaixo. A exceção:

org.codehaus.jackson.map.JsonMappingException: nenhum construtor adequado encontrado para o tipo [tipo simples, classe MyPackage.ProtocolContainer]: não é possível instanciar o objeto JSON (é necessário adicionar/ativar informações de tipo?) em [Fonte: Java.io. StringReader @ 4059dcb0; linha: 1, coluna: 2]

ProtocolContainer.Java - uma classe de contêiner que encapsula meus "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 - uma superclasse para todos os meus pacotes de dados

public class DataPacket 
{

}

LoginRequestReply.Java - um DataPacket

package MyPackage.DataPackets;

import MyPackage.DataPacket;

public class LoginRequestReply extends DataPacket
{
    public boolean LoginOK;
    public int UserId;
}
23
Ted

As mensagens de erro dizem tudo, seu ProtocolContainer não possui um construtor padrão, portanto, Jackson não pode criar uma instância dele. (Como a única maneira atual de criar um ProtocolContainer é passando um DataPacket.)

31
Ola Herrdahl

Nesse caso, você pode adicionar a anotação @JsonCreator Ao construtor. Existem duas maneiras de fazer isso:

  • Se você adicionar apenas essa anotação, todo o JSON correspondente será primeiro vinculado ao tipo do único argumento (`DataPacket '). Eu suponho que você não quer fazer isso.
  • Se você também adicionar a anotação @JsonProperty Antes do argumento, a propriedade JSON correspondente a esse nome será transmitida ao construtor (a anotação é obrigatória porque Java Java NÃO contém o nome do método ou construtor) argumentos) - Suspeito que você queira @JsonProperty("SubPacket")

Isso funciona se as informações necessárias para o construtor vierem do JSON. Caso contrário, você precisará adicionar um construtor alternativo no-arg.

A propósito, a mensagem de erro parece errada neste caso. Só deve ser fornecido se os dados JSON corresponderem ao valor esperado se uma String JSON.

15
StaxMan

Regra do polegar: adicione um construtor padrão para cada classe que você usou como uma classe de mapeamento. Você perdeu isso e o problema surge!

Basta adicionar um construtor padrão e ele deve funcionar.

2
Badal

Outra possibilidade se você usar Lombok! Eu não descobri o motivo.

@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 a anotação do construtor lombok for usada na classe externa, a classe interna, mesmo que alguém escreva todo o construtor args manualmente, ainda reclama que o construtor não foi encontrado. Se você usar @AllArgsConstructor em Maintenance em vez de escrever o seu próprio, jackson desserializaria com sucesso. Eu tive a mesma experiência hoje, adicionando @AllArgsConstructor resolve.

1
Tiina

Eu estava enfrentando o problema e nenhuma das respostas funcionou para mim. parece que a exceção lançada é muito genérica e é lançada para n número de causas. Portanto, uma correção pode não funcionar para todos. Meu caso: temos uma resposta json em que cartão de crédito é um tipo complexo, mas opcional. quando não há dados do cartão de crédito, recebíamos uma string vazia em resposta:

"cartão de crédito":""

Mas o cartão de crédito é um tipo complexo para nós:

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

Descobrimos que, se não houver dados do cartão de crédito, deveríamos ter algo parecido com isto na resposta json:

"cartão de crédito":{}

e não "cartão de crédito": ""

corrigiu esse problema.

1
vks