Dynamic JSON Filtering in Spring Boot using MappingJacksonValue

The article explains how Spring Boot’s MappingJacksonValue wrapper enables flexible JSON responses by allowing dynamic field selection, view‑based filtering with @JsonView, runtime filtering with @JsonFilter, and legacy JSONP support, while also noting its deprecation in newer Spring versions.

Senior Xiao Ying
Senior Xiao Ying
Senior Xiao Ying
Dynamic JSON Filtering in Spring Boot using MappingJacksonValue

1. What is MappingJacksonValue?

MappingJacksonValue is a wrapper class introduced in Spring Framework 4.1, located in org.springframework.http.converter.json. Its core purpose is to carry additional serialization instructions during JSON serialization.

Analogy: it works like a “delivery box” that holds the data to return and a “handling note” indicating which fields to show or how to wrap the output.

User user = new User("张三", "123456", "[email protected]");
MappingJacksonValue wrapper = new MappingJacksonValue(user);
return wrapper; // Spring will unwrap and process automatically

Why is it needed?

In standard Spring MVC, @ResponseBody or @RestController uses HttpMessageConverter to serialize the return value directly to JSON. When dynamic control over serialization is required—such as returning different fields based on user permissions, supporting JSONP, or filtering sensitive fields—custom HttpMessageConverter or AOP is needed, which is complex. MappingJacksonValue provides a declarative solution.

2. Core Principle

2.1 Workflow

Controller returns → MappingJacksonValue → HttpMessageConverter detects → extracts instructions → performs serialization

When MappingJackson2HttpMessageConverter encounters a MappingJacksonValue object, it does not serialize the wrapper itself. Instead it:

Calls getValue() to obtain the actual data object.

Calls getSerializationView() to obtain a view configuration (if any).

Calls getFilters() to obtain filter configuration (if any).

Serializes the data object using the obtained configurations.

3. Practical Scenario 1 – JSON View Dynamic Field Control

3.1 Theory

Jackson’s JSON View lets you define view interfaces and annotate fields/getters with @JsonView. During serialization, only fields belonging to the selected view (or its parent) are included.

3.2 Code Example

Scenario: a user‑info API where normal users see only id and name, while administrators see all fields including email and role.

Step 1: Define view interfaces

public class Views {
    // Public view: visible to all users
    public interface Public {}
    // Internal view: visible only to admins, extends Public
    public interface Internal extends Public {}
}

Step 2: Annotate the entity with @JsonView

public class User {
    private Long id;
    private String name;
    private String email;
    private String role;

    @JsonView(Views.Public.class)
    public Long getId() { return id; }

    @JsonView(Views.Public.class)
    public String getName() { return name; }

    @JsonView(Views.Internal.class)
    public String getEmail() { return email; }

    @JsonView(Views.Internal.class)
    public String getRole() { return role; }
    // getters/setters omitted
}

Step 3: Use MappingJacksonValue in the controller

@RestController
@RequestMapping("/api/users")
public class UserController {

    @GetMapping("/{id}")
    public MappingJacksonValue getUser(@PathVariable Long id,
                                       @RequestParam(required = false) String role) {
        // Simulate DB query
        User user = new User();
        user.setId(1L);
        user.setName("张三");
        user.setEmail("[email protected]");
        user.setRole("ADMIN");

        MappingJacksonValue wrapper = new MappingJacksonValue(user);
        if ("admin".equalsIgnoreCase(role)) {
            wrapper.setSerializationView(Views.Internal.class);
        } else {
            wrapper.setSerializationView(Views.Public.class);
        }
        return wrapper;
    }
}

Test results

# Normal user request
GET /api/users/1?role=user
Response: {"id":1,"name":"张三"}

# Admin request
GET /api/users/1?role=admin
Response: {"id":1,"name":"张三","email":"[email protected]","role":"ADMIN"}

4. Dynamic Field Filtering with @JsonFilter

@JsonFilter allows runtime specification of which fields to include or exclude, offering more flexibility than JSON View.

