【问题标题】: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;没有单字符串构造函数/工厂方法。如果我添加一个接受字符串的构造函数,那么没有错误但对象是“空的”......我认为没有必要在构造函数中添加实现支持。 我应该如何解决这个问题?

标签: java json jackson


【解决方案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就解决了。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-01-12
        • 2021-06-29
        • 1970-01-01
        • 1970-01-01
        • 2021-11-14
        • 2020-10-06
        • 2016-03-24
        相关资源
        最近更新 更多