我将实体类型作为递归树中的项目,因此任何项目都引用其父级和子级(相同类型)
public class Category {
private Integer id;
private String displayName;
@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
@JsonIdentityReference(alwaysAsId=true)
private Category parent;
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
@JsonIdentityReference(alwaysAsId=true)
private Set<Category> children;
// constructors, getters and setters
}
如您所见,我使用 @JsonIdentityReference
注释标记了两个引用字段,强制它们呈现为纯 id。目前,使用此设置,实体呈现如下:
// from .../categories/0
{
"id" : 0,
"displayName" : "Root",
"parent" : null,
"children" : [ 1, 13, 17 ]
}
(非常好)。
但是,客户端非常常见的使用场景是从特定节点开始获取整个子树,在此实现中需要向服务器发出多个请求。我想创建另一个 enpoint,允许客户端通过单个请求执行此操作。
@RestController
@RequestMapping(value = "/categories")
public class CategoryController {
@Autowired
private CategoryService categoryService;
@Autowired
private CategoryRepository categoryRepo;
@RequestMapping(value = "/tree", method = RequestMethod.GET)
public Category getTree(@RequestParam(name = "root", required = false) Integer id) {
Category root = id == null ? categoryService.getRoot() : categoryRepo.findOne(id);
return categoryService.getTree(root);
}
// other endpoints
// getOne(id)
// getAll()
}
仅当我从 children
字段手动删除 (alwaysAsId=true)
标志时,来自此端点的响应才会呈现完整对象。但是,我希望两个端点共存并呈现不同的布局。所以,问题是:如何让特定的 Controller 方法选择是否将完整实体替换为 id?。
我已经尝试了与 @JsonView
的各种组合,但似乎这种方法不起作用。 @JsonView
只能控制是否包含或完全省略特定字段,而我需要 children
字段仅更改布局。另外,由于子类型与实体类型相同,因此无法在不同类之间拆分注释标记。
最佳答案
/* 在关于在多个级别层次结构中丢失 View 标记的评论之后更新了答案。 */
您正在寻找的结果仍然可以通过使用 @JsonView
和 Category
对象内的较小解决方法来实现。
假设我们有两种类型用作标记来输出 JSON View FlatView
和 HierarchicalView
。
解决方案的原理是:
我们将大多数字段与 View 相关联。
children
字段仅与FlatView
关联。我们为
children
字段创建一个额外的 getter,为其提供另一个属性名称,而不与该字段或其原始 getter 冲突。此属性仅与HierarchicalView
关联。
它为 Category
类提供以下布局:
public class Category {
@JsonView({ FlatView.class, HierarchicalView.class}) // both views
private Integer id;
@JsonView({ FlatView.class, HierarchicalView.class}) // both views
private String displayName;
@JsonView({ FlatView.class, HierarchicalView.class}) // both views
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
@JsonIdentityReference(alwaysAsId = true)
private Category parent;
@JsonView(FlatView.class) // flat view only!
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
@JsonIdentityReference(alwaysAsId = true)
private Set<Category> children;
@JsonView(HierarchicalView.class) // hierarchical view only!
// note that the name is not `getChildren`: this generates another JSON property.
// Or use @JsonProperty to customize it.
public Set<Category> getChildrenAsTree() {
return this.children;
}
// constructors, getters and setters
}
生成输出的 Controller 方法应使用相应的 @JsonView
进行注释。
缺点:
此方法不会对以不同方式表示的相同信息使用相同的属性名称。这可能会给客户端的属性匹配、反序列化带来一些困难。
如果
Category
的某个位置仍使用默认 View ,它将包含通过附加 getter 访问的字段和属性。冗余且重复的注释:)难以维护。
children
字段的冗余 getter。
关于java - 带 Spring Boot 的 Jackson : control object substitution for specific method,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49808805/