当前位置:网站首页>基于注解和反射生成xml
基于注解和反射生成xml
2022-06-10 17:23:00 【llp1110】
基于注解和反射生成xml
大家好,我是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方式
[email protected]
/** * 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";
}
[email protected]
/** * xml属性注解 * @Target(ElementType.FIELD) 修饰字段 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XmlPropertyELement {
/** * 别名 */
String name() default "";
/** * 命名空间 */
String namespace() default "";
/** * 是否解析空值 */
boolean analysisEmpty() default false;
}
[email protected]
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XmlBeanElement {
/** * 别名 */
String name() default "";
/** * 命名空间 */
String namespace() default "";
/** * 是否解析空值 * @return */
boolean analysisEmpty() default false;
}
[email protected]
/** * CDATA区 * <![CDATA[ * 这里可以把你输入的字符原样显示,不会解析 xml * ]]> * 1.因为这里设计@XmlCDATA是配合@XmlPropertyELement注解进行使用 * 是否解析空值、命名空间、别名等交由@XmlPropertyELement控制 * [email protected]注解只关注于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> */
}
[email protected]
@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;
}
[email protected]
@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:[email protected]</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:[email protected]");
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:[email protected]</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>
边栏推荐
猜你喜欢

PCA principal component analysis tutorial (origin analysis & drawing, without R language)

mmdetection之dataset类解读

训练时添加进度条的库--tqdm

numpy——记录

IP总结(TCP/IP卷1和卷2)

踩坑了,BigDecimal 使用不当,造成P0事故!

THE LOTTERY TICKET HYPOTHESIS: FINDING SPARSE, TRAINABLE NEURAL NETWORKS论文笔记

mmcv之Config类介绍

Abbexa 8-OHdG CLIA 试剂盒解决方案

Abbkine柱式法ExKine Pro动物细胞/组织总蛋白提取试剂盒
随机推荐
mmdetection之dataloader构建
pwnable start
mmcv之Registry类解读
The short ticket hypothesis: finding sparse, trainable neural networks
MMdetection之build_optimizer模块解读
sense of security
2022 version of idea graphical interface GUI garbled code solution super detailed simple version
蓝桥杯_挑选子串_组合数学_乘法原理_ / 尺取法
绘制混淆矩阵
Feign based remote call
Jouer avec la classe de fonctions de pytorch
Can the "no password era" that apple is looking forward to really come true?
踩坑了,BigDecimal 使用不当,造成P0事故!
力扣 20. 有效的括号
5年后,你将如何融入20万亿美元的「项目经济」
Leetcode String to integer(Atoi)
2022上半年信息系统项目管理师论文真题
Summary of vim common commands
Classic topics of leetcode tree (I)
系统需要把所有文件扫描一遍,并尝试识别视频的封面