I have a concrete object of an abstract class that I've made.
I'm using annotations on the abstract class and the daughter class, but the JSON output doesn't look right and I keep getting an exception on deserialiation.
I'm still learning Jackson, and unfortunatly a lot of the tutorials on the subject are outdated.
Here are the classes with my object mapper at the bottom:
Abstract Class:
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "BTRFSPhysicalLocationItem")
@JsonSubTypes({
@Type(name = "Snapshot", value = Snapshot.class),
@Type(name = "Backup", value = Backup.class),
@Type(name = "Subvolume", value = Subvolume.class)})
public abstract class BTRFSPhysicalLocationItem {
private final String name;
@JsonProperty
private final Path location;
/**
* Makes a new subvolume instance with the specified location. May or may
* not actually exist.
*
* @param location The location of the subvolume
*/
@JsonCreator
protected BTRFSPhysicalLocationItem(@JsonProperty(value = "location") Path location) {
this.location = location;
this.name = location.getFileName().toString();
}
Concrete Class:
public class Subvolume extends BTRFSPhysicalLocationItem {
/**
* Creates a new subvolume with the specified path.The subvolume may or may
* not exist at this point.
*
* @param location
*/
@JsonCreator
public Subvolume(@JsonProperty Path location) {
super(location);
}
Objectmapper block:
ObjectMapper MAPPER = new ObjectMapper();
List<Subvolume> SUBVOLUME_LIST = new ArrayList<>();
//Subvolume is populated by other methods it's not worth showing them here
System.out.println("\n\n");
MAPPER.writeValue(System.out, SUBVOLUME_LIST);
System.out.println("\n\n\n");
var s = MAPPER.writeValueAsString(SUBVOLUME_LIST);
List<Subvolume> list = MAPPER.readValue(s, new TypeReference<List<Subvolume>>() {
});
Output JSON: [{"name":"WanderingEcho","location":"file:///home/sarah/NetBeansProjects/WanderingEcho/"}]
Exception:
Aug 05, 2019 4:55:14 PM com.protonmail.sarahszabo.wanderingecho.btrfs.BTRFS <clinit>
SEVERE: null
com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Missing type id when trying to resolve subtype of [simple type, class com.protonmail.sarahszabo.wanderingecho.btrfs.subvolume.Subvolume]: missing type id property 'BTRFSPhysicalLocationItem'
at [Source: (String)"[{"name":"WanderingEcho","location":"file:///home/sarah/NetBeansProjects/WanderingEcho/"}]"; line: 1, column: 89] (through reference chain: java.util.ArrayList[0])
Other answers such as Jackson deserialization of polymorphic types suggest using the ObjectMapper
methods, but according to the tutorials I've read, this shouldn't be necessary.
Not sure what I'm doing wrong with the annotations.
In your json
you are missing the BTRFSPhysicalLocationItem
- just think about it - how Jackson should know what implementation do you expect?
It should look like
[
{
"BTRFSPhysicalLocationItem": "Subvolume",
"name": "WanderingEcho",
"location":"file:///home/sarah/NetBeansProjects/WanderingEcho/"
}
]
if you want Jackson to include the type when serializing the object you also need to add JsonTypeInfo.As.PROPERTY
as include
as a parameter of @JsonTypeInfo
annotation like
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "BTRFSPhysicalLocationItem")
EDIT ok seems that I figured this out - your problem with not generating properties is connected to Java type erasure and well described here.
tldr; Jackson has problem with underestanding type - seems List<Subvolume>
for him does not providing the type of elements. Because he doesn't know the type he won't serialize it
The solution is to create a helper class that will extends ArrayList<Subvolume>
like
public class SubvolumeList extends ArrayList<Subvolume> {}
Additionaly you have a few issues in your code - one is that you should use use = JsonTypeInfo.Id.NAME
in @JsonTypeInfo
if you want to deserialize by property value (like Subvolume
) not by package/class string literal
Second is that you should name @JsonProperty
on @JsonCreator
parameter list like @JsonProperty(value = "location")