Code Example

Scenario: an API that lets the client request specific fields via a ?fields=name,email parameter.

Step 1: Define a filter name

public class DynamicFilter {
    public static final String FILTER_NAME = "dynamicFieldFilter";
}

Step 2: Annotate the entity with @JsonFilter

@JsonFilter(DynamicFilter.FILTER_NAME)
public class Product {
    private Long id;
    private String name;
    private BigDecimal price;
    private String description;
    private String secretCode; // sensitive field
    // getters/setters omitted
}

Step 3: Build the filter dynamically in the controller

@RestController
@RequestMapping("/api/products")
public class ProductController {

    @GetMapping("/{id}")
    public MappingJacksonValue getProduct(@PathVariable Long id,
                                          @RequestParam(required = false) String fields) {
        Product product = new Product();
        product.setId(id);
        product.setName("机械键盘");
        product.setPrice(new BigDecimal("399.00"));
        product.setDescription("RGB背光,机械轴体");
        product.setSecretCode("INTERNAL-001");

        MappingJacksonValue wrapper = new MappingJacksonValue(product);
        if (fields != null && !fields.isEmpty()) {
            String[] fieldNames = fields.split(",");
            PropertyFilter filter = SimpleBeanPropertyFilter.filterOutAllExcept(fieldNames);
            FilterProvider filters = new SimpleFilterProvider()
                    .addFilter(DynamicFilter.FILTER_NAME, filter);
            wrapper.setFilters(filters);
        } else {
            PropertyFilter filter = SimpleBeanPropertyFilter.serializeAllExcept("secretCode");
            FilterProvider filters = new SimpleFilterProvider()
                    .addFilter(DynamicFilter.FILTER_NAME, filter);
            wrapper.setFilters(filters);
        }
        return wrapper;
    }
}

Test results

# Request all fields (secretCode excluded automatically)
GET /api/products/1
Response: {"id":1,"name":"机械键盘","price":399,"description":"RGB背光,机械轴体"}

# Request specific fields
GET /api/products/1?fields=name,price
Response: {"name":"机械键盘","price":399}

5. JSONP Cross‑Origin Support (Legacy Approach)

Warning: JSONP carries XSS risks and only works with GET requests; modern projects should prefer CORS. The following code is intended for maintaining legacy systems.

Code Example

@RestController
@RequestMapping("/api")
public class JsonpController {

    @GetMapping("/data")
    public MappingJacksonValue getData(@RequestParam(required = false) String callback) {
        Map<String, String> data = new HashMap<>();
        data.put("message", "Hello World");
        data.put("status", "success");

        MappingJacksonValue wrapper = new MappingJacksonValue(data);
        if (callback != null && !callback.isEmpty()) {
            wrapper.setJsonpFunction(callback); // Spring 4.x support
        }
        return wrapper;
    }
}

Client call

<script>
function handleResponse(data) {
    console.log(data); // {message: "Hello World", status: "success"}
}
var script = document.createElement('script');
script.src = 'http://api.example.com/api/data?callback=handleResponse';
document.head.appendChild(script);
</script>

6. Deprecation and Performance Notes

According to Spring 7.0 Javadoc, MappingJacksonValue is marked “Deprecated for removal” and its functionality is superseded by the hints mechanism of SmartHttpMessageConverter. Projects on Spring 6.x or newer should consider migrating.

Spring 5.x removed native JSONP support; to retain it you must implement it yourself or use AbstractJsonpResponseBodyAdvice, which is also deprecated.

Creating a new FilterProvider on every request incurs overhead. For high‑concurrency scenarios, caching commonly used filter configurations is advisable.

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.

Spring BootJSONP@JsonViewJsonFilterJSON filteringMappingJacksonValue
Senior Xiao Ying
Written by

Senior Xiao Ying

Dedicated to sharing Java backend technical experience and original tutorials, offering career transition advice and resume editing. Recognized as a rising star in CSDN's Java backend community and ranked Top 3 in the 2022 New Star Program for Java backend.

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.