Home > database >  Polymorphism in a REST API - different data type in response based on another fields's value
Polymorphism in a REST API - different data type in response based on another fields's value

Time:12-03

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:

  1. Use a custom name to identify the exact subtype...
  2. ...putting it in a property of the object...
  3. ...(the property is) called observationType
  4. ...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.

  • Related