I'm writing a SpringBoot application where I'm creating a REST API from a fairly complex object model that was created in UML. The guys who created the UML defined several classes that inherit from other classes and in some cases override a field in the parent class. I'm having trouble deserializing the JSON describing these objects, and I'm unable to find clear advice about how to proceed. Since Spring Boot is using Jackson to deserialize JSON, I came here.
The issue is I have a class, Nameplate, that describes attributes about a Device. There are several subclasses of Device, and for some Device's there are subclasses of Nameplate being used.
Nameplate
@Embeddable
public abstract class Nameplate {
@EmbeddedId
@GeneratedValue
@JsonProperty("nameplateID")
private Long nameplateID = null;
@JsonProperty("characteristics")
// @OneToMany(cascade = CascadeType.ALL)
// @JoinColumn(name = "nameplateID", nullable = true)
@ElementCollection
List<Characteristics> characteristics = null;
... getters/setters ommitted
}
DeviceNameplate
@Embeddable
@ApiModel(parent=Nameplate.class)
public class DeviceNameplate extends Nameplate {
}
InverterNameplate
@Embeddable
public class InverterNameplate extends DeviceNameplate {
.... some other fields
@JsonProperty("characteristics")
// @OneToMany(cascade = CascadeType.ALL)
// @JoinColumn(name = "inverterID", nullable = true)
@ElementCollection
private List<InverterCharacteristics> characteristics = null;
... getters/setters ommitted
}
Then, the Device class has a field nameplate in the UML but I ran into problems defining the classes as so:
@MappedSuperclass
public abstract class Device {
... other fields
@JsonProperty("nameplate")
@Embedded
private DeviceNameplate nameplate = null;
... other fields
... getters/setters ommitted
}
@Entity
@AttributeOverride(name = "nameplate", column = @Column(name="nameplate")) // , column = @Column(name = "nameplate"))
public class Inverter extends Device {
@JsonProperty("nameplate")
@Embedded
private InverterNameplate nameplate = null;
... other fields
... getters/setters ommitted
}
If Device has its DeviceNameplate field declared, then there are problems deserializing the JSON. The JSON for the Inverter object needs to be interpreted such that the nameplate field is interpreted as InverterNameplate. But the wrong thing happened. The nameplate field ended up as null. As a workaround, I've commented-out the nameplate field in Device and am explicitly declaring the correct nameplate field in each Device subclass.
The current version of the problem has to do with the characteristics field in the Nameplate and InverterNameplate classes. It's clearly a variation of the same problem.
In both cases there is a subclass with an overridden field. When deserializing JSON for the subclass I need it to deserialize into the correct type of the overridden field.
I'm getting an exception:
nested exception is org.hibernate.property.access.spi.PropertyAccessException: Error accessing field [private java.lang.String com.amzur.orangebuttonapi.model.InverterCharacteristics.disconnectionType] by reflection for persistent property [com.amzur.orangebuttonapi.model.InverterCharacteristics#disconnectionType] : Characteristics [characteristicsID=, testConditions=[class TestCondition {
... fields of TestCondition class
}]
This occurs when I post to my /inverters endpoint, the POST operation is declared in annotations to receive an Inverter object, and the Inverter object includes the InverterNameplate and other stuff shown above.
Another exception tells what I think is the core issue:
java.lang.IllegalArgumentException: Can not set java.lang.String field com.amzur.orangebuttonapi.model.InverterCharacteristics.disconnectionType to com.amzur.orangebuttonapi.model.Characteristics
The InverterCharacteristics class has a disconnectionType field and a bunch of other fields, and is a subclass of Characteristics. If you refer above, Nameplate has "List<Characteristics> characteristics" while InverterNameplate has "List<InverterCharacteristics> characteristics". Hence, the field with the same name is overridden in InverterNameplate.
The JSON has:
"nameplate": {
"characteristics": [ {
"disconnectionType": "string",
"maxCurrentAC": 0,
"maximumInputCurrentPerMPPT": 0,
"maxPowerAC": 0,
"maximumPowerDC": 0,
"maximumShortCircuitCurrentPerMPPT": 0,
"maximumVoltageDC": 0,
.... some fields ommitted
} ],
"efficiencyCEC": 90,
"efficiencyEURO": 90,
"maximumEfficiency": 90,
"nightConsumption": 10,
"topology": "transformer"
},
That is, the Characteristics JSON supplied here matches InverterCharacteristics. But for some reason it (Hibernate) is trying to assign fields into a Characteristics object instead of an InverterCharacteristics object.
Hurm... maybe since it is Hibernate doing this assignment, I need to look to Hibernate for a solution. I'm not certain. In the Hibernate documentation on subclassing objects it does not discuss what to do when you override a field (or what they call an attribute) in the subclassed object.
+ David Herron