json – @IntDef Android support annontation with Jackson deserializing-ThrowExceptions

Exception or error:

Using JacksonAnnotations along with Android Support Annotations. My POJO is:

@JsonIgnoreProperties(ignoreUnknown = true)
public class Schedule {
    public static final int SUNDAY = 0;
    public static final int MONDAY = 1;
    public static final int TUESDAY = 2;
    public static final int WEDNESDAY = 3;
    public static final int THURSDAY = 4;
    public static final int FRIDAY = 5;
    public static final int SATURDAY = 6;

    private Integer weekday;

    public Schedule() {
    }

    @Weekday
    public Integer getWeekday() {
        return weekday;
    }

    public void setWeekday(@Weekday Integer weekday) {
        this.weekday = weekday;
    }

    @Retention(RetentionPolicy.RUNTIME)
    @IntDef({SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY})
    public @interface Weekday {}
}

From backed i get object:

{"schedule":{"weekday":"MONDAY"}}

What i want is to map Weekday to it’s integer value defined in constants. Is there any way i can achieve this?

Update: The main purpose is optimization (You should strictly avoid using enums on Android like it said here).

How to solve:

Since you can count on weekdays not growing out of control, creating a static map seems like a pretty clean solution. Providing a @JsonCreator factory named fromString will work with Jackson and will also play nicely with JAX-RS:

public class Schedule {
    ...
    private static final ImmutableMap<String, Schedule> WEEKDAY_TO_INT_MAP =
            ImmutableMap.<String, Schedule>builder()
                .put("SUNDAY", withIntWeekday(0))
                .put("MONDAY", withIntWeekday(1))
                .put("TUESDAY", withIntWeekday(2))
                .put("WEDNESDAY", withIntWeekday(3))
                .put("THURSDAY", withIntWeekday(4))
                .put("FRIDAY", withIntWeekday(5))
                .put("SATURDAY", withIntWeekday(6))
                .build();

    public static Schedule withIntWeekday(int weekdayInt) {
        Schedule schedule = new Schedule();
        schedule.setWeekday(weekdayInt);
        return schedule;
    }

    @JsonCreator
    public static Schedule fromString(String weekday) {
        return WEEKDAY_TO_INT_MAP.get(weekday);
    }
    ...
}

###

Maybe I am missing something, but why not do something like this:

public class Schedule {
    public enum Weekday {
        SUNDAY(0),
        MONDAY(1),
        TUESDAY(2),
        WEDNESDAY(3),
        THURSDAY(4),
        FRIDAY(5),
        SATURDAY(6);

        private final Integer weekday;

        Weekday(Integer weekday) {
            this.weekday = weekday;
        }

        public Integer getWeekday() {
            return weekday;
        }
    }

    private Weekday weekday;

    public Integer getWeekday() {
        return weekday.getWeekday();
    }

    public void setWeekday(Weekday weekday) {
        this.weekday = weekday;
    }

}

###

If the backend is returning "MONDAY" as a string then you probably should not be using @IntDef because you will have to manage converting "MONDAY" into an integer value.

I would try using the @StringDef annotation instead that way you won’t have to worry about any sort of conversion between the backend and the client app. The only catch is you need to have reliable responses from your backend and agree upon the naming conventions ahead of time.

If you change the type from @IntDef to @StringDef it would look like this:

@JsonIgnoreProperties(ignoreUnknown = true)
public class Schedule {
    public static final String SUNDAY = "SUNDAY";
    public static final String MONDAY = "MONDAY";
    public static final String TUESDAY = "TUESDAY";
    public static final String WEDNESDAY = "WEDNESDAY";
    public static final String THURSDAY = "THURSDAY";
    public static final String FRIDAY = "FRIDAY";
    public static final String SATURDAY = "SATURDAY";

    private String weekday;

    public Schedule() {
    }

    @Weekday
    public String getWeekday() {
        return weekday;
    }

    public void setWeekday(@Weekday String weekday) {
        this.weekday = weekday;
    }

    @Retention(RetentionPolicy.RUNTIME)
    @StringDef({SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY})
    public @interface Weekday {}
}

###

The elegant solution is using an Enum.

The efficient solution is writing a custom serializer that maps a week day string to an int and vice versa using switch statements.

You could also access the values in the StringDef annotation using reflection in your serializer, but that will be slower and less readable.

Leave a Reply

Your email address will not be published. Required fields are marked *