Reputation: 20184
I have a problem deserializing a JSON string using Jackson (but I have no problem serializing an object to JSON).
Below I present the classes I use. The problem comes when I rececive a JSON-string (a ProtocolContainer that was serialized elsewhere and retrieved via webservice) and want to de-serialize it:
JSON-string:
{"DataPacketJSONString":null,"DataPacketType":"MyPackage.DataPackets.LoginRequestReply","MessageId":6604,"SenderUsername":null,"SubPacket":{"__type":"LoginRequestReply:#MyPackage.DataPackets","Reason":"Wrong pass or username","Success":false,"Username":"User1"}}
I try to deserialize like this:
ProtocolContainer ret = ProtocolContainer.Create(jsonString);
and the code that executes in ProtocolContainer can be seen below. The exception:
org.codehaus.jackson.map.JsonMappingException: No suitable constructor found for type [simple type, class MyPackage.ProtocolContainer]: can not instantiate from JSON object (need to add/enable type information?) at [Source: java.io.StringReader@4059dcb0; line: 1, column: 2]
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;
}
Upvotes: 24
Views: 96809
Reputation: 4797
Another possibility if you use Lombok! I haven't found out the reason.
@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;
}
}
...
}
If lombok constructor annotation is used at outer class, the inner class even if one writes all args constructor manually, it still complains the constructor cannot be found. If you use @AllArgsConstructor
on Maintenance
instead of writing your own, jackson would deserialise successfully. I got the same experience today, adding @AllArgsConstructor
solves it.
Upvotes: 1
Reputation: 21
I was facing the issue and none of the answers worked for me. it seems the exception thrown is very generic one and is thrown for n number of causes. So one fix may not work for everyone. My case: we have a json response in which creditcard is a complex type but optional. when there is no creditcard data, we were getting an empty string in response:
"creditcard":""
But Credit card is a complex type for us:
<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>
We figured out, if there is no creditcard data, we should have something like this in json response:
"creditcard":{}
and not "creditcard":""
it fixed this issue.
Upvotes: 1
Reputation: 116630
In this case, you could add @JsonCreator
annotation to constructor. There are two ways it could be done:
@JsonProperty
annotation before the argument, then JSON property matching that name is passed to constructor (annotation is mandatory because Java byte code does NOT contain name of method or constructor arguments) -- I suspect you want @JsonProperty("SubPacket")
This works if necessary information for constructor comes from JSON. If not, you do need to add alternate no-arg constructor.
By the way, the error message does sound wrong in this case. It should only be given if JSON data matching expected value if a JSON String.
Upvotes: 15
Reputation: 4188
Thumb Rule: Add a default constructor for each class you used as a mapping class. You missed this and issue arise!
Simply add a default constructor and it should work.
Upvotes: 2
Reputation: 4296
The error messages says it all, your ProtocolContainer does not have a default constructor so Jackson is unable to create an instance of it. (Since the only current way of creating a ProtocolContainer is by passing in a DataPacket.)
Upvotes: 31