https://blog.chuangzhi8.cn/2018/07/13/ring-Boot响应结果同时支持json和xml,Java-Bean对象列表与xml互转/#more
虽然目前接口都流行响应json格式数据,但平时开发还是免不了有返回xml格式数据的需求,遇到这种情况自己写个xml解析&转换器显然太明智了,对于使用SpringBoot开发的小伙伴儿还是挺幸福的,SpringBoot已经完美支持这两种格式的解析和转换。
对于@RestController 默认支持返回json格式数据,即使不做任何配置也能返回json数据,但是返回xml格式的数据该怎么做?
1 2 3 4 5 6 7 | @RestController public class DemoController { @RequestMapping(value = "/query", produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE }) public Object demo() { } } |
然后通过query.json或者query.xml分别获取json和xml数据(不能通过设置Content-Type头为application/json或application/xml访问)
说明:网上搜的一些关于SpringBoot返回xml格式的教程都要加jackson-dataformat-xml的依赖,但是作者没有引入这个依赖依然正常使用
话外音:SpringBoot默认使用JAXB进行JavaBean和xml之间的转换。JAXB(Java Architecture for XML Binding)是根据XML Schema映射到JavaBean的技术。过程中,JAXB将XML实例文档反向生成Java对象树的方法,并能将Java对象树的内容重新写到 XML实例文档。
1 2 3 4 | <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> </dependency> |
接下来是Java Bean与xml怎样使用注解进行互转,尤其使用对象列表时如何优雅的使用注解转成xml呢?
先给出目标xml格式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <BandWidthData> <Result>success</Result> <DetailInfo>expired 136 milliseconds.</DetailInfo> <ChannelGroupIDs> <ChannelGroupID ID="70813" Value="2180053898"> <Region Name="9050" Value="2180053898"/> <Region Name="10200" Value="0"/> </ChannelGroupID> <ChannelGroupID ID="2539" Value="485842"> <Region Name="9050" Value="485842"/> <Region Name="10200" Value="0"/> </ChannelGroupID> </ChannelGroupIDs> </BandWidthData> |
这个xml的结构有个较难的点就是如何使用注解表示这个ChannelGroupIDs列表?
先给出对应的Java Bean再解释各个注解的作用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | 最外层对象: import java.util.List; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; import com.fasterxml.jackson.databind.PropertyNamingStrategy; import com.fasterxml.jackson.databind.annotation.JsonNaming; @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) //返回xml格式时非必须,此注解是转json的蛇形命名用的 @XmlRootElement(name = "BandWidthData") // 定义xml根节点 @XmlType(propOrder = { "result", "detailInfo", "channelGroupBandwiths" }) // 定义xml节点的顺序 public class BandwidthDataResult { private String result; private List<ChannelGroupBandwithResult> channelGroupBandwiths; private String detailInfo; public String getResult() { return result; } @XmlElement(name = "Result") //定义xml节点 public void setResult(String result) { this.result = result; } public List<ChannelGroupBandwithResult> getChannelGroupBandwiths() { return channelGroupBandwiths; } @XmlElementWrapper(name = "ChannelGroupIDs") // 定义list包装层节点的名称 @XmlElement(name = "ChannelGroupID") // 映射List中每个节点的名称 public void setChannelGroupBandwiths(List<ChannelGroupBandwithResult> channelGroupBandwiths) { this.channelGroupBandwiths = channelGroupBandwiths; } public String getDetailInfo() { return detailInfo; } @XmlElement(name = "DetailInfo") public void setDetailInfo(String detailInfo) { this.detailInfo = detailInfo; } } 中间对象: import java.util.List; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlType; import com.fasterxml.jackson.databind.PropertyNamingStrategy; import com.fasterxml.jackson.databind.annotation.JsonNaming; @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) @XmlType(propOrder = { "channelGroupId", "channelGroupBandwith", "regionBandwiths" }) public class ChannelGroupBandwithResult { private long channelGroupId; private long channelGroupBandwith; private List<RegionBandwithResult> regionBandwiths; public long getChannelGroupId() { return channelGroupId; } @XmlAttribute(name = "ID") // 映射将该对象的此属性为xml节点的属性 public void setChannelGroupId(long channelGroupId) { this.channelGroupId = channelGroupId; } public long getChannelGroupBandwith() { return channelGroupBandwith; } @XmlAttribute(name = "Value") // 同上 public void setChannelGroupBandwith(long channelGroupBandwith) { this.channelGroupBandwith = channelGroupBandwith; } public List<RegionBandwithResult> getRegionBandwiths() { return regionBandwiths; } @XmlElement(name = "Region") public void setRegionBandwiths(List<RegionBandwithResult> regionBandwithResults) { this.regionBandwiths = regionBandwithResults; } } 底层对象: import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlType; import com.fasterxml.jackson.databind.PropertyNamingStrategy; import com.fasterxml.jackson.databind.annotation.JsonNaming; @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) @XmlType(propOrder = { "regionName", "regionBandwith" }) public class RegionBandwithResult { private String regionName; private long regionBandwith; public String getRegionName() { return regionName; } @XmlAttribute(name = "Name") public void setRegionName(String regionName) { this.regionName = regionName; } public long getRegionBandwith() { return regionBandwith; } @XmlAttribute(name = "Value") public void setRegionBandwith(long regionBandwith) { this.regionBandwith = regionBandwith; } } |
至此基本可以支持Java Bean的List对象转xml格式了。
采坑记录:1、作者最初使用lombok的@Setter、@Getter生成bean的set和get方法,同时把@XmlAttribute(name = “Name”)注解标在属性上,导致别名name=”Name”失效了;2、xml的相关注解不要同时在set和get方法上使用。
其他注解:1、@XmlValue 映射List中对象的属性 到 xml节点的值,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | 目标接收xml格式数据: <?xml version="1.0" encoding="GB2312"?> <BandWidthDatas> <Result>success</Result> <Datas> <Data area="9050" chlgrp="10018487" >47870270,</Data> <Data area="10200" chlgrp="10018487" >0,</Data> <Data area="9050" chlgrp="33755" >6460458,</Data> <Data area="10200" chlgrp="33755" >0,</Data> </Datas> </BandWidthDatas> Java类的最外层对象结构: import java.util.List; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "BandWidthDatas") public class BandWidthDataDTO { private String result; private List<BandWidthDetailDataDTO> datas; @XmlElement(name = "Result") public String getResult() { return result; } public void setResult(String result) { this.result = result; } @XmlElementWrapper(name = "Datas") @XmlElement(name = "Data") public List<BandWidthDetailDataDTO> getDatas() { return datas; } public void setDatas(List<BandWidthDetailDataDTO> datas) { this.datas = datas; } } Java类最底层对象结构: import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlValue; public class BandWidthDetailDataDTO { private String area; private String chlgrp; private String value; @XmlAttribute(name = "area") public String getArea() { return area; } public void setArea(String area) { this.area = area; } @XmlAttribute(name = "chlgrp") public String getChlgrp() { return chlgrp; } public void setChlgrp(String chlgrp) { this.chlgrp = chlgrp; } @XmlValue // 映射xml节点的值 public String getValue() { return value; } public void setValue(String value) { this.value = value; } } |