无关联地序列化模型

时间:2016-12-23 23:15:13

标签: java spring spring-mvc spring-boot jackson

我有两种模式:

@Entity
class ModelA {
  ...
  @OneToMany(mappedBy = "modelA")
  @JsonManagedReference
  private List<ModelB> modelBs = new ArrayList<ModelB>();
  ...
}

@Entity
class ModelB {
  ...
  @ManyToOne
  @JoinColumn(name = "modela_id")
  @JsonBackReference
  private ModelA modela;
  ...
}

ModelA的REST /资源控制器,它有一个列表方法:

@GetMapping("list")
public @ResponseBody ArrayList<ModelA> list() {
  ArrayList<ModelA> modelas = new ArrayList();

  for (ModelA moa : modelARepository.findAll()) {
    modelas.add(moa);
  }

  return modelas;
}

现在默认情况下,来自该控制器的响应将包含ModelA对象的JSON数组,但这些对象也将包含ModelB。有没有办法选择你想要序列化的关系以及什么时候?

来自Django / Laravel我会用专门的序列化来解决这个问题,例如我会有一个ModelAWithRelatiionsSerializer我会序列化ModelA +它的所有关系或ModelAWithModelBSerializer会返回一个序列化的ModelA ModelB等等。

我知道我可以忽略注释而不包含它但是真的不一样,我在Spring包中没有看到关于序列化的内容。这样做的最佳或最佳方式是什么?

1 个答案:

答案 0 :(得分:0)

对于其他任何从Laravel(PHP),Django(Python),Rails(Ruby)迁移的人:

我创建了一个包serializers,其中我为每个模型都有一个序列化程序类。

public class UserSerializer {

  // Default user values
  public interface User {}

  // User values with users posts
  public interface UserWithPosts extends User {}  

}

现在,在您的模型中,您所做的是注释它们所属的序列化程序的字段:

@Entity  
class User {

  @Id
  @GeneratedValue
  @JsonView(UserSerializer.User.class)
  private Long id;

  @JsonView(UserSerializer.User.class)
  private String username;

  @JsonView(UserSerializer.User.class)
  private String email;

  @OneToMany(mappedBy = "user")
  @JsonManagedReference  // This prevents infinite loop  
  @JsonView(UserSerializer.UserWithPosts.class)  
  private List<Post> posts;

  // .. getters and setters bellow
}

在您的控制器中,您可以使用@JsonView(..)为您的方法添加关于要使用的序列化程序的注释。

@RestController  
@RequestMapping("users")  
public class UsersController {

  private UserRepository userRepository;

  public UserController(UserRepository userRepository) {
    this.userRepository = userRepository;
  }  

  /**
   * In list method we only want users default values to keep the payload smaller.
   */
  @GetMapping("list")
  @JsonView(UserSerializer.User.class)  
  public @ResponseBody ArrayList<User> list() {
    ArrayList<User> users = new ArrayList<>();

    for (User user : userRepository.findAll()) {
      users.add(user);
    }

    return users;
  }


  /**
   * In this method we are only fetching a single user so payload is smaller so we can add the users posts as well. You could also toggle this with a url filter/parameter.  
   */
  @GetMapping("show")
  @JsonView(UserSerializer.UserWithPosts.class)  
  public @ResponseBody ArrayList<User> show(@RequestParam(name = "id", required = true) Long id) {
    return userRepository.findOne(id):
  }

}  

配置!这很容易理解,但是当我第一次做到这一点时,没有任何示例/教程告诉/表明您还需要更新配置。如果你没有这样做,那么序列化你的关系会有问题,序列化器仍然可以工作,但你会在你的JSON响应中得到这样的东西:

...
"posts": [
  {},
  {},
  {},..
]
...

所以它会为每个帖子用户返回一个JSON对象,但没有值来修复这个我创建的config包添加了WebConfiguration类:

@Configuration
@EnableWebMvc
public class WebConfiguration extends WebMvcConfigurerAdapter {

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

}  

您也可以在XML配置中执行此操作。但我个人更喜欢使用基于Java类的配置。如果弹簧文档包含对新手的更好解释,那将是很好的。