mtyson
mtyson

Reputation: 8560

Jackson: Deserialize instantiated subclass of interface set (yml)

Given the following yml:

---
  commands:
    dev: !foo.bar.Baz {}

And the following class

public class PluginFile {
    private Map<String, Command> commands = new HashMap<String, Command>();
    public Map<String, Command> getCommands() {
        return commands;
    }
    public void setCommands(Map<String, Command> commands) {
        this.commands = commands;
    }

And reading the file:

mapper.readValue(theFile, PluginFile.class);

Results in error:

com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of foo.bar.command.Command, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information
 at [Source: C:\theFile.yml; line: 3, column: 10] (through reference chain: foo.bar.PluginFile["commands"]->java.util.LinkedHashMap["dev"])

Which seems to be saying that Jackson cannot determine the concrete instance of the Command to set on pluginFile.commands(). However, that type is defined and created by the yml file itself with the !foo.bar.DevCommand {} syntax.

I can get this working by using the following yaml:

---
  commands:
    dev: 
      type: foo.bar.Baz

And then annotating the map property:

@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="type")

But I really want to use the yaml Java literal syntax.

Upvotes: 0

Views: 711

Answers (1)

StaxMan
StaxMan

Reputation: 116582

Base class Command would need to have @JsonTypeInfo to indicate it requires polymorphic handling; and if so, Jackson would know to look for Type Identifier that YAML content has. If foo.bar.Baz specifies a class (that is, package foo.bar, class Baz), type inclusion should be specified as "class name".

Or, alternatively, you can specify @JsonTypeInfo for the map property as well. Something like:

public class PluginFile {
  @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS)
  private Map<String, Command> commands = new HashMap<String, Command>();
}

Upvotes: 1

Related Questions