Десериализация JSON с Джексоном - почему JsonMappingException «Нет подходящего конструктора»? - PullRequest
23 голосов
/ 03 декабря 2011

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

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

JSON-строка:

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

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

ProtocolContainer ret = ProtocolContainer.Create(jsonString);

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

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

ProtocolContainer.java - класс контейнера, который инкапсулирует мои «подпакеты»:

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

Ответы [ 5 ]

31 голосов
/ 03 декабря 2011

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

15 голосов
/ 07 декабря 2011

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

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

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

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

2 голосов
/ 26 февраля 2015

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

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

1 голос
/ 10 декабря 2017

Еще одна возможность, если вы используете Ломбок ! Я не выяснил причину.

@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 решает его.

1 голос
/ 12 октября 2017

Я столкнулся с проблемой, и ни один из ответов не помог мне.кажется, что выброшенное исключение является очень общим и выбрасывается по 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": {}

, а не "creditcard": ""

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...