是否可以为xml Web服务创建全局DTO
,但是内部有条件字段?
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class MyDTO {
...
@XmlElementWrapper
@XmlElement(name = "somename")
private List<String> list;
}
现在,如果我要发布Web服务的另一个版本,并由此重命名
@XmlElement
字段(或引入其他字段,删除某些字段等),该怎么办。这样就保留了向后兼容性,但是相同的对象用于“新”版本。
我可以用
/v1
,/v2
等添加请求路径方法来做到这一点。但是,我如何才能维护单个DTO类,但字段取决于版本路径?
还是我总是必须复制那些
DTO
类并完全根据我的版本需求进行修改?@RestController
public void MyServlet {
@RequestMapping("/v1")
public MyDTO1 request1() {
}
@RequestMapping("/v2")
public MyDTO2 request2() {
}
}
最佳答案
我希望为每个版本的API使用量身定制的DTO。为避免将实体映射到DTO时的样板代码,您可以考虑使用诸如MapStruct之类的映射框架。
如果您使用的是Jackson,则可以考虑使用JSON视图(它们也可以使用XML)。引用Latest Jackson integration improvements in Spring文章:
JSON Views
有时,筛选上下文序列化到HTTP响应正文的对象可能很有用。为了提供这样的功能,Spring MVC现在已经内置了对Jackson’s Serialization Views的支持(从Spring Framework 4.2开始,@MessageMapping
处理程序方法也支持JSON视图)。
以下示例说明了如何根据序列化上下文使用@JsonView
过滤字段-例如在处理集合时获得“摘要”视图,在处理单个资源时获得完整的表示形式:
public class View {
interface Summary {}
}
public class User {
@JsonView(View.Summary.class)
private Long id;
@JsonView(View.Summary.class)
private String firstname;
@JsonView(View.Summary.class)
private String lastname;
private String email;
private String address;
private String postalCode;
private String city;
private String country;
}
public class Message {
@JsonView(View.Summary.class)
private Long id;
@JsonView(View.Summary.class)
private LocalDate created;
@JsonView(View.Summary.class)
private String title;
@JsonView(View.Summary.class)
private User author;
private List<User> recipients;
private String body;
}
借助Spring MVC
@JsonView
支持,可以在每个处理程序方法的基础上选择应序列化哪个字段:@RestController
public class MessageController {
@Autowired
private MessageService messageService;
@JsonView(View.Summary.class)
@RequestMapping("/")
public List<Message> getAllMessages() {
return messageService.getAll();
}
@RequestMapping("/{id}")
public Message getMessage(@PathVariable Long id) {
return messageService.get(id);
}
}
在此示例中,如果检索到所有消息,则由于使用
getAllMessages()
注释的@JsonView(View.Summary.class)
方法,只有最重要的字段才被序列化:[ {
"id" : 1,
"created" : "2014-11-14",
"title" : "Info",
"author" : {
"id" : 1,
"firstname" : "Brian",
"lastname" : "Clozel"
}
}, {
"id" : 2,
"created" : "2014-11-14",
"title" : "Warning",
"author" : {
"id" : 2,
"firstname" : "Stéphane",
"lastname" : "Nicoll"
}
}, {
"id" : 3,
"created" : "2014-11-14",
"title" : "Alert",
"author" : {
"id" : 3,
"firstname" : "Rossen",
"lastname" : "Stoyanchev"
}
} ]
在Spring MVC默认配置中,
MapperFeature.DEFAULT_VIEW_INCLUSION
设置为false
。这意味着在启用JSON视图时,未注释的未注释字段或属性(例如正文或收件人)不会被序列化。使用
Message
处理程序方法(未指定JSON View)检索特定的getMessage()
时,所有字段均按预期方式序列化:{
"id" : 1,
"created" : "2014-11-14",
"title" : "Info",
"body" : "This is an information message",
"author" : {
"id" : 1,
"firstname" : "Brian",
"lastname" : "Clozel",
"email" : "bclozel@pivotal.io",
"address" : "1 Jaures street",
"postalCode" : "69003",
"city" : "Lyon",
"country" : "France"
},
"recipients" : [ {
"id" : 2,
"firstname" : "Stéphane",
"lastname" : "Nicoll",
"email" : "snicoll@pivotal.io",
"address" : "42 Obama street",
"postalCode" : "1000",
"city" : "Brussel",
"country" : "Belgium"
}, {
"id" : 3,
"firstname" : "Rossen",
"lastname" : "Stoyanchev",
"email" : "rstoyanchev@pivotal.io",
"address" : "3 Warren street",
"postalCode" : "10011",
"city" : "New York",
"country" : "USA"
} ]
}
只能使用
@JsonView
注释指定一个类或接口,但是您可以使用继承来表示JSON View层次结构(如果字段是JSON View的一部分,那么它也将是父视图的一部分)。例如,此处理程序方法将序列化用@JsonView(View.Summary.class)
和@JsonView(View.SummaryWithRecipients.class)
注释的字段:public class View {
interface Summary {}
interface SummaryWithRecipients extends Summary {}
}
public class Message {
@JsonView(View.Summary.class)
private Long id;
@JsonView(View.Summary.class)
private LocalDate created;
@JsonView(View.Summary.class)
private String title;
@JsonView(View.Summary.class)
private User author;
@JsonView(View.SummaryWithRecipients.class)
private List<User> recipients;
private String body;
}
@RestController
public class MessageController {
@Autowired
private MessageService messageService;
@JsonView(View.SummaryWithRecipients.class)
@RequestMapping("/with-recipients")
public List<Message> getAllMessagesWithRecipients() {
return messageService.getAll();
}
}