使用 Jackson 反序列化 JSON - 为什么 JsonMappingException “没有合适的构造函数”?

2022-09-01 20:17:23

我在使用 Jackson 反序列化 JSON 字符串时遇到问题(但我没有问题将对象序列化为 JSON)。

下面我将介绍我使用的类。当我发现一个JSON字符串(一个在其他地方序列化并通过Web服务检索的协议容器)并希望反序列化它时,问题就来了:

JSON 字符串:

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

我尝试像这样反序列化:

ProtocolContainer ret = ProtocolContainer.Create(jsonString);

在ProtocolContainer中执行的代码可以在下面看到。例外情况:

org.codehaus.jackson.map.JsonMappingException:没有为类型[简单类型,类MyPackage.ProtocolContainer]找到合适的构造函数:无法从JSON对象实例化(需要添加/启用类型信息?)在[来源: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

错误消息说明了一切,您的ProcortContainer没有默认构造函数,因此Jackson无法创建它的实例。(因为目前创建协议容器的唯一方法是传入DataPacket。


答案 2

在这种情况下,您可以向构造函数添加注释。有两种方法可以做到这一点:@JsonCreator

  • 如果仅添加该注释,则整个匹配的 JSON 首先绑定到唯一参数的类型(“DataPacket”)。我假设你不想这样做。
  • 如果您还在参数之前添加注释,则与该名称匹配的JSON属性将传递给构造函数(注释是必需的,因为Java字节代码不包含方法名称或构造函数参数) - 我怀疑您想要@JsonProperty@JsonProperty("SubPacket")

如果构造函数的必要信息来自 JSON,则此方法有效。如果没有,则确实需要添加备用 no-arg 构造函数。

顺便说一句,在这种情况下,错误消息听起来确实是错误的。仅当 JSON 数据与 JSON 字符串的预期值匹配时,才应给出它。