Deserializing JSON с Jackson - Почему JsonMappingException "Нет подходящего конструктора"?

У меня проблема с десериализацией строки JSON с помощью Jackson (но у меня нет проблем с сериализацией объекта в JSON).

Ниже я представляю классы, которые я использую. Проблема возникает, когда я получаю JSON-строку (ProtocolContainer, который был сериализован в другом месте и получен через веб-сервис) и хочу десериализовать его:

JSON-строку:

{ "DataPacketJSONString": нулевой, "DataPacketType": "MyPackage.DataPackets.LoginRequestReply", "MessageId": 6604, "SenderUsername": нулевой, "подпакет": { "__ типа": "LoginRequestReply: # MyPackage.DataPackets"," Причина ":" Неверный пароль или имя пользователя "," Успех ": ложь," Имя пользователя ":" Пользователь1 "}}

Я пытаюсь десериализовать так:

ProtocolContainer ret = ProtocolContainer.Create(jsonString);

и код, который выполняется в ProtocolContainer, можно увидеть ниже. Исключение:

org.codehaus.jackson.map.JsonMappingException: не найден подходящий конструктор для типа [простой тип, класс MyPackage.ProtocolContainer]: невозможно создать экземпляр объекта JSON (необходимо добавить/включить информацию о типе?) в [Source: java.io. StringReader @4059dcb0; строка: 1, столбец: 2]

ProtocolContainer.java - класс контейнера, который инкапсулирует мои "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 - суперкласс для всех моих пакетов данных

public class DataPacket 
{

}

LoginRequestReply.java - пакет данных

package MyPackage.DataPackets;

import MyPackage.DataPacket;

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

Ответ 1

В сообщениях об ошибках сказано все, у вашего ProtocolContainer нет конструктора по умолчанию, поэтому Джексону не удается создать экземпляр. (Поскольку единственный способ создания протокола - это передать DataPacket.)

Ответ 2

В этом случае вы можете добавить аннотацию @JsonCreator к конструктору. Это можно сделать двумя способами:

  • Если вы добавляете только эту аннотацию, то весь соответствующий JSON сначала привязан к типу единственного аргумента (`DataPacket '). Я предполагаю, что вы не хотите этого делать.
  • Если вы добавите аннотацию @JsonProperty перед аргументом, то свойство JSON, соответствующее этому имени, передается конструктору (аннотация обязательна, потому что байт-код Java НЕ содержит имя аргумента метода или аргумента конструктора). Я подозреваю, что вы хотите @JsonProperty("SubPacket")

Это работает, если необходимая информация для конструктора поступает из JSON. Если нет, вам нужно добавить альтернативный конструктор no-arg.

Кстати, сообщение об ошибке в этом случае звучит неправильно. Он должен указываться только в том случае, если данные JSON соответствуют ожидаемому значению, если строка JSON.

Ответ 3

Правило Thumb. Добавьте конструктор по умолчанию для каждого класса, который вы использовали в качестве класса сопоставления. Вы пропустили это и возникли проблемы!

Просто добавьте конструктор по умолчанию и он должен работать.

Ответ 4

Я столкнулся с проблемой, и ни один из ответов не работал у меня. кажется, что заброшенное исключение является очень общим и бросается за n количество причин. Поэтому одно исправление может не работать для всех. Мое дело: у нас есть ответ json, в котором кредитная карта является сложным типом, но необязательна. когда нет данных кредитной карты, мы получили пустую строку в ответ:

"CreditCard": ""

Но кредитная карта для нас сложная:

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

Мы выяснили, что если нет данных кредитной карты, у нас должно быть что-то вроде этого в json-ответе:

"CreditCard": {}

а не "кредитная карта": "

он исправил эту проблему.

Ответ 5

Другая возможность, если вы используете Lombok! Я не нашел причины.

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

Если во внешнем классе используется аннотатор конструктора lombok, внутренний класс, даже если он записывает каждый конструктор args вручную, он все еще жалуется, что конструктор не может быть найден. Если вы используете @AllArgsConstructor на Maintenance вместо того, чтобы писать свои собственные, джексон успешно десериализует. Сегодня у меня такой же опыт, добавив @AllArgsConstructor.