首先,微信公众平台发送被动响应消息接口官网wiki:
如,回复文本消息必须是如下格式:
<xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>12345678</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[你好]]></Content> </xml>
<xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>12345678</CreateTime> <MsgType><![CDATA[image]]></MsgType> <Image> <MediaId><![CDATA[media_id]]></MediaId> </Image> </xml>
下面讲下笔者这里的处理方法-->
首先,有一个rsp模板类:
/** * 被动响应消息模板基类,其他Msg类需extends其 * 注:Msg类属性命名方式未遵循驼峰模式,因为需要和微信接口定义保持一致。 * @author will_awoke * @version 2014-6-24 * @see TemplateRspMsg * @since */ public class TemplateRspMsg { // 响应消息时,接收方帐号(收到的OpenID) protected String ToUserName; // 响应消息时,开发者微信号(公众平台原始ID) protected String FromUserName; // 消息创建时间 Long型 protected String CreateTime; // 消息类型 protected String MsgType; /** * 构造器 */ public TemplateRspMsg() { } //setter getter public String getMsgType() { return MsgType; } //protected for msgTypeSetter protected void setMsgType(String msgType) { MsgType = msgType; } public String getFromUserName() { return FromUserName; } public void setFromUserName(String fromUserName) { FromUserName = fromUserName; } public String getToUserName() { return ToUserName; } public void setToUserName(String toUserName) { ToUserName = toUserName; } public String getCreateTime() { return CreateTime; } public void setCreateTime(String createTime) { CreateTime = createTime; } }
文本类回复消息类,extends 模板类:
/** * 文本类响应消息 MsgType=text * @author will_awoke * @version 2014-6-24 * @see TextRspMsg * @since */ public class TextRspMsg extends TemplateRspMsg { //文本消息内容 private String Content; /** * 构造器,同时初始化MsgType */ public TextRspMsg() { setMsgType("text"); } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("TextRspMsg [Content=").append(Content).append(",ToUserName=").append( ToUserName).append(",FromUserName=").append(FromUserName).append(",CreateTime=").append( CreateTime).append(",MsgType=").append(MsgType).append("]"); return builder.toString(); } //setter getter public String getContent() { return Content; } public void setContent(String content) { Content = content; } }
import java.util.HashMap; import java.util.Map; /** * 图片类型客服消息 * @author will_awoke * @version 2014-6-25 * @see ImageRspMsg * @since */ public class ImageRspMsg extends TemplateRspMsg { //用于拼装形如 <Image><MediaId><![CDATA[media_id]]></MediaId></Image>专有属性 private Map<String,Object> Image; /** * 构造器,同时初始化MsgType */ public ImageRspMsg() { setMsgType("image"); } /** * 格式化成符合微信API要求的实体 * @param ToUserName 普通用户openid * @param FromUserName 开发者微信号(公众平台原始ID) * @param MediaId 通过上传多媒体文件,得到的id。 * @return 符合API要求的实体 * @see */ public ImageRspMsg xmlFormatBean(String ToUserName,String FromUserName,String MediaId) { this.setToUserName(ToUserName); this.setFromUserName(FromUserName); //由于微信服务端需要的时间整形是以秒为单位的,故需要除以1000L this.setCreateTime(String.valueOf(System.currentTimeMillis() / 1000L)); Map<String,Object> map = new HashMap<String,Object>(); map.put("MediaId",MediaId); this.setImage(map); return this; } //setter getter public Map<String,Object> getImage() { return Image; } public void setImage(Map<String,Object> image) { Image = image; } }
下面是核心工具类:
@H_301_61@import java.lang.reflect.Field; import java.util.Iterator; import java.util.Map; import org.apache.commons.lang.StringUtils; import com.xx.wechat.model.rsp.ImageRspMsg; import com.xx.wechat.model.rsp.TextRspMsg; /** * * 将被动响应消息实体类转换成符合微信接口API要求的xml格式。 * 如: * <xml> * <ToUserName><![CDATA[toUser]]></ToUserName> * <FromUserName><![CDATA[fromUser]]></FromUserName> * <CreateTime>12345678</CreateTime> * <MsgType><![CDATA[image]]></MsgType> * <Image> * <MediaId><![CDATA[media_id]]></MediaId> * </Image> * </xml> * 注意:根节点是xml而非实体名,且无xml头文件,每个节点的首字母是大写。 * 而此功能是JOX转换不具有的,故造此轮子工具类 * 另请参见:wechat.model.rsp包 * * @author will_awoke * @version 2014-6-25 * @see Bean2WeChatXml * @since */ public class Bean2WeChatXml { /** * 将实体类转换成符合微信接口API要求的xml格式 * @param obj * @return * @throws IllegalArgumentException * @throws IllegalAccessException * @see */ @SuppressWarnings("rawtypes") public static String beanToXML(Object obj) throws IllegalArgumentException,IllegalAccessException { StringBuffer sb = new StringBuffer("<xml>\n"); Class clazz = obj.getClass(); Field[] fields = clazz.getDeclaredFields(); //父类Class Class superClass = clazz.getSuperclass(); Field[] superFields = superClass.getDeclaredFields(); //拼装自身的字段和字段值 String fieldName = null; Object mapObj = null; for (Field field : fields) { field.setAccessible(true); fieldName = field.getName();//获得字段名 mapObj = field.get(obj);//获得字段值 //如果字段是Map类型,形如ImageRspMsg类中Map字段 if (mapObj instanceof Map) { //迭代map集合 StringBuffer mapFieldValue = new StringBuffer(""); String key = ""; Map castMap = (Map)mapObj; Iterator iterator = castMap.keySet().iterator(); while (iterator.hasNext()) { //迭代 key = (String)iterator.next(); mapFieldValue.append("<").append(key).append(">"); //调用value的toString方法 mapFieldValue.append("<![CDATA[").append(castMap.get(key).toString()).append("]]>"); mapFieldValue.append("</").append(key).append(">\n"); } sb.append("<").append(fieldName).append(">\n"); sb.append(mapFieldValue); //map集合内的迭代结果,勿加CDATA sb.append("</").append(fieldName).append(">\n"); } //字段非Map类型,则按照String类型处理(获得value时直接调用toString方法) else { sb.append("<").append(fieldName).append(">"); sb.append("<![CDATA[").append(mapObj.toString()).append("]]>"); sb.append("</").append(fieldName).append(">\n"); } } //拼装父类的字段和字段值 String superFieldName = ""; for (Field field : superFields) { field.setAccessible(true); superFieldName = field.getName(); sb.append("<").append(superFieldName).append(">"); sb.append("<![CDATA[").append(field.get(obj).toString()).append("]]>"); sb.append("</").append(superFieldName).append(">\n"); } sb.append("</xml>"); return sb.toString(); } /** * @param args * @see */ public static void main(String[] args) throws Exception { TextRspMsg rsp = new TextRspMsg(); rsp.setToUserName("ToUserName"); rsp.setFromUserName("FromUserName"); rsp.setCreateTime(String.valueOf(System.currentTimeMillis())); rsp.setContent("您好!welcome to subscribe will's public ..."); System.out.println(JoxUtil.beanToXML(rsp)); System.out.println(beanToXML(rsp)); ImageRspMsg image = new ImageRspMsg(); image.xmlFormatBean("ToUserName","FromUserName","MediaId"); System.out.println("\n" + beanToXML(image)); } }这个是console输出结果:
<?xml version="1.0" encoding="UTF-8"?> <TextRspMsg> <content>您好!welcome to subscribe will's public ...</content> <createTime>1404286867859</createTime> <fromUserName>FromUserName</fromUserName> <msgType>text</msgType> <toUserName>ToUserName</toUserName> </TextRspMsg> <xml> <Content><![CDATA[您好!welcome to subscribe will's public ...]]></Content> <ToUserName><![CDATA[ToUserName]]></ToUserName> <FromUserName><![CDATA[FromUserName]]></FromUserName> <CreateTime><![CDATA[1404286867859]]></CreateTime> <MsgType><![CDATA[text]]></MsgType> </xml> <xml> <Image> <MediaId><![CDATA[MediaId]]></MediaId> </Image> <ToUserName><![CDATA[ToUserName]]></ToUserName> <FromUserName><![CDATA[FromUserName]]></FromUserName> <CreateTime><![CDATA[1404286868]]></CreateTime> <MsgType><![CDATA[image]]></MsgType> </xml>
可以看到传统的JOX转换来的xml是有header部分的,且多了个TextRspMsg节点,子节点都是小写(这里,JOX内部在转换为xml的时候会遵循Java命名格式,将所有的属性的首字符改成小写,所以,即使把实体类的字段属性故意写成大写也不行。)而用Bean2WeChatXml工具类转化来的xml就能完全满足API格式要求了,这也就是造这个轮子的意义所在。
完毕。