java - 带 Spring Boot 的 Jackson : control object substitution for specific method

标签 java spring-boot serialization jackson

我将实体类型作为递归树中的项目,因此任何项目都引用其父级和子级(相同类型)

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 标记的评论之后更新了答案。 */

您正在寻找的结果仍然可以通过使用 @JsonViewCategory 对象内的较小解决方法来实现。

假设我们有两种类型用作标记来输出 JSON View FlatViewHierarchicalView

解决方案的原理是:

  • 我们将大多数字段与 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/

相关文章:

java - 写入同一个 log4j 日志文件的不同应用程序

java - 针对未经身份验证的用户的 Spring Security 404 页面

javascript - 通过服务器端渲染对 DataTable 进行排序

java - @ConditionalOnProperty 用于多值属性

java - Spring Boot 在从 application.yml 注入(inject) Map 时不包含特殊字符

java - 通过java套接字发送对象

java - 如何将一个存储为java对象的对象的变量传递给另一个也存储为java对象的对象的函数?

android - 数据结构的最快序列化/反序列化模式

java - 序列化和反序列化未实现 java.io.Serializable 的对象

java - 如何在 ActiveJDBC 中锁定用户记录?或者当你使用Activejdbc事务时它会自动锁定吗?