Reputation: 6741
I'm trying to use Jackson as a generic serialization engine instead of Java serialization. By initializing the mapper in the following way:
objectMapper = new ObjectMapper();
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
All type information in stored in JSON so I'm able to write and read all my objects back with:
objectMapper.readValue(json, Object.class)
I'm having problems when I try to serialize and then deserialize java arrays. Since Jackson doesn't store the array type into the JSON it fails later in the deserialization phase. In the following code:
String [] strings = {"A", "B", "C"};
try {
String json = objectMapper.writeValueAsString(strings);
String [] stringsBack = (String [])objectMapper.readValue(json, Object.class);
if (!strings.equals(stringsBack)) {
System.err.println("ERROR, stringsBack not the same!!!\n\n");
}
} catch (IOException e) {
e.printStackTrace();
}
json will be set to "["A","B","C"]" but on deserialization I get the following exception:
Exception in thread "main" java.lang.IllegalArgumentException: Invalid type id 'A' (for id type 'Id.class'): no such class found
at com.fasterxml.jackson.databind.jsontype.impl.ClassNameIdResolver._typeFromId(ClassNameIdResolver.java:66)
at com.fasterxml.jackson.databind.jsontype.impl.ClassNameIdResolver.typeFromId(ClassNameIdResolver.java:48)
at com.fasterxml.jackson.databind.jsontype.impl.TypeDeserializerBase._findDeserializer(TypeDeserializerBase.java:157)
at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._deserialize(AsArrayTypeDeserializer.java:94)
at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer.deserializeTypedFromAny(AsArrayTypeDeserializer.java:68)
at com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer$Vanilla.deserializeWithType(UntypedObjectDeserializer.java:494)
at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:42)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3560)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2576)
at Main.run(Main.java:86)
Is there a way to instruct Jackson to store java arrays types information as well into JSON? My serialization engine is generic and doesn't know in advance the type it is going to read from the JSON string.
Upvotes: 1
Views: 3525
Reputation: 239
There are Multiple issue with code.
1) Firstly let us see why the Exception
Exception in thread "main" java.lang.IllegalArgumentException: Invalid type id 'A' (for id type 'Id.class'): no such class found
The above error says that it did not find some Class and refers A as Invalid type id 'A'and it seems like an issue in Json .
The issue is because of the lines
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
This enabled Default Typing for all Non Final types. Default typing prints the type of Object with the actual value of Object thus enable deserialization into the same type. Declaring it NON_FINAL would not add the "natural" types such as String.
and
String [] stringsBack = (String [])objectMapper.readValue(json, Object.class);
Now while Deserialization the Json ["A", "B", "C"], the deserializer didnt know waht object the json is to be converted too and took A as a Class Type and tried to find the class which it did not get.
2) Commenting the First Line would probably give a ClassCastException because
objectMapper.readValue(json, Object.class);
would return an Object of ArrayList and it cannot be cast to String[].
3) !strings.equals(stringsBack)
will always return true as strings and stringsBack both are different objects of String[].
Correcting all the things following code would work
ObjectMapper objectMapper = new ObjectMapper();
//You can remove the below line if Default Typing is not Required.
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
String[] strings = { "A", "B", "C" };
try
{
String json = objectMapper.writeValueAsString(strings);
System.out.println(json);
String[] stringsBack = (String[]) objectMapper.readValue(json, new TypeReference<String[]>()
{});
if (!Arrays.equals(strings, stringsBack))
{
System.err.println("ERROR, stringsBack not the same!!!\n\n");
}
}
catch (IOException e)
{
e.printStackTrace();
}
Upvotes: 2
Reputation: 394
Correct way is:
String[] strings = { "A", "B", "C" };
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(strings);
List<String> data = mapper.readValue(json,
mapper.getTypeFactory().constructCollectionType(List.class, String.class));
for (String string : data) {
System.out.println(string);
}
Upvotes: 0
Reputation: 6741
The best solution I've found for now is checking before serialization if the object is an array (Class.isArray) and then doing the serialization:
TypeReference ref = new TypeReference<List[]>() { };
String json = objectMapper.writerFor(ref).writeValueAsString(strings);
This will add the array type into JSON and enable reading it back.
Upvotes: 2