大家好,我是llp。最近在对接第三方接口时,发现他们都是xml形式的报文,而且各有差异有的集合的地方包含父标签而有的又没有,于是就想着自己做一个基于注解反射去生成xml的方案。今天正好完成了,做个记录顺着做个分享,程序肯定有很多不足的地方,非常欢迎大家提出建议。
1.XMl语法
在此之前我们需要先知道一个XML文件分为如下几部分内容
1.文档声明
2.元素
3.属性
4.注释
5.CDATA区、特殊字符
具体细节,可以参考官网文档
2.定义自己的一套注解
这里我将注解分为下面几类:
1.@XmlBeanElement 用于修饰类
2.@XmlPropertyELement 用于修饰普通类型的字段
3.@XmlBeanElement 用于修饰bean类型的字段
4.@XmlCDATA 用于修饰生成CDATA区的字段
5.@XmlGatherElement 用于修饰集合类型的字段 List<String>、List<Person>、List<Map>
6.@XmlMapElement 用于修饰Map类型的字段 Map<String,String>
这里Map只考虑了普通类型的key,val方式
1.@XmlBeanElement
/**
* xml跟标签注解(xml元素)
* ElementType.TYPE 修饰类
* @Retention(RetentionPolicy.RUNTIME) 运行时生效
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XmlRootElement {
/**
* 别名
*/
String name() default "";
/**
* 命名空间
*/
String[] namespace() default "";
/**
* 版本号 用于文档声明
*/
String version() default "v1.0";
/**
* 编码 用于文档声明
*/
String encoding() default "utf-8";
}
2.@XmlPropertyELement
/**
* xml属性注解
* @Target(ElementType.FIELD) 修饰字段
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XmlPropertyELement {
/**
* 别名
*/
String name() default "";
/**
* 命名空间
*/
String namespace() default "";
/**
* 是否解析空值
*/
boolean analysisEmpty() default false;
}
3.@XmlBeanElement
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XmlBeanElement {
/**
* 别名
*/
String name() default "";
/**
* 命名空间
*/
String namespace() default "";
/**
* 是否解析空值
* @return
*/
boolean analysisEmpty() default false;
}
4.@XmlCDATA
/**
* CDATA区
* <![CDATA[
* 这里可以把你输入的字符原样显示,不会解析 xml
* ]]>
* 1.因为这里设计@XmlCDATA是配合@XmlPropertyELement注解进行使用
* 是否解析空值、命名空间、别名等交由@XmlPropertyELement控制
* 2.@XmlCDATA注解只关注于xml中CDATA区的处理
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XmlCDATA {
/**
* <code>
* <!--如果希望把某些字符串,当做普通文本,使用CDATA包括 -->
* <![CDATA[
* <script data-compress=strip>
* function h(obj){
* obj.style.behavior='url(#default#homepage)';
* var a = obj.setHomePage('//www.baidu.com/');
* }
* </script>
* ]]>
* </code>
*/
}
5.@XmlGatherElement
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XmlGatherElement {
/**
* 别名
*/
String name() default "";
/**
* 命名空间
*/
String namespace() default "";
/**
* 子标签
*/
String childName() default "";
/**
* 是否解析空值
*/
boolean analysisEmpty() default false;
/**
* 是否显示父标签名
*/
boolean showName() default true;
/**
* 是否是bean
*/
boolean isBean() default false;
}
6.@XmlMapElement
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XmlMapElement {
/**
* 标签别名
*/
String name() default "";
/**
* 命名空间
*/
String namespace() default "";
/**
* 是否解析空值
*/
boolean analysisEmpty() default false;
}
3.生成XML的工具类
public class XmlUtil {
/**
* 生成xml字符串
*
* @param obj xml元素注解修饰的对象
* @return
*/
public static String generateXml(Object obj) {
//1.获取到class对象的
Class<?> clazz = obj.getClass();
//2.获取xml元素注解
XmlRootElement xmlRootElement = clazz.getAnnotation(XmlRootElement.class);
//3.定义一个StringBuffer对象用于拼接xml字符串
StringBuffer xml = new StringBuffer();
//4.文档声明 组装 <?xml version="1.0" encoding="UTF-8"?>
xml.append("<?xml ")
.append("version=" + xmlRootElement.version())
.append(" ")
.append("encoding=" + xmlRootElement.encoding())
.append("?>");
//5.xml元素(跟标签)拼接
String name = xmlRootElement.name();
//如果别名为空,以类名作为标签
if ("".equals(name) || name == null) {
name = clazz.getSimpleName();
}
xml.append("<")
.append(name)
.append(" ");
String[] namespace = xmlRootElement.namespace();
//6.遍历命名空间,拼接xml元素命名空间
for (int i = 0; i < namespace.length; i++) {
if (StringUtils.isNotBlank(namespace[i])) {
xml.append(namespace[i])
.append(" ");
}
}
//移除末尾多余的空格
xml.deleteCharAt(xml.lastIndexOf(" "));
xml.append(">");
//7.拼接xml属性
element(xml, clazz, obj);
//8.xml元素结尾
xml.append("</")
.append(xmlRootElement.name())
.append(">");
return xml.toString();
}
/**
* 组装xml属性标签内容
*
* @param xml
* @param clazz
* @param obj
*/
private static void element(StringBuffer xml, Class<?> clazz, Object obj) {
//1.获取类的所有字段
Field[] fields = clazz.getDeclaredFields();
//2.遍历所有字段
for (Field field : fields) {
Object val = getFieldVal(field, obj);
//3.判断每个字段被什么类型的注解修饰
if (field.isAnnotationPresent(XmlPropertyELement.class)) {
//基本类型字段内容处理
propertyElement(field, xml, val);
} else if (field.isAnnotationPresent(XmlBeanElement.class)) {
//对象字段内容处理
beanELement(field, xml, val);
} else if (field.isAnnotationPresent(XmlGatherElement.class)) {
//集合字段内容处理
gatherELement(field, xml, val);
} else if (field.isAnnotationPresent(XmlMapElement.class)) {
//map字段内容处理
mapELement(field, xml, val);
}
}
}
/**
* map字段内容处理
*
* @param field
* @param xml
* @param val
*/
private static void mapELement(Field field, StringBuffer xml, Object val) {
//1.获取@XmlMapElement注解
XmlMapElement xmlMapElement = field.getAnnotation(XmlMapElement.class);
//2.判断是否解析空值
if (!xmlMapElement.analysisEmpty() && isEmpty(val)) {
return;
}
//3.获取别名,如果别名为空则以字段名作为标签
String name = xmlMapElement.name();
if ("".equals(name) || name == null) {
name = field.getName();
}
xml.append("<").append(name);
//4.获取命名空间,如果存在则拼接
if (!"".equals(xmlMapElement.namespace()) && xmlMapElement.namespace() != null) {
xml.append(" ").append(xmlMapElement.namespace());
}
xml.append(">");
//遍历map拼接
if (val instanceof Map) {
mapToXmlString(xml, (Map) val);
} else {
throw new RuntimeException("@XmlMapElement标识字段必须为Map类型");
}
//结束标签拼接
xml.append("</").append(name).append(">");
}
/**
* 集合字段内容处理
*
* @param field
* @param xml
* @param val
*/
private static void gatherELement(Field field, StringBuffer xml, Object val) {
//1.获取@XmlGatherElement注解
XmlGatherElement gatherElement = field.getAnnotation(XmlGatherElement.class);
//判断是否处理空数据
if (!gatherElement.analysisEmpty() && isEmpty(val)) {
return;
}
String name = gatherElement.name();
//别名为空按名字装配
if (gatherElement == null || name.length() == 0) {
name = field.getName();
}
//判断是否组装父标签
boolean showName = gatherElement.showName();
if (showName) {
xml.append("<").append(name);
if (gatherElement.namespace() != null && !"".equals(gatherElement.namespace()))
xml.append(" ").append(gatherElement.namespace());
xml.append(">");
}
//获取子标签名称
String childName = gatherElement.childName();
//子标签为空则已父标签作为子标签
if (isEmpty(childName))
childName = name;
if (val instanceof Array) {
//处理数组数据
//判断子标签是否需要解析
if (!gatherElement.isBean()) {
//不为bean对象则直接遍历数组,对每个元素进行拼接
for (Array a : (Array[]) val) {
xml.append("<").append(childName).append(">").append(a).append("</").append(childName).append(">");
}
} else {
//每个元素都是bean对象的数组
for (Object ob : (Array[]) val) {
//组装子标签数据
beanOrMapEment(childName, xml, ob);
}
}
} else if (val instanceof Collection) {
//处理集合数据
Collection list = (Collection) val;
//判断集合子标签是否需要解析
if (!gatherElement.isBean()) {
for (Object ob : list) {
xml.append("<").append(childName).append(">").append(ob).append("</").append(childName).append(">");
}
} else {
for (Object ob : list) {
//解析子标签
beanOrMapEment(childName, xml, ob);
}
}
}
//如果显示父标签,拼接父标签结束标签
if (showName) {
xml.append("</").append(name).append(">");
}
}
/**
* 每个元素为bean或map数据拼接
*
* @param childName
* @param xml
* @param ob
*/
private static void beanOrMapEment(String childName, StringBuffer xml, Object ob) {
xml.append("<").append(childName).append(">");
//当为空时不处理
if (ob != null) {
//判断集合内元素是否为map
if (ob instanceof Map) {
//解析map数据
mapToXmlString(xml, (Map) ob);
} else {
//解析bean,获取元素的class对象
Class clazz = ob.getClass();
//组装元素xml报文
element(xml, clazz, ob);
}
}
xml.append("</").append(childName).append(">");
}
/**
* map遍历组装
*
* @param xml
* @param map
*/
private static void mapToXmlString(StringBuffer xml, Map map) {
if (map == null || map.size() == 0) {
return;
}
for (Object key : map.keySet()) {
xml.append("<").append(key).append(">").append(map.get(key)).append("</").append(key).append(">");
}
}
/**
* bean属性字段内容处理
*
* @param field
* @param xml
* @param val
*/
private static void beanELement(Field field, StringBuffer xml, Object val) {
//1.获取@XmlBeanElement注解
XmlBeanElement xmlBeanElement = field.getAnnotation(XmlBeanElement.class);
//2.判断是否对空值进行解析
if (!xmlBeanElement.analysisEmpty() && isEmpty(val)) {
//如果不对空值解析且属性值为空则直接return
return;
}
//3.拼接bean对象起始标签
String name = xmlBeanElement.name();
//如果别名为空则已字段名进行拼接
if (name == null || "".equals(name)) {
name = field.getName();
}
xml.append("<").append(name);
//4.判断属性是否设置命名空间
if (!"".equals(xmlBeanElement.namespace()) && xmlBeanElement.namespace() != null) {
xml.append(" ").append(xmlBeanElement.namespace());
}
xml.append(">");
//5.判断是否字段是否被@XmlCDATA注解修饰
if (field.isAnnotationPresent(XmlCDATA.class)) {
//对内容进行包裹
xml.append("<![CDATA[").append(val).append("]]>");
}
//6.获取对象字段的class对象,对bean对象的属性进行组装,如果字段中还含有bean对象则进行递归处理,依次类推
Class<?> fieldClass = field.getType();
element(xml, fieldClass, val);
//7.拼接bean对象结束标签
xml.append("</").append(xmlBeanElement.name()).append(">");
}
/**
* 基本类型字段内容处理
*
* @param field
* @param xml
* @param val
*/
private static void propertyElement(Field field, StringBuffer xml, Object val) {
//1.获取@XmlPropertyELement
XmlPropertyELement xmlPropertyELement = field.getAnnotation(XmlPropertyELement.class);
//2/判断是否解析空值,如果不解析且值为空则返回
if (!xmlPropertyELement.analysisEmpty() && isEmpty(val)) {
return;
}
//3.属性标签名称拼接
//如果别名为空则已字段名进行拼接
String name = xmlPropertyELement.name();
if (name == null || "".equals(name)) {
name = field.getName();
}
xml.append("<").append(name);
//4.判断属性是否设置命名空间,如果存在则进行拼接
if (!"".equals(xmlPropertyELement.namespace()) && xmlPropertyELement.name() != null) {
xml.append(" ").append(xmlPropertyELement.namespace());
}
xml.append(">");
/**
*
* <![CDATA[
* 这里可以把你输入的字符原样显示,不会解析 xml
* ]]>
*/
//5.拼接属性标签内容
if (field.isAnnotationPresent(XmlCDATA.class)) {
//如果字段被@XmlCDATA注解就用<![CDATA[标签内容]]>将标签体进行包裹
xml.append("<![CDATA[").append(val).append("]]>");
} else {
//如果字段没有被@XmlCDATA注解修饰则直接拼接标签体
xml.append(val);
}
//6.拼接属性标签结束标记
xml.append("</").append(xmlPropertyELement.name()).append(">");
}
/**
* 判断对象值是否为空
*
* @param val
* @return
*/
private static boolean isEmpty(Object val) {
return val == null || "".equals(val);
}
/**
* 反射获取字段值
*
* @param field
* @param obj
* @return
*/
private static Object getFieldVal(Field field, Object obj) {
try {
//允许调用私有的方法
field.setAccessible(true);
//反射获取字段值
return field.get(obj);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
4.测试使用
1.使用步骤
1.首先我们需要用注解去修饰含有xml字段的bean
test01—FileDto
@Data
@XmlRootElement(name = "file",
namespace = {"xmlns="urn:gsma:params:xml:ns:rcs:rcs:fthttp"",
"xmlns:x="urn:gsma:params:xml:ns:rcs:rcs:up:fthttpext""})
public class FileDto {
@XmlBeanElement(name = "file-info" ,namespace = "type="thumbnail"")
private FileInfoDto thumbFileInfoDto;
@XmlBeanElement(name = "file-info", namespace = "file-disposition="file"")
private FileInfoDto fileInfoDto;
}
@Data
public class FileInfoDto {
@XmlPropertyELement(name = "file-size")
private String fileSize;
@XmlPropertyELement(name = "file-name")
private String fileName;
@XmlPropertyELement(name = "content-type")
private String contentType;
@XmlPropertyELement(name = "x:branded-url")
private String brandedUrl;
}
test02—OutboundMessageRequestDto
/**
* <?xml version="1.0" encoding="UTF-8"?>
* <msg:outboundMessageRequest xmlns:msg="urn:oma:xml:rest:netapi:messaging:1">
* <messageId>5eae954c-42ca-4181-9ab4-9c0ef2e2ac66</messageId>
* <clientCorrelator>567895</clientCorrelator>
* </msg:outboundMessageRequest>
*/
@Data
@XmlRootElement(name = "msg:outboundMessageRequest",namespace = "xmlns:msg="urn:oma:xml:rest:netapi:messaging:1"")
public class OutboundMessageRequestDto {
@XmlPropertyELement(name = "messageId")
private String messageId;
@XmlPropertyELement(name = "clientCorrelator")
private String clientCorrelator;
@XmlCDATA
@XmlPropertyELement(name = "bodyText",analysisEmpty = true)
private String bodyText;
}
test03-RequestDto
@Data
@XmlRootElement(name = "Request",namespace = "xmlns:msg="urn:oma:xml:rest:netapi:messaging:1"")
public class RequestDto {
@XmlPropertyELement(name = "address")
private String address;
@XmlGatherElement(name = "destinationAddress",childName = "phone")
private List<String> destinationAddress;
@XmlPropertyELement(name = "senderAddress")
private String senderAddress;
@XmlBeanElement(name = "Message")
private MessageDto messageDto;
@XmlPropertyELement(name = "clientCorrelator")
private String clientCorrelator;
}
@Data
public class MessageDto {
@XmlPropertyELement(name = "contentType")
private String contentType;
@XmlPropertyELement(name = "conversationID")
private String conversationID;
@XmlPropertyELement(name = "contributionID")
private String contributionID;
@XmlGatherElement(name = "reportRequest",showName = true,analysisEmpty = true)
private List<String> reportRequest;
@XmlBeanElement(name = "serviceCapability")
private ServiceCapabilityDto serviceCapabilityDto;
@XmlPropertyELement(name = "messageId")
private String messageId;
@XmlPropertyELement(name = "bodyText")
private String bodyText;
}
@Data
public class ServiceCapabilityDto {
@XmlPropertyELement(name = "capabilityId")
private String capabilityId;
@XmlPropertyELement(name = "version")
private String version;
}
2.测试案例
public class MyTest {
/**
一个XML文件分为如下几部分内容
1.文档声明
2.元素
3.属性
4.注释
5.CDATA区、特殊字符
* <?xml version="1.0" encoding="UTF-8"?>
* <file
* xmlns="urn:gsma:params:xml:ns:rcs:rcs:fthttp"
* xmlns:x="urn:gsma:params:xml:ns:rcs:rcs:up:fthttpext">
* <file-info type="thumbnail">
* <file-size>2048</file-size>
* <content-type>png</content-type>
* </file-info>
* <file-info type="file" file-disposition="file">
* <file-size>1024</file-size>
* <file-name>1.jpg</file-name>
* <content-type>jpeg</content-type>
* <x:branded-url>[alternative branded HTTP URL of the file]</x:branded-url>
* </file-info>
* </file>
*/
@Test
public void test01(){
FileDto fileDto = new FileDto();
FileInfoDto thumbFileInfo = new FileInfoDto();
thumbFileInfo.setFileSize("2048");
thumbFileInfo.setContentType("png");
FileInfoDto fileInfoDto = new FileInfoDto();
fileInfoDto.setFileSize("1024");
fileInfoDto.setFileName("1.jpg");
fileInfoDto.setContentType("jpeg");
fileDto.setThumbFileInfoDto(thumbFileInfo);
fileDto.setFileInfoDto(fileInfoDto);
String xml = XmlUtil.generateXml(fileDto);
System.out.println(xml);
}
/**
* <?xml version=v1.0 encoding=utf-8?>
* <msg:outboundMessageRequest
* xmlns:msg="urn:oma:xml:rest:netapi:messaging:1">
* <messageId>c8c43a68-c7dd-44c6-b047-2e7e43d5d1b9</messageId>
* <clientCorrelator>123456</clientCorrelator>
* <bodyText>
* <![CDATA[null]]>
* </bodyText>
* </msg:outboundMessageRequest>
*/
@Test
public void test02(){
OutboundMessageRequestDto outboundMessageRequestDto = new OutboundMessageRequestDto();
outboundMessageRequestDto.setMessageId(UUID.randomUUID().toString());
outboundMessageRequestDto.setClientCorrelator("123456");
outboundMessageRequestDto.setBodyText(null);
String xml = XmlUtil.generateXml(outboundMessageRequestDto);
System.out.println(xml);
}
/**
* <?xml version="1.0" encoding="UTF-8"?>
* <Request
* xmlns:msg="urn:oma:xml:rest:netapi:messaging:1">
* <address>tel:+8619585550103</address>
* <destinationAddress>
* <phone>tel:+8619585550103</phone>
* <phone>tel:+8619585550104</phone>
* </destinationAddress>
* <senderAddress>sip:12599@botplatform.rcs.chinamobile.com</senderAddress>
* <Message>
* <contentType> text/plain
* </contentType>
* <conversationID>XSFDEREW#%$^$%^^&^%
* </conversationID>
* <contributionID>SFF$#%$%%^%&^%THT
* </contributionID>
* <reportRequest>Delivered</reportRequest>
* <reportRequest>Failed</reportRequest>
* <serviceCapability>
* <capabilityId>ChatbotSA</capabilityId>
* <version>+g.gsma.rcs.botversion="#=1"</version>
* </serviceCapability>
* <messageId>5eae954c-42ca-4181-9ab4-9c0ef2e2ac66</messageId>
* <bodyText>Hello
* </bodyText>
* </Message>
* <clientCorrelator>567895</clientCorrelator>
* </Request>
*/
@Test
public void test03(){
RequestDto requestDto = new RequestDto();
requestDto.setAddress("tel:+8619585550103");
List<String> destinationAddress = new ArrayList<String>(2);
destinationAddress.add("tel:+8619585550103");
destinationAddress.add("tel:+8619585550104");
requestDto.setDestinationAddress(destinationAddress);
requestDto.setSenderAddress("sip:12599@botplatform.rcs.chinamobile.com");
MessageDto messageDto = new MessageDto();
messageDto.setContentType("text/plain");
messageDto.setConversationID(UUID.randomUUID().toString());
messageDto.setContributionID(UUID.randomUUID().toString());
List<String> reportRequest = null;
// reportRequest.add("Delivered");
// reportRequest.add("Failed");
messageDto.setReportRequest(reportRequest);
ServiceCapabilityDto serviceCapabilityDto = new ServiceCapabilityDto();
serviceCapabilityDto.setCapabilityId("ChatbotSA");
serviceCapabilityDto.setVersion("+g.gsma.rcs.botversion="#=1"");
messageDto.setServiceCapabilityDto(serviceCapabilityDto);
messageDto.setMessageId(UUID.randomUUID().toString());
messageDto.setBodyText("Hello");
requestDto.setMessageDto(messageDto);
requestDto.setClientCorrelator("567895");
String xml = XmlUtil.generateXml(requestDto);
System.out.println(xml);
}
}
3.测试结果
test01
<?xml version=v1.0 encoding=utf-8?>
<file
xmlns="urn:gsma:params:xml:ns:rcs:rcs:fthttp"
xmlns:x="urn:gsma:params:xml:ns:rcs:rcs:up:fthttpext">
<file-info type="thumbnail">
<file-size>2048</file-size>
<content-type>png</content-type>
</file-info>
<file-info file-disposition="file">
<file-size>1024</file-size>
<file-name>1.jpg</file-name>
<content-type>jpeg</content-type>
</file-info>
</file>
test02
<?xml version=v1.0 encoding=utf-8?>
<msg:outboundMessageRequest
xmlns:msg="urn:oma:xml:rest:netapi:messaging:1">
<messageId>50e1ba7a-dbb5-4f2f-8811-d81859d029ff</messageId>
<clientCorrelator>123456</clientCorrelator>
<bodyText>
<![CDATA[null]]>
</bodyText>
</msg:outboundMessageRequest>
test03
<?xml version=v1.0 encoding=utf-8?>
<Request
xmlns:msg="urn:oma:xml:rest:netapi:messaging:1">
<address>tel:+8619585550103</address>
<destinationAddress>
<phone>tel:+8619585550103</phone>
<phone>tel:+8619585550104</phone>
</destinationAddress>
<senderAddress>sip:12599@botplatform.rcs.chinamobile.com</senderAddress>
<Message>
<contentType>text/plain</contentType>
<conversationID>3869f2e2-aedf-424f-8928-3082dc284104</conversationID>
<contributionID>f9d6b351-a245-4e79-b850-0700114361dd</contributionID>
<reportRequest></reportRequest>
<serviceCapability>
<capabilityId>ChatbotSA</capabilityId>
<version>+g.gsma.rcs.botversion="#=1"</version>
</serviceCapability>
<messageId>35a43af0-df1d-4a0d-94a0-60492e520bc4</messageId>
<bodyText>Hello</bodyText>
</Message>
<clientCorrelator>567895</clientCorrelator>
</Request>
原文始发于微信公众号(爱笑的boy):基于注解生成xml
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/62539.html