首页 文章

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

提问于
浏览
23

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

下面我介绍我使用的课程 . 当我接收一个JSON字符串(一个在其他地方序列化并通过webservice检索的ProtocolContainer)并想要反序列化它时,问题出现了:

JSON字符串:

{“DataPacketJSONString”:null,“DataPacketType”:“MyPackage.DataPackets.LoginRequestReply”,“MessageId”:6604,“SenderUsername”:null,“SubPacket”:{“__ type”:“LoginRequestReply:#MyPackage.DataPackets”, “原因”:“错误的通行证或用户名”,“成功”:false,“用户名”:“用户1”}}

我试着像这样反序列化:

ProtocolContainer ret = ProtocolContainer.Create(jsonString);

并且可以在下面看到在ProtocolContainer中执行的代码 . 例外:

org.codehaus.jackson.map.JsonMappingException:找不到类型[simple type,class MyPackage.ProtocolContainer]的合适构造函数:无法在[源代码:java.io]中实例化JSON对象(需要添加/启用类型信息?) .StringReader @ 4059dcb0; line:1,column:2]

我真的很感激这里的一些输入=)Thx!

ProtocolContainer.java - a container class that encapsulates my "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 - a superclass for all my datapackets

public class DataPacket 
{

}

LoginRequestReply.java - a DataPacket

package MyPackage.DataPackets;

import MyPackage.DataPacket;

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

5 回答

  • 1

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

  • 31

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

    • 如果只添加该注释,则整个匹配的JSON首先绑定到唯一参数的类型(`DataPacket') . 我假设你不想这样做 .

    • 如果你还在参数之前添加 @JsonProperty 注释,那么匹配该名称的JSON属性将传递给构造函数(注释是必需的,因为Java字节代码不包含方法或构造函数参数的名称) - 我怀疑你想要 @JsonProperty("SubPacket")

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

    顺便说一下,在这种情况下错误消息确实是错误的 . 如果JSON数据与JSON字符串匹配期望值,则应该给出它 .

  • 2

    Thumb Rule :为您用作映射类的每个类添加默认构造函数 . 你错过了这个,问题就出现了!

    只需添加默认构造函数即可 .

  • 15

    我正面临这个问题,没有一个答案对我有用 . 似乎抛出的异常是非常通用的,并且被抛出了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响应中有类似的东西:

    “信用卡”:{}

    而不是“信用卡”:“”

    它解决了这个问题 .

  • 1

    如果你使用 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 而不是自己编写,那么jackson会成功反序列化 . 我今天得到了相同的经验,添加 @AllArgsConstructor 解决了它 .

相关问题