docs(blogs): add Jackson JSON flattening guide
- Add @JsonUnwrapped and @JsonAnyGetter usage examples with code - Include comparison table for choosing between annotations - Sync content across zh-hans and en-gb versions
This commit is contained in:
2 files changed
+144
No files matched your search
@@ -207,4 +207,75 @@ Specification<User> spec = (root, query, cb) -> {
|
|||||||
Page<User> page = userRepository.findAll(spec, pageable);
|
Page<User> page = userRepository.findAll(spec, pageable);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Flattening Objects or Maps into JSON with Jackson
|
||||||
|
|
||||||
|
When serialising to JSON, you sometimes need to "flatten" a nested object's properties into the parent JSON rather than having them appear as a nested field. Jackson provides two annotations for handling objects and `Map` flattening separately.
|
||||||
|
|
||||||
|
### `@JsonUnwrapped` — flattening objects with a known structure
|
||||||
|
|
||||||
|
`@JsonUnwrapped` expands the properties of a nested object directly into the parent JSON:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class Address {
|
||||||
|
private String street;
|
||||||
|
private String city;
|
||||||
|
// getters and setters
|
||||||
|
}
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@JsonUnwrapped
|
||||||
|
private Address address;
|
||||||
|
// getters and setters
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Serialised output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "John Doe",
|
||||||
|
"street": "Oxford Street",
|
||||||
|
"city": "London"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Without `@JsonUnwrapped`, `address` would appear as a nested JSON object: `"address": { "street": "...", "city": "..." }`.
|
||||||
|
|
||||||
|
### `@JsonAnyGetter` — flattening a Map with dynamic keys
|
||||||
|
|
||||||
|
`@JsonAnyGetter` flattens a `Map`'s key-value pairs directly into the parent JSON, ideal for dynamic or open-ended fields:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class Product {
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private Map<String, Object> attributes = new HashMap<>();
|
||||||
|
|
||||||
|
@JsonAnyGetter
|
||||||
|
public Map<String, Object> getAttributes() {
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
// other getters and setters
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
With `attributes` containing `{"colour": "red", "size": "XL"}`, the serialised output is:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "Example Product",
|
||||||
|
"colour": "red",
|
||||||
|
"size": "XL"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that `@JsonAnyGetter` must be placed on the **getter method**, not the field itself. You will also need `@JsonAnySetter` to support deserialisation.
|
||||||
|
|
||||||
|
### How to choose
|
||||||
|
|
||||||
|
| Scenario | Annotation |
|
||||||
|
|-------------------------------------------------------|--------------------|
|
||||||
|
| Flatten nested objects with fixed fields (e.g. address, config) | `@JsonUnwrapped` |
|
||||||
|
| Flatten dynamic key-value pairs with unknown keys | `@JsonAnyGetter` |
|
||||||
@@ -201,3 +201,76 @@ Specification<User> spec = (root, query, cb) -> {
|
|||||||
|
|
||||||
Page<User> page = userRepository.findAll(spec, pageable);
|
Page<User> page = userRepository.findAll(spec, pageable);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 使用 Jackson 将对象或 Map 平铺到 JSON 中
|
||||||
|
|
||||||
|
在序列化 JSON 时,有时需要将嵌套对象的属性"平铺"到父级 JSON 中,而不是作为一个嵌套字段出现。Jackson 提供了两个注解来分别处理对象和 `Map` 的平铺场景。
|
||||||
|
|
||||||
|
### `@JsonUnwrapped` — 平铺已知结构的对象
|
||||||
|
|
||||||
|
`@JsonUnwrapped` 会将一个嵌套对象的属性直接展开到父级 JSON 中:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class Address {
|
||||||
|
private String street;
|
||||||
|
private String city;
|
||||||
|
// getters and setters
|
||||||
|
}
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@JsonUnwrapped
|
||||||
|
private Address address;
|
||||||
|
// getters and setters
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
序列化结果:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "陳大文",
|
||||||
|
"street": "彌敦道",
|
||||||
|
"city": "香港"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
如果不使用 `@JsonUnwrapped`,`address` 会作为一个嵌套 JSON 对象出现:`"address": { "street": "...", "city": "..." }`。
|
||||||
|
|
||||||
|
### `@JsonAnyGetter` — 平铺键值不固定的 Map
|
||||||
|
|
||||||
|
`@JsonAnyGetter` 可以将 `Map` 中的键值对直接平铺到父级 JSON 中,适合 key 不固定或动态扩展的场景:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class Product {
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private Map<String, Object> attributes = new HashMap<>();
|
||||||
|
|
||||||
|
@JsonAnyGetter
|
||||||
|
public Map<String, Object> getAttributes() {
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
// other getters and setters
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
当 `attributes` 包含 `{"colour": "red", "size": "XL"}` 时,序列化结果:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "Example Product",
|
||||||
|
"colour": "red",
|
||||||
|
"size": "XL"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
注意 `@JsonAnyGetter` 需要加在 **getter 方法**上,而不是字段上;同时需要搭配 `@JsonAnySetter` 来支持反序列化。
|
||||||
|
|
||||||
|
### 如何选择
|
||||||
|
|
||||||
|
| 场景 | 注解 |
|
||||||
|
|----------------------------------------|--------------------|
|
||||||
|
| 平铺字段固定的嵌套对象(如地址、配置) | `@JsonUnwrapped` |
|
||||||
|
| 平铺 key 不固定的动态键值对 | `@JsonAnyGetter` |
|
||||||
Reference in new issue
Block a user