前言
在微信订阅号和支付宝生活号日常开发中,我们会涉及到对象和XML之间的相互转换。
比如我们可以利用StringBuilder去直接拼接来构造XML
/**
* 构造基础的响应消息
*
* @return
*/
public static String buildBaseAckMsg(String fromUserId) {
StringBuilder sb = new StringBuilder();
sb.append("");
sb.append(" + fromUserId + "]]>");
sb.append(" + AlipayServiceEnvConstants.APP_ID + "]]>");
sb.append("" + Calendar.getInstance().getTimeInMillis() + "");
sb.append("");
sb.append("");
return sb.toString();
}
作为像我这么懒得程序员,肯定会去找大佬写好的轮子,这就是我和XStream
相遇的契机。下面我们一起走进XStream
;
一.关于 XStream
Xstream 是一个简单的库,用于将对象序列化为 XML 然后再序列化回来。
二.简单入门
2.1 创建要序列化的类
这里有几个简单的类,XStream 可以将这些类的实例转换为 XML,然后再转换回来。
public class Person {
private String firstname;
private String lastname;
private PhoneNumber phone;
private PhoneNumber fax;
// ... constructors and methods
}
public class PhoneNumber {
private int code;
private String number;
// ... constructors and methods
}
注意: 注意这些字段是私有的。Xstream 不关心字段的可见性。不需要getters or setters
。此外,XStream 并不限制你拥有一个默认构造函数
。
2.2 初始化 XStream
引入依赖
<dependency>
<groupId>com.thoughtworks.xstreamgroupId>
<artifactId>xstreamartifactId>
<version>1.4.10version>
dependency>
要使用 XStream,只需实例化 XStream 类:
XStream xstream = new XStream();
2.3.将序列化对象转为xml
让我们创建一个 Person 实例并填充它的字段:
Person person = new Person("Joe", "milo");
person.setPhone(new PhoneNumber(123,"1234-456"));
person.setFax(new PhoneNumber(123,"999-456"));
现在,要将其转换为XML,只需要简单的调用XStream的toXML()
方法
String xml = xstream.toXML(person);
现在,为了使 XStream 输出的 XML 更简洁,可以为 XML 元素名的自定义类名创建别名。这是使用 XStream 所需的惟一映射类型,当然这是可选的。
xstream.alias("person",Person.class);
我们会发现XML变得更简洁
2.4.将XML反序列化对象
首先,我们重写Person
对象的toString()
@Override
public String toString() {
return "Person{" +
"firstname='" + firstname + '\'' +
", lastname='" + lastname + '\'' +
", phone=" + phone +
", fax=" + fax +
'}';
}
要重构一个对象,我们只需调用fromXML()
方法
XStream xstream = new XStream();
xstream.alias("person",Person.class);
//xml字符串
String xmldemo = "<?xml version=\"1.0\" ?>Joemilo123
1234-456123
999-456";
Person o = (Person)xstream.fromXML(xmldemo);
System.out.println(o.toString());
关于更多关于Xstream的操作,大家可以阅读:
https://www.cnblogs.com/jpfss/p/9836465.html
三.高级入门
在高级入门中,我们以支付宝生活号开发为例,采用Xstream的注解开发来完成事件订阅过程中的xml报文相关的操作
3.1 项目搭建
首先我们搭建项目springboot-xstream
,当然你可以在
https://gitee.com/milogenius/milogenius-springboot
找到源代码,案例位于springboot-xstream
模块下面。由于案例代码太多,强烈建议大家下载源代码案例自己跑一跑。
3.2 相关类解释
3.3 和XStream相关的类
1.AlipayXmlMessage
package com.milo.xstream.xml;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamConverter;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.io.InputStream;
import java.io.Serializable;
import java.util.Map;
/**
* 支付宝生活号推送过来的xml消息
* @author milogenius
* @date 2020/4/4 15:29
*
*/
@Data
@Slf4j
@XStreamAlias("XML")
public class AlipayXmlMessage implements Serializable {
private static final long serialVersionUID = -3586245291677274914L;
/**
* 使用dom4j解析的存放所有xml属性和值的map.
*/
private Map allFieldsMap;///// 以下都是支付宝生活号推送过来的消息的xml的element所对应的属性////**AppId*///AppId--->xml中的字段//appId --->pojo中的字段@XStreamAlias("AppId")@XStreamConverter(value = XStreamCDataConverter.class)private String appId;/**用户userid,用户唯一标识*/@XStreamAlias("FromAlipayUserId")@XStreamConverter(value = XStreamCDataConverter.class)private String fromAlipayUserId;/**消息创建时间*/@XStreamAlias("CreateTime")private Long createTime;/**消息类型*/@XStreamAlias("MsgType")@XStreamConverter(value = XStreamCDataConverter.class)private String msgType;/**事件类型*/@XStreamAlias("EventType")@XStreamConverter(value = XStreamCDataConverter.class)private String eventType;/**用户从特定场景关注,带的自定义参数值*/@XStreamAlias("ActionParam")@XStreamConverter(value = XStreamCDataConverter.class)private String actionParam;/**支付宝用户信息*/@XStreamAlias("UserInfo")@XStreamConverter(value = XStreamCDataConverter.class)private String userInfo;/**消息id 用于消息去重*/@XStreamAlias("MsgId")private String msgId;public static AlipayXmlMessage fromXml(String xml) {//修改支付宝生活号变态的消息内容格式,方便解析
xml = xml.replace("<?xml version=\\\"1.0\\\" encoding=\\\"gbk\\\"?>", "");final AlipayXmlMessage xmlMessage = XStreamTransformer.fromXml(AlipayXmlMessage.class, xml);
xmlMessage.setAllFieldsMap(XmlUtils.xml2Map(xml));return xmlMessage;
}public static AlipayXmlMessage fromXml(InputStream is) {return XStreamTransformer.fromXml(AlipayXmlMessage.class, is);
}
}
2.AlipayXmlOutMessage
package com.milo.xstream.outxml;
import com.milo.xstream.builder.AckBuilder;
import com.milo.xstream.xml.XStreamCDataConverter;
import com.milo.xstream.xml.XStreamTransformer;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamConverter;
import lombok.Data;
import java.io.Serializable;
/**
* 响应XML
* @author milogenius
* @date 2020/4/4 11:57
*
*/
@XStreamAlias("xml")
@Data
public abstract class AlipayXmlOutMessage implements Serializable {
private static final long serialVersionUID = -381382011286216263L;
/**接受者userid*/
@XStreamAlias("ToUserId")
@XStreamConverter(value = XStreamCDataConverter.class)
protected String toUserId;
/**支付宝生活号appid*/
@XStreamAlias("AppId")
@XStreamConverter(value = XStreamCDataConverter.class)
protected String appId;
/**创建时间*/
@XStreamAlias("CreateTime")
protected Long createTime;
/**消息类型*/
@XStreamAlias("MsgType")
@XStreamConverter(value = XStreamCDataConverter.class)
protected String msgType;
@XStreamAlias("response")
protected Response response;
@XStreamAlias("sign")
protected String sign;
@XStreamAlias("sign_type")
protected String signType;
/**
* 获得ack builder
* @return
*/
public static AckBuilder ACK() {
return new AckBuilder();
}
@SuppressWarnings("unchecked")
public String toXml() {
StringBuilder builder = new StringBuilder();
String xml = XStreamTransformer.toXml((Class) this.getClass(), this);
builder.append("<?xml version=\"1.0\" encoding=\"gbk\"?>\n");
builder.append(xml);return builder.toString();
}
}
3.XStreamCDataConverter
package com.milo.xstream.xml;
import com.thoughtworks.xstream.converters.basic.StringConverter;
/**
* 自定义转换器
* @author milogenius
* @date 2020/4/4 10:56
*
*/
public class XStreamCDataConverter extends StringConverter {
@Override
public String toString(Object obj) {
return " + super.toString(obj) + "]]>";
}
}
相关注解说明
@XStreamAlias
用于定义XStream
类或字段别名的注释
@XStreamConverter
用于声明转换器的注释
3.4 测试
XmlDemoTest
package com.milo.xstream;
import com.milo.xstream.outxml.AlipayXmlOutMessage;
import com.milo.xstream.xml.AlipayXmlMessage;
/**
* @author milogenius
* @date 2020-04-04 11:49
*/
public class XmlDemoTest {
public static void main(String[] args) {
//xml --->pojo
String bizContent = "\n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
"";
AlipayXmlMessage alipayXmlMessage = AlipayXmlMessage.fromXml(bizContent);
// System.out.println(alipayXmlMessage);
//pojo --->xml
AlipayXmlOutMessage mpXmlOutMessage = AlipayXmlOutMessage.ACK().toUserId("123456").appId("99999999").build();
String xml = mpXmlOutMessage.toXml();
System.out.println(xml);
}
}
3.5测试结果
3.5.1 xml ---->pojo
3.5.2 pojo --->xml
四.总结
通过上面的一些小案例,我们学习Xstream的基本用法和注解用法,文章到此为止,谢谢大家阅读;
END
Java面试题专栏
【61期】MySQL行锁和表锁的含义及区别(MySQL面试第四弹)【62期】解释一下MySQL中内连接,外连接等的区别(MySQL面试第五弹)【63期】谈谈MySQL 索引,B+树原理,以及建索引的几大原则(MySQL面试第六弹)【64期】MySQL 服务占用cpu 100%,如何排查问题? (MySQL面试第七弹)【65期】Spring的IOC是啥?有什么好处?【66期】Java容器面试题:谈谈你对 HashMap 的理解【67期】谈谈ConcurrentHashMap是如何保证线程安全的?【68期】面试官:对并发熟悉吗?说说Synchronized及实现原理【69期】面试官:对并发熟悉吗?谈谈线程间的协作(wait/notify/sleep/yield/join)【70期】面试官:对并发熟悉吗?谈谈对volatile的使用及其原理欢迎长按下图关注公众号后端技术精选