Spring Boot Restful API动态生成JSON:无需双对象的字段移除方案
如何在不创建父子对象的情况下修改Restful API的JSON输出?
问题描述
我正在开发Restful API服务,需要修改输出JSON,移除部分字段:
当前输出JSON
{ "id": "10001", "name": "math", "family": "mac", "code": "1", "subNotes": [ { "id": null, "name": "john", "family": null, "code": "1-1", "subNotes": null }, { "id": null, "name": "cris", "family": null, "code": "1-2", "subNotes": null }, { "id": null, "name": "eli", "family": null, "code": "1-3", "subNotes": null } ] }
目标输出JSON
{ "id": "10001", "name": "math", "family": "mac", "code": "1", "subNotes": [ { "name": "john", "code": "1-1" }, { "name": "cris", "code": "1-2" }, { "name": "eli", "code": "1-3" } ] }
请问能否无需创建父、子两个对象实现该修改?最优解决方案是什么?
解决方案
完全可以不用创建新的父子对象,以下是几种实用方案,按场景推荐:
1. 序列化注解(静态过滤,适合固定需求)
如果你的API用Jackson作为JSON序列化工具(Spring Boot默认),直接在实体类的目标字段上添加注解即可:
@JsonIgnore:直接标记要移除的字段(比如子对象的
id、family、subNotes),序列化时会自动忽略这些字段。public class SubNote { @JsonIgnore private String id; private String name; @JsonIgnore private String family; private String code; @JsonIgnore private List<SubNote> subNotes; // getter/setter }优点:实现简单,无额外代码;缺点:如果该实体类被其他接口复用,会影响其他接口的输出。
@JsonView:定义视图接口,给需要输出的字段标记视图,接口方法上指定使用的视图,实现按需输出。
// 定义视图接口 public interface ApiView { interface Basic {} interface Detailed extends Basic {} } // 父实体类 public class ParentNote { @JsonView(ApiView.Basic.class) private String id; @JsonView(ApiView.Basic.class) private String name; @JsonView(ApiView.Basic.class) private String family; @JsonView(ApiView.Basic.class) private String code; @JsonView(ApiView.Basic.class) private List<SubNote> subNotes; // getter/setter } // 子实体类 public class SubNote { // 不标记视图,默认不输出 private String id; @JsonView(ApiView.Basic.class) private String name; // 不标记视图,默认不输出 private String family; @JsonView(ApiView.Basic.class) private String code; // 不标记视图,默认不输出 private List<SubNote> subNotes; // getter/setter } // 接口方法 @GetMapping("/notes") @JsonView(ApiView.Basic.class) public ParentNote getNotes() { // 返回数据逻辑 }优点:灵活控制不同接口的输出字段,不影响实体类复用;缺点:需要定义视图接口,稍显繁琐。
2. 动态字段过滤(灵活适配多场景)
如果需要在运行时动态决定过滤哪些字段,可以用Jackson的FilterProvider:
@GetMapping("/notes") public String getNotes() throws JsonProcessingException { ParentNote note = getNoteData(); // 获取原始数据 ObjectMapper mapper = new ObjectMapper(); // 定义过滤器,排除子对象的id、family、subNotes字段 SimpleBeanPropertyFilter subNoteFilter = SimpleBeanPropertyFilter.serializeAllExcept("id", "family", "subNotes"); FilterProvider filters = new SimpleFilterProvider() .addFilter("subNoteFilter", subNoteFilter); // 注意:需要给子实体类添加@JsonFilter("subNoteFilter")注解 return mapper.writer(filters).writeValueAsString(note); }
优点:无需修改实体类的全局注解,运行时动态调整;缺点:需要编写少量过滤逻辑代码。
3. 直接操作JSON节点(无需修改实体类)
如果不想修改任何实体类,可以先将对象转成JsonNode,手动移除不需要的字段后再转成JSON:
@GetMapping("/notes") public String getNotes() throws JsonProcessingException { ParentNote note = getNoteData(); ObjectMapper mapper = new ObjectMapper(); JsonNode rootNode = mapper.valueToTree(note); // 遍历subNotes数组,移除每个子节点的指定字段 if (rootNode.has("subNotes") && rootNode.get("subNotes").isArray()) { ArrayNode subNotesArray = (ArrayNode) rootNode.get("subNotes"); for (JsonNode subNode : subNotesArray) { ((ObjectNode) subNode).remove("id"); ((ObjectNode) subNode).remove("family"); ((ObjectNode) subNode).remove("subNotes"); } } return mapper.writeValueAsString(rootNode); }
优点:完全不依赖实体类,适合临时需求或实体类无法修改的场景;缺点:需要手动处理JSON节点,嵌套复杂时代码量会增加。
最优方案选择
- 如果是固定单一场景,优先用
@JsonIgnore,实现成本最低; - 如果实体类需要多接口复用,用
@JsonView更合适,兼顾灵活性和可维护性; - 如果需要动态调整过滤规则(比如根据请求参数决定输出字段),选择动态过滤或JSON节点操作方案。
内容的提问来源于stack exchange,提问作者hamidreza_ahmadi
相关产品推荐
相关产品推荐

