5

Ok, I know there are a bunch of similar questions, but nothing seems to work.

I have the following structure set up for my entities.

public abstract class MyAbstractClass {
   // bunch of properties, getters, and setters that subclasses share

   public abstract String getType();
}

public class MySubclass1 extends MyAbstractClass {
    //  a few unique properties, getters, and setters

    public String getType() {
        return "Type_1"; //always the same for each instance of MySubclass1
    }
}

public class MySubclass2 extends MyAbstractClass {
    //  a few unique properties, getters, and setters

    public String getType() {
        return "Type_2"; //always the same for each instance of MySubclass2
    }
}

In my controller, I try to map a request to the following method.

public @RequestBody MyAbstractClass saveObject(@RequestBody MyAbstractClass mac) {
    // call model to save object
}

I would like to use 1 controller method versus separate ones for the 2 entities. But using the above results in the following.

com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of path.to.my.entity.MyAbstractClass, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information

Makes sense.

TRY 1

@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="implementingClass")
public abstract class MyAbstractClass

What I think it does - adds a metadata implementingClass property that will store the subclass class.

What the result is.

Unexpected token (END_OBJECT), expected FIELD_NAME: missing property 'implementingClass' that is to contain type id  (for class path.to.my.entity.MyAbstractClass)

Tried with "class" instead of "implementingClass" for the property and got similar results.

TRY 2

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.WRAPPER_OBJECT)
@JsonSubTypes({
    @Type(name="MySubclass1", value=MySubclass1.class),
    @Type(name="MySubclass2", value=MySubclass2.class)
})
public abstract class MyAbstractClass

What I think it does - uses the defined name to do some sort of wrapping thing.

What the result is.

Could not resolve type id 'myUuid' into a subtype of [simple type, class path.to.my.entity.MyAbstractClass]

Same results even when adding @JsonTypeName("MySubclass1") and @JsonTypeName("MySubclass2") to the 2 subclasses.

Other Tries

I tried a lot. Nothing works. Won't include everything here.

I feel like there should be a simple way to do this, but I just keep on configuring things incorrectly.

I feel like the getType could maybe be leveraged, but I don't want to add an actual property for type (it's just a helper method). Also I would like to do this with annotations versus other options.

Thank you.

Snowy Coder Girl
  • 5,408
  • 10
  • 41
  • 72
  • Can you give an example of the JSON you're submitting to the controller? For your `TRY 1`, you're telling it to add "implementingClass" to the serialization, and to look for an "implementingClass" property on deserialize, but you haven't specified it. [See also](http://stackoverflow.com/questions/14222429/jackson-deserializing-nested-polymorphic-type) – xathien Oct 14 '15 at 21:02
  • Ok, well that answers something. I do not want to have to implement the implementingClass for the JSON. So maybe Try 1 is not what I want to do. Ty – Snowy Coder Girl Oct 14 '15 at 21:18
  • So this maybe comes down to whether the `getType` can be leveraged. Which I am thinking it cannot w/o being an actual property. – Snowy Coder Girl Oct 14 '15 at 21:39
  • How do you want to leverage `getType`? Is it a property in the JSON? If so, just change the "implementingClass" to "type", and use the `@JsonSubTypes` (like in `TRY 2`) with e.g. `@Type(name="Type_1", value=MySubclass1.class)`. Again, much easier if we knew what the JSON looked like. – xathien Oct 15 '15 at 15:06

1 Answers1

10

I figured it out but I guess I'll answer in case anyone else has this problem.

I added a type property to my subclasses instead of just a helper method (one example included below).

public class MySubclass1 extends MyAbstractClass {
    @Transient
    private final String type = "TYPE_1";

    public String getType() {
        return type;
    }
}

Then I did the following for my abstract superclass.

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
@JsonSubTypes({
    @Type(name="TYPE_1", value=MySubclass1.class),
    @Type(name="TYPE_2", value=MySubclass2.class)
})
public abstract class MyAbstractClass

When providing the JSON, I was sure to include the type. I won't include this because it's weird knockout insanity.

It's not great. But it worked.

Snowy Coder Girl
  • 5,408
  • 10
  • 41
  • 72