Master Polymorphic Deserialization in Spring Boot 3 with Jackson

This tutorial shows how to use Jackson's polymorphic deserialization in Spring Boot 3 to handle variable JSON request bodies by defining a common Order interface, concrete implementations, and a REST controller that routes calculations based on the order type, eliminating redundant endpoints.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Master Polymorphic Deserialization in Spring Boot 3 with Jackson

Introduction

When the JSON body format is unknown, defining a separate API for each possible structure leads to redundant endpoints. Polymorphic deserialization with Jackson provides a concise and extensible solution.

Case Study

Assume an online store needs to handle three order types: regular, discount, and promo, each with different fields and calculation logic.

1. Interface Definition

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "orderType", visible = true)
@JsonSubTypes({
    @JsonSubTypes.Type(value = RegularOrder.class, name = "regular"),
    @JsonSubTypes.Type(value = DiscountOrder.class, name = "discount"),
    @JsonSubTypes.Type(value = PromoOrder.class, name = "promo")
})
public interface Order {
}

2. POJO Implementations

public class DiscountOrder implements Order {
    private double price;
    private int quantity;
    private double discountRate;
}
public class PromoOrder implements Order {
    private double price;
    private int quantity;
    private String promoCode;
}
public class RegularOrder implements Order {
    private double price;
    private int quantity;
}

3. Controller

@RestController
@RequestMapping("/orders")
public class OrderController {
    private final OrderService orderService;
    public OrderController(OrderService orderService) {
        this.orderService = orderService;
    }
    @PostMapping("/calculate")
    public ResponseEntity<Double> calculateTotal(@RequestBody Order order) {
        double total = 0;
        switch (order) {
            case RegularOrder r -> total = orderService.calculateRegularTotal(r);
            case DiscountOrder d -> total = orderService.calculateDiscountTotal(d);
            case PromoOrder p -> total = orderService.calculatePromoTotal(p);
            default -> logger.error("Unknown order type: {}", order);
        }
        return ResponseEntity.ok(total);
    }
}

4. Service

@Service
public class OrderService {
    public double calculateRegularTotal(RegularOrder order) {
        return order.getPrice() * order.getQuantity();
    }
    public double calculateDiscountTotal(DiscountOrder order) {
        return order.getPrice() * order.getQuantity() * (1 - order.getDiscountRate());
    }
    public double calculatePromoTotal(PromoOrder order) {
        return order.getPrice() * order.getQuantity() * 0.75;
    }
}

5. Alternative: Dynamic Field Types

By annotating a field with @JsonTypeInfo and @JsonSubTypes, the field can hold different Java types based on a "type" property in the JSON.

public class Order {
    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type")
    @JsonSubTypes({
        @JsonSubTypes.Type(value = String.class, name = "string"),
        @JsonSubTypes.Type(value = Long.class, name = "long"),
        @JsonSubTypes.Type(value = Integer.class, name = "int")
    })
    protected Object value;
}

When the request contains "type":"long", the value field is deserialized as a Long, etc.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendjavaSpring BootREST APIJacksonPolymorphic Deserialization
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.