Wednesday, 7 August 2019

Best practices while parsing downstream response with enum value

Enums are always been the contract between the API (service) and its client(s). But if at all it is required to enhance the enum (add a new value) from API side, it is almost impossible to inform about the change to all its client(s). At the end, if the client implementation is not backward compatible it fails to parse the API response.

Example – 1:
Here when we try to parse API response with a new enum value it ends up with below exception.
com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `Status` from String "INTERMEDIATE": value not one of declared Enum instance names: [START, END] at [Source: (String)"{"status": "INTERMEDIATE"}"; line: 1, column: 12] (through reference chain: Resp["status"])

We can avoid such situation by making our client code backward compatible by following few best practices:

Method - I : By replacing the invalid value by some default enum constant
Always define a default enum value like EMPTY or INVALID or DEFAULT or UNKNOWN or _ etc. based on the best suitable
Test Cases:

Method - II : By setting the below deserialization feature to ObjetMapper object
Test Case:

Pros N Cons of the above two methods

  • Method - I gives you a default enum constant which is NULL safe and you can easily ignore this through out your code.
  • Method - II gives you a NULL which prone to NullPointerException


Tuesday, 16 July 2019

Toggleable Enum in Java

I came across this below code in my code base where we just want to toggle the enum value. If it is DEBIT return CREDIT, if CREDIT return DEBIT like below.

enum DebitCreditCode implements Toggleable<DebitCreditCode> {
   DEBIT,
   CREDIT
public static DebitCreditCode getReverseOfPaymentDebitCreditCode(DebitCreditCode
                                                                     debitOrCreditFromSummary) {
   if (debitOrCreditFromSummary != null && debitOrCreditFromSummary
                                                 .equals(DebitCreditCode.CREDIT)) {
      return DebitCreditCode.DEBIT;
   } else if (debitOrCreditFromSummary != null && debitOrCreditFromSummary
                                                        .equals(DebitCreditCode.DEBIT)) {
      return DebitCreditCode.CREDIT;
   }
   return null;
}

This method is excellent and works as expected.
 
But…
If we want such requirement for any other enum,
  • we have to define similar utility method for each enum and it is a repetitive job
  • we will be landed up with duplicate code
So, to get rid of this, I have designed the below solution through which we can achieve the same without writing new method every time.

Step – 1Define this below interface in your common module. This is a one time job.

interface Toggleable<T extends Enum<T>> {
   default T toggle() {
      final T[] enumConstants = (T[]) this.getClass().getEnumConstants();
      if (enumConstants == null) {
         throw new UnsupportedOperationException("Only enum can support toggling");
      }
      if (enumConstants.length != 2) {
         throw new UnsupportedOperationException("Enum must have 2 objects to support toggling");
      }
      return enumConstants[0] == this ? enumConstants[1] : enumConstants[0];
   }
}

Step – 2Just implement the above interface to your enum class where you want this 
toggle/reverse feature like below :

enum DebitCreditCode implements Toggleable<DebitCreditCode> {
   DEBIT,
   CREDIT
}

Step – 3 : Call method toggle() on the enum object like

final DebitCreditCode toggled = DebitCreditCode.DEBIT.toggle();
Assert.assertEquals(DebitCreditCode.CREDIT, toggled);
final DebitCreditCode original = toggled.toggle();
Assert.assertEquals(DebitCreditCode.DEBIT, original);

Find complete code here