前言
这篇文章介绍微信订阅号消息接口中,XML对象的封装和解析。希望能给大家带来帮助,微信开发这块也不是很熟悉,只限跑通简单示例,懂行大佬轻拍。下面会详细介绍包括哪些内容。
介绍
对象主要包括两类,普通消息和推送消息。以及一个通用的转换工具类,类似fastjson的工具类。 为简单起见这里把语音,文本,图片等各自特有的字段都整合到一个对象里,方便使用,缺点是字段显得多些。要补充的是,代码只限跑通例子,对于性能,规范等方面没有做过多的考虑。其中推送消息类有问题,无法使用,普通消息解析是正常的,可以用以下对应的单测例子debug校验。
代码
接口对象
//消息对象类
@Data //lombok 注解
@XmlAccessorType(XmlAccessType.FIELD) //必须
@XmlRootElement(name = "xml")
public class WxMsg {
@XmlElement(name = "ToUserName")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String toUserName; //接收方微信号
@XmlElement(name = "FromUserName")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String fromUserName;//发送方微信号,若为普通用户,则是一个OpenID
@XmlElement(name = "CreateTime")
private Long createTime;//消息创建时间 (整型)
@XmlElement(name = "MsgType")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String msgType;//video voice text image location link
@XmlElement(name = "PicUrl")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String picUrl; // 图片链接(由系统生成)
@XmlElement(name = "Content")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String content; //文本消息内容
@XmlElement(name = "MediaId")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String mediaId;//视频消息媒体id,可以调用获取临时素材接口拉取数据。
@XmlElement(name = "Format")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String format; //语音格式:amr speex等
@XmlElement(name = "Recognition")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String recognition; // 语音识别结果,UTF8编码
@XmlElement(name = "ThumbMediaId")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String thumbMediaId;//视频消息缩略图的媒体id,可以调用多媒体文件下载接口拉取数据。
@XmlElement(name = "Title")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String title; //链接消息标题
@XmlElement(name = "Description")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String description; //链接消息描述
@XmlElement(name = "Url")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String url; //链接消息link
@XmlElement(name = "Location_X")
private String locationX; //地理位置纬度
@XmlElement(name = "Location_Y")
private String locationY; //地理位置经度
@XmlElement(name = "Scale")
private Integer scale; //地理精度
@XmlElement(name = "Label")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String label; //地理位置详情
@XmlElement(name = "ArticleCount")
private Integer articleCount; //图文消息文章数量
@XmlElement(name = "MsgId")
private Long msgId; //消息id,64位整型
/**
* 附加类型
**/
@XmlElement(name = "Music")
private WxMusic music;
@XmlElement(name = "Voice")
private WxVoice wxVoice;
@XmlElement(name = "Video")
private WxVideo wxVideo;
@XmlElement(name = "Image")
private WxImage wxImage;
@XmlElement(name = "Articles")
private WxNews articles;
@Getter
@AllArgsConstructor
public enum MsgType {
UnKnow(-1, "unknow", "未知类型"),
Text(1, "text", "文本"),
Voice(2, "voice", "语音"),
Image(3, "image", "图像"),
Location(4, "location", "位置"),
Link(5, "link", "链接"),
Event(6, "event", "推送"),
Video(7, "video", "视频"),
Music(8, "music", "音乐"),
News(9, "news", "图文");
private int code;
private String key;
private String desc;
public static MsgType fromKey(String key) {
return Arrays.stream(MsgType.values()).filter(e -> e.key.equals(key)).findFirst().orElse(MsgType.UnKnow);
}
}
}
//推送消息类
@EqualsAndHashCode(callSuper = true)
@Data
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "xml")
public class WxEventMsg extends WxMsg {
@XmlElement(name = "Event")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String event; //事件类型,subscribe(订阅)、unsubscribe(取消订阅) 事件类型,LOCATION 菜单 CLICK 跳转 VIEW
@XmlElement(name = "EventKey")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String eventKey; //未关注:事件KEY值,qrscene_为前缀,后面为二维码的参数值 , 已关注:事件KEY值,是一个32位无符号整数,即创建二维码时的二维码scene_id 菜单:事件KEY值,与自定义菜单接口中KEY值对应 ; 跳转: 配置的url
@XmlElement(name = "Ticket")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String ticket; //二维码的ticket,可用来换取二维码图片
@XmlElement(name = "Latitude")
private String latitude; //地理位置纬度
@XmlElement(name = "Longitude")
private String longitude; //地理位置经度
@XmlElement(name = "Precision")
private String precision; //地理位置精度 浮点
@XmlElement(name = "ScanCodeInfo")
private WxScan scanCodeInfo;
@XmlElement(name = "MenuId")
private String menuId; //菜单ID,如果是个性化菜单,则可以通过这个字段,知道是哪个规则的菜单被点击了
@Getter
@AllArgsConstructor
public enum EventType {
UnKnow(-1, "unkown", "未知类型"),
Subscribe(1, "subscribe", "加入订阅"),
Unsubscribe(2, "unsubscribe", "取消订阅"),
Location(3, "LOCATION", "位置"),
MenuClick(4, "CLICK", "菜单点击"),
ViewClick(5, "VIEW", "链接点击"),
ScanPush(6, "scancode_push", "扫码"),
ScanWaitMsg(7, "scancode_waitmsg", "扫码推事件且弹出“消息接收中”提示框的事件推送"),
PicSysPhoto(8, "pic_sysphoto", "系统拍照"),
ViewMiniProgram(9, "view_miniprogram", "点击菜单跳转小程序的事件推送"),
PicWeixin(10, "pic_weixin", "弹出微信相册发图器的事件推送"),
;
private int code;
private String key;
private String desc;
}
}
//几个扩展消息
@Data
@XmlAccessorType(XmlAccessType.FIELD)
public class WxImage {
@XmlElement(name = "MediaId")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String mediaId;
}
@Data
@XmlAccessorType(XmlAccessType.FIELD)
public class WxMusic {
@XmlElement(name = "Title")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String title; //音乐标题
@XmlElement(name = "Description")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String description; //音乐描述
@XmlElement(name = "MusicUrl")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String musicUrl; //音乐链接
@XmlElement(name = "HQMusicUrl")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String hqMusicUrl; //高质量音乐链接,WIFI环境优先使用该链接播放音乐
@XmlElement(name = "ThumbMediaId")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String thumbMediaId; //缩略图的媒体id,通过素材管理中的接口上传多媒体文件,得到的id
}
@Data
@XmlAccessorType(XmlAccessType.FIELD)
public class WxNews {
@XmlElement(name = "item")
List<Item> item;
@Data
@XmlAccessorType(XmlAccessType.FIELD)
public static class Item {
@XmlElement(name = "Title")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String title; //图文消息标题
@XmlElement(name = "Description")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String description; //图文消息描述
@XmlElement(name = "PicUrl")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String picUrl; //图片链接,支持JPG、PNG格式,较好的效果为大图360*200,小图200*200
@XmlElement(name = "Url")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String url; //点击图文消息跳转链接
}
}
@Data
public class WxScan {
@XmlElement(name = "ScanType")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String scanType; //扫描结果,即二维码对应的字符串信息
@XmlElement(name = "ScanResult")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String scanResult; //扫描结果,即二维码对应的字符串信息
}
@Data
@XmlAccessorType(XmlAccessType.FIELD)
public class WxVideo {
@XmlElement(name = "MediaId")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String mediaId; //通过素材管理中的接口上传多媒体文件,得到的id
@XmlElement(name = "Title")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String title; //视频消息的标题
@XmlElement(name = "Description")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String description; //视频消息的描述
}
@Data
@XmlAccessorType(XmlAccessType.FIELD)
public class WxVoice {
@XmlElement(name = "MediaId")
@XmlJavaTypeAdapter(XmlUtil.CDATAAdapter.class)
private String mediaId;
}
封装XML解析类
public class XmlUtil {
public static <T> T parseXml(String input, Class<T> tClass) {
try {
JAXBContext jaxbContext = JAXBContext.newInstance(tClass);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
StringReader reader = new StringReader(input);
T obj = (T) unmarshaller.unmarshal(reader);
return obj;
} catch (Exception ignore) {
return null;
}
}
//核心都是使用的jdk自带 rt.jar
public static <T> String toXml(T obj) {
try {
JAXBContext jaxbContext = JAXBContext.newInstance(obj.getClass());
Marshaller malshaller = jaxbContext.createMarshaller();
StringWriter writer = new StringWriter();
malshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
malshaller.setProperty("com.sun.xml.internal.bind.characterEscapeHandler",
(CharacterEscapeHandler) (ch, start, length, isAttVal, writer1) -> writer1.write(ch, start, length)); //不转义内容 如amp;, 可以取消这个参数试试
malshaller.setProperty("com.sun.xml.internal.bind.xmlDeclaration", false);//去除 <?xml version="1.0" encoding="UTF-8" ?> 头
malshaller.marshal(obj, writer);
return writer.toString();
} catch (Exception ignore) {
return null;
}
}
//CDATA 格式内容的处理
public static class CDATAAdapter extends XmlAdapter<String, String> {
@Override
public String marshal(String v) throws Exception {
return "<![CDATA[" + v + "]]>";
}
@Override
public String unmarshal(String v) throws Exception {
return v;
}
}
}
测试例子
@Test
public void xmlTest() {
String data = "<xml>\n"
+ " <ToUserName><![CDATA[toUser]]></ToUserName>\n"
+ " <FromUserName><![CDATA[fromUser]]></FromUserName>\n"
+ " <CreateTime>1348831860</CreateTime>\n"
+ " <MsgType><![CDATA[image]]></MsgType>\n"
+ " <PicUrl><![CDATA[this is a url]]></PicUrl>\n"
+ " <MediaId><![CDATA[media_id]]></MediaId>\n"
+ " <MsgId>1234567890123456</MsgId>\n"
+ "</xml>\n"
+ "\n";
WxMsg msg = XmlUtil.parseXml(data, WxMsg.class);
System.out.println(msg);
}
@Test
public void xmlStringTest() {
WxMsg msg = new WxMsg();
msg.setCreateTime(222244L);
msg.setPicUrl("hsdgfsdgsdgsdg");
System.out.println(XmlUtil.toXml(msg));
}
@Test
public void wxNewsTest() {
String news = "<xml>\n"
+ " <ToUserName><![CDATA[toUser]]></ToUserName>\n"
+ " <FromUserName><![CDATA[fromUser]]></FromUserName>\n"
+ " <CreateTime>12345678</CreateTime>\n"
+ " <MsgType><![CDATA[news]]></MsgType>\n"
+ " <ArticleCount>1</ArticleCount>\n"
+ " <Articles>\n"
+ " <item>\n"
+ " <Title><![CDATA[sdgsdngaosdng]]></Title>\n"
+ " <Description><![CDATA[fsdfsdgsdgsdg]]></Description>\n"
+ " <PicUrl><![CDATA[sdnongsd]]></PicUrl>\n"
+ " <Url><![CDATA[sdnobvnsdiong]]></Url>\n"
+ " </item>\n"
+ " <item>\n"
+ " <Title><![CDATA[sdgsdngaosdng]]></Title>\n"
+ " <Description><![CDATA[fsdfsdgsdgsdg]]></Description>\n"
+ " <PicUrl><![CDATA[sdnongsd]]></PicUrl>\n"
+ " <Url><![CDATA[sdnobvnsdiong]]></Url>\n"
+ " </item>\n"
+ " </Articles>\n"
+ "</xml>";
WxMsg msg = XmlUtil.parseXml(news, WxMsg.class);
System.out.println(msg);
WxNews.Item news1 = new WxNews.Item();
WxNews wxNews = new WxNews();
news1.setDescription("fsdfsdgsdgsdg");
news1.setTitle("sdgsdngaosdng");
news1.setUrl("sdnobvnsdiong");
news1.setPicUrl("sdnongsd");
msg.setArticles(wxNews);
wxNews.setItem(Lists.newArrayList(news1,news1));
System.out.println(XmlUtil.toXml(msg));
}
结语
至此结束。再次补充下,代码只限跑通例子,对于性能,规范等方面没有做过多的考虑,有类似需求的可以加以修改优化;如果使用maven打包,报找不到rt.jar问题,按如下补充pom.xml配置:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<compilerArgs>
<arg>-XDignore.symbol.file</arg>
</compilerArgs>
<fork>true</fork>
</configuration>
</plugin>
谢谢。
网友评论