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

Posted

技术标签:

【中文标题】使用 Jackson 反序列化 JSON - 为啥 JsonMappingException“没有合适的构造函数”?【英文标题】:Deserializing JSON with Jackson - Why JsonMappingException "No suitable constructor"?使用 Jackson 反序列化 JSON - 为什么 JsonMappingException“没有合适的构造函数”? 【发布时间】:2012-01-12 03:19:37 【问题描述】:

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

下面我介绍我使用的类。当我收到一个 JSON 字符串(一个在别处序列化并通过 web 服务检索的 ProtocolContainer)并想要反序列化它时,问题就出现了:

JSON 字符串:

"DataPacketJSONString":null,"DataPacketType":"MyPackage.DataPackets.LoginRequestReply","MessageId":6604,"SenderUsername":null,"SubPacket":"__type":"LoginRequestReply:#MyPackage.DataPackets ","Reason":"错误的密码或用户名","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;

【问题讨论】:

跟进:经过一番折腾,我没有收到以下错误:JsonMappingException: Can not instantiate value of type [simple type, class MyPackage.ProtocolContainer] from JSON String;没有单字符串构造函数/工厂方法。如果我添加一个接受字符串的构造函数,那么没有错误但对象是“空的”......我认为没有必要在构造函数中添加实现支持。 我应该如何解决这个问题? 【参考方案1】:

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

【讨论】:

是的,我添加了一个空的构造函数。这很奇怪,因为我之前遇到了另一个错误,当我有一个空的构造函数时......无论如何,这个特殊问题已经解决......不,我必须处理.NET在序列化中添加的“__type”东西...... 嗯,所以错误又回来了,有点。现在我得到了这个: JsonMappingException: Can not instantiate value of type [simple type, class MyPackage.ProtocolContainer] from JSON String;没有单字符串构造函数/工厂方法。如果我添加一个接受字符串的构造函数,那么没有错误,但对象是“空的”……我认为没有必要在构造函数中添加实现支持…… 忘记放默认构造函数了,现在可以了,谢谢^^【参考方案2】:

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

如果您只添加该注释,则整个匹配 JSON 将首先绑定到唯一参数 (`DataPacket') 的类型。我假设您不想这样做。 如果您还在参数前添加@JsonProperty 注释,则匹配该名称的 JSON 属性将传递给构造函数(注释是强制性的,因为 Java 字节码不包含方法或构造函数参数的名称)——我怀疑你想要 @ 987654323@

如果构造函数的必要信息来自 JSON,则此方法有效。如果没有,您确实需要添加替代的无参数构造函数。

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

【讨论】:

【参考方案3】:

拇指规则:为您用作映射类的每个类添加一个默认构造函数。你错过了这个,问题就出现了!

只需添加一个默认构造函数,它就可以工作了。

【讨论】:

以上答案都不正确,唯一对我有用的是 public ConsolidationUserAndManagerCredentialsRequest() super () ;没有super() 没有任何作用。【参考方案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 响应中有这样的内容:

“信用卡”:

而不是“信用卡”:“”

它解决了这个问题。

【讨论】:

【参考方案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构造函数,它仍然会抱怨找不到构造函数。如果您在Maintenance 上使用@AllArgsConstructor 而不是自己编写,杰克逊将成功反序列化。我今天也有同样的经历,加了@AllArgsConstructor就解决了。

【讨论】:

以上是关于使用 Jackson 反序列化 JSON - 为啥 JsonMappingException“没有合适的构造函数”?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Jackson 反序列化来自 json 对象的对象数组

使用 Jackson 反序列化 JSON 的问题

使用 Jackson 反序列化:获取 Json 对象设置的字段列表

无法使用泛型使用 Jackson 反序列化动态 json

如何使用jackson(Java)反序列化对象中的json对象?

在scala中使用jackson反序列化json