关于java:Json序列化以及Infinite-recursion

19次阅读

共计 4070 个字符,预计需要花费 11 分钟才能阅读完成。

背景

JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion

过后开发的时候报了这个错。

看报错就晓得是因为,json 的相互依赖。

比方 A 中有一个属性是 B , 而 B 中也有一个属性是 A。这将造成了 json 序列化的时候,报相互依赖的异样。

而正好,以后的代码就是这么干的。

@Entity
public class DivisionalWorksType {@ApiModelProperty("评分我的项目模版")
  @OneToMany(mappedBy = "divisionalWorksType")
  @JsonView(ScoringItemTemplateJsonView.class)
  List<ScoringItemTemplate> scoringItemTemplates = new ArrayList<>();}
@Entity
public class ScoringItemTemplate {
 @ManyToOne
  @ApiModelProperty("所属散布工程模版类型")
  @JsonView(DivisionalWorksTypeJsonView.class)
  private DivisionalWorksType divisionalWorksType;
}

序列化

可能还有的人不太分明序列化。

什么是序列化与反序列化?

Java 序列化 是指把 Java 对象转换为字节序列的过程,而 Java 反序列化是指把字节序列复原为 Java 对象的过程。

为什么要实现对象的序列化和反序列化?

  1. 咱们创立的 Java 对象被存储在 Java 堆中,当程序运行完结后,这些对象会被 JVM 回收。但在事实的利用中,可能会要求在程序运行完结之后还能读取这些对象,并在当前检索数据,这时就须要用到序列化。
  2. 当 Java 对象通过网络进行传输的时候。因为数据只可能以二进制的模式在网络中进行传输,因而当把对象通过网络发送进来之前须要先序列化成二进制数据,在接收端读到二进制数据之后反序列化成 Java 对象。

spring boot 中如何实现的序列化?

答案是:通过 Serializable 接口

能够发现,该接口是空的。

实际上,Serializable这个标记接口是给 java 虚拟机参考的,java 虚拟机看到这个接口之后,会为该类主动生成一个序列化版本号。

有人感到纳闷:理论在开发中,有时候咱们的类并没有实现 Serializable 接口。然而为什么依然能通过网络传输?


答案兴许是:类里的属性的根本类型,都根本实现了 Serializable 接口

能够看到,很多的 java 类型都实现了 Serializable 接口。这时候,就能够很快地将该对象里的属性序列化。

同时 Spring 框架帮咱们做了另一些一些事件:Spring 并不是间接把 Object 进行网络传输,而是先把 Object通过序列化 转换成 json 格局的字符串,而后再进行传输的。

这里的序列化也能够示意为:序列化就是将对象转换成 Json 格局的字符串,反序列化就是逆过程,将 Json 串转换成对象。

Spring 框架如何将数据转换成 Json?

通常,咱们应用的时候,只须要在 Controller 类中做如下定义:

@RestController
@RequestMapping("User")
public class UserController {
    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public User save(@RequestBody User user) {return this.userService.save(user);
    }
}

在 Controller 中应用 @ResponseBody 注解即可返回 Json 格局的数据,

@ResponseBody 的作用, 其实是将 Controller 的办法返回的对象通过转换为指定格局之后,写入到 response 对象的 body 区,通常用来返回 json 或者 xml 数据

@RestController 注解蕴含了 @ResponseBody 注解,所以默认状况下,@RestController即可将返回的数据结构转换成 Json 格局。

所以,咱们应用 @RestController 注解即可。

这些注解之所以能够进行 Json 与 Java 类 之间的互相转换,就是因为 HttpMessageConverter 施展着作用。

HttpMessageConverter

前端发来申请后,

1. 调用 HttpInputMessage 从输出流中获取 Json 字符串

2. 在 HttpMessageConverter 中把 Json 转换为接口须要的形参类型。

3. 在 HttpMessageConverter 将 Json 转换为 Java 实体类

至于 HttpMessageConverter 怎么解决,这里就不开展阐明了

解决问题

回到背景问题。

过后去谷歌之后有两个比拟不便的办法:

1. 应用 @JsonManagedReference@JsonBackReference注解。

@JsonManagedReference 正文增加到父级模型,即父级的 getter 办法

@JsonManagedReference
public List<DivisionalWorksType> getDivisionalWorksTypes() {return divisionalWorksTypes;}

再将@JsonBackReference 增加到子级模型

@JsonBackReference
public DivisionalWorksTemplate getDivisionalWorksTemplate() {return divisionalWorksTemplate;}

2. 应用 @JsonIgnore 注解

@JsonIgnore
public List<DivisionalWorksType> getDivisionalWorksTypes() {return divisionalWorksTypes;}

原理:

@JsonManagedReference:治理援用的一方,能够了解为具备援用的一方,被这个注解的属性序列化时会失常获取
@JsonBackReference:反向援用,能够了解为此属性为反向援用,被这个注解的属性序列化时会疏忽

一个用于父级角色,另一个用于子级角色:

用了这两个注解后,作用即:一方不被序列化

再来看 @JsonIgnore

@JsonIgnore 并不是为解决有限递归问题而设计的,它只是疏忽了正文属性不被序列化或反序列化。然而,如果字段之间存在双向链接,则因为 @JsonIgnore 会疏忽带正文的属性,因而能够防止有限递归。

后话

过后用了 @JsonManagedReference 和 @JsonBackReference 这对注解后,便没有再报错了。

然而之后删除这两个注解后,也并不会报错。

或者是因为起初加上了 @JsonView 注解,管制输入输出后的 json。

entity:

 
@ApiModelProperty("散布工程类型")
  @OneToMany(mappedBy = "divisionalWorksTemplate")
  @JsonView(DivisionalWorksTypesJsonView.class)
  List<DivisionalWorksType> divisionalWorksTypes = new ArrayList<>();

controller:

@GetMapping("page")
@JsonView(PageJsonView.class)
public Page<DivisionalWorksTemplate> page(@RequestParam(required = false) String name,
      @SortDefault.SortDefaults(@SortDefault(sort = "id", direction = Sort.Direction.DESC)) Pageable pageable
) {return this.divisionalWorksTemplateService.page(name, pageable);
}

private class PageJsonView implements DivisionalWorksTemplate.DivisionalWorksTypesJsonView, DivisionalWorksType.ScoringItemTemplateJsonView {}

配置 @JsonView

想管制 json 格局输入的时候,若在实体中,一个个地加上 @JsonView 注解太麻烦了;

咱们就能够配置 @JsonView

@Configuration
public class WebConfig implements WebMvcConfigurer {

  /**
   * 配置 JsonView
   */
  @Override
  public void configureMessageConverters(final List<HttpMessageConverter<?>> converters) {final ObjectMapper mapper = Jackson2ObjectMapperBuilder.json().defaultViewInclusion(true).build();
    converters.add(new MappingJackson2HttpMessageConverter(mapper));
  }

这里设置了defaultViewInclusion(true),看 javadoc 说这是一个双向开关,开启将输入没有 JsonView 注解的属性,false 敞开将输入有 JsonView 注解的属性。

所以咱们就能够反转 @JsonView 注解的应用了。

参考文章: https://blog.csdn.net/qq_4261…

正文完
 0