Mastering Partial Updates in Spring Boot 3: @JsonMerge & @JsonSetter Explained

This article demonstrates how to implement elegant partial updates in Spring Boot 3 by combining @JsonMerge and @JsonSetter(nulls = Nulls.SKIP), addressing null‑value handling, reducing boilerplate code, and ensuring nested objects and maps are merged correctly.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering Partial Updates in Spring Boot 3: @JsonMerge & @JsonSetter Explained

1. Introduction

In RESTful API design, the PATCH method is used for partial updates, sending only the fields that need to change while leaving others untouched. Implementing partial updates in Spring Boot often raises the question of how to handle null values correctly.

The main challenge is distinguishing between a client‑sent null (intending to clear a field) and an omitted field (which should be ignored).

2. Practical Example

2.1 Prepare Environment

Environment: Spring Boot 3.4.2

public class User {
  private String name;
  private Integer age;
  private Address address;
  private Map<String, Object> extras;
}
public class Address {
  private String province;
  private String city;
  private String county;
  private String street;
}
@RestController
@RequestMapping("/merge/users")
public class MergeController {
  private User user;
  public MergeController() {
    User user = new User();
    user.setName("张三");
    user.setAge(30);
    Address address = new Address();
    address.setProvince("新疆维吾尔自治区");
    address.setCity("乌鲁木齐市");
    address.setCounty("天山区");
    address.setStreet("卫星路");
    user.setAddress(address);
    user.setExtras(new HashMap<>(Map.of("hobby","coding","gender","female")));
    this.user = user;
  }
  @GetMapping
  public ResponseEntity<User> getUser() {
    return ResponseEntity.ok(this.user);
  }
}

Access GET /merge/users to retrieve the initial object.

GET response
GET response

2.2 Traditional Update Method

Using @PutMapping and manually checking each field for null leads to verbose and error‑prone code:

@PutMapping
public ResponseEntity<User> updateUser(@RequestBody User updatedUser) {
  if (updatedUser.getName() != null) {
    user.setName(updatedUser.getName());
  }
  if (updatedUser.getAge() != null) {
    user.setAge(updatedUser.getAge());
  }
  // other fields...
  return ResponseEntity.ok(user);
}

Problems:

Lengthy code and easy mistakes, especially with nested objects.

High maintenance when the model changes.

Ambiguous null semantics – a null cannot be used to explicitly clear a field.

Violates DRY principle.

2.3 Using @JsonMerge

Annotating fields with @JsonMerge allows Jackson to merge incoming JSON into the existing object:

public class User {
  @JsonMerge
  private Address address;
  @JsonMerge
  private Map<String, Object> extras;
}

Controller method:

private final ObjectMapper objectMapper;
private User user;
@PutMapping
public ResponseEntity<User> updateUser(@RequestBody User updatedUser) throws Exception {
  User mergedUser = objectMapper.readerForUpdating(this.user)
                               .readValue(objectMapper.writeValueAsString(updatedUser));
  this.user = mergedUser;
  return ResponseEntity.ok(this.user);
}

Access PUT /merge/users to apply the merge.

PUT merge response
PUT merge response

2.4 Using @JsonSetter + @JsonMerge

Adding @JsonSetter(nulls = Nulls.SKIP) to primitive fields tells Jackson to ignore null values during merge, while @JsonMerge still merges collections and nested objects:

public class User {
  @JsonSetter(nulls = Nulls.SKIP)
  private String name;
  @JsonSetter(nulls = Nulls.SKIP)
  private Integer age;
  @JsonMerge
  private Address address;
  @JsonMerge
  private Map<String, Object> extras;
}
public class Address {
  @JsonSetter(nulls = Nulls.SKIP)
  private String province;
  @JsonSetter(nulls = Nulls.SKIP)
  private String city;
  @JsonSetter(nulls = Nulls.SKIP)
  private String county;
  @JsonSetter(nulls = Nulls.SKIP)
  private String street;
}

After updating via PUT /merge/users , null values are skipped, preventing unwanted overwrites.

PUT with JsonSetter response
PUT with JsonSetter response

Conclusion

Combining @JsonMerge with @JsonSetter(nulls = Nulls.SKIP) provides an elegant solution for partial updates in Spring Boot: nested objects and maps are merged, while nulls are ignored unless explicitly desired. Remember that without @JsonMerge on a field, a null will overwrite the existing value.

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.

JavaSpring BootREST APIPartial UpdateJsonMergeJsonSetter
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.