I have a REST API with an observation resource with some attributes as observationType and observationValue. Some valid values for observationType are: HEART_RATE, BLOOD_PRESSURE, ORTHOSTATIC_BLOOD_PRESSURE.
Based on the observationType, the observationValue field might hold a different data type, an integer for HEART_RATE, a double for BODY_WEIGHT and for BLOOD_PRESSURE we would need something that allows to show the value for the systolic and diastolic blood pressure, something like this:
{
"observationType": "HEART_RATE"
"observationValue": 90
}
{
"observationType": "BODY_WEIGHT"
"observationValue": 81.5
}
{
"observationType": "BLOOD_PRESSURE"
"observationValue": {"systolicBloodPressureValue": 120, "diastolicBloodPressureValue": 80}
}
What might be a good approach to model this in a REST API?, I don't expect for the best option to implement it, but usually how do we implement it? What are some of the patterns in the industry?
Should I just return a String value for observationValue?
It looks like having polymorphism in a REST API would be some kind of a mess.
CodePudding user response:
One way to achieve this is to use Jackson's polymorphic type support. Model your data as:
public interface Observation<T> {
T getObservationValue();
}
And implement it e.g. as:
public class HeartRateObservation implements Observation<Integer> {
private Integer observationValue;
public void setObservationValue(Integer value) {
this.observationValue = value;
}
@Override
public Integer getObservationValue() {
return observationValue;
}
}
public class BodyWeightObservation implements Observation<Float> {
// ...
}
public class BloodPressureValue {
private float systolicBloodPressureValue;
private float diastolicBloodPressureValue;
// other members, getters and setters
}
public class BloodPressureObservation implements Observation<BloodPressureValue> {
// ...
}
Now comes the Jackson-specific part: You have to instruct Jackson to (de-)serialize your classes with type info. Amend the previous model as follows:
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME, // (1)
include = JsonTypeInfo.As.PROPERTY, // (2)
property = "observationType" // (3)
)
public interface Observation<T> { ... }
And add the name to the implementations:
@JsonTypeName("HEART_RATE") // (4)
public class HeartRateObservation implements Observation<Integer> { ... }
@JsonTypeName("BODY_WEIGHT")
public class BodyWeightObservation implements Observation<Float> { ... }
@JsonTypeName("BLOOD_PRESSURE")
public class BloodPressureObservation implements Observation<BloodPressureValue> { ... }
The configuration can be interpreted as:
- Use a custom name to identify the exact subtype...
- ...putting it in a property of the object...
- ...(the property is) called
observationType
- ...and for type
HeartRateObservation
has the value"HEART_RATE"
DISCLAIMER 1: I have not run the code, it may need some tweaking, let me know if you encounter problems.
DISCLAIMER 2: This is one way of doing it with Jackson - admittedly I do not know if you are using Jackson. There are other valid solutions too.