微信公众号(小程序)事件推送数据xml处理

226 阅读4分钟

前因

由于微信服务器回调项目应用传输的数据是xml,因此需要将其转换为对象进行操作

<xml>
   <ToUserName><![CDATA[toUser]]></ToUserName>
   <FromUserName><![CDATA[fromUser]]></FromUserName>
   <CreateTime>1482048670</CreateTime>
   <MsgType><![CDATA[text]]></MsgType>
   <Content><![CDATA[this is a test]]></Content>
   <MsgId>1234567890123456</MsgId>
</xml>

整合接收对象所需要保存的字段

import lombok.Data;

import java.util.List;

/**
* @package: 
* @className: ReceiveXmlEntity
* @Description:
* @author: Created By Lg
* @date: 2023-02-21 17:26
*/
@Data
    public class ReceiveXmlEntity {
        /**
* 开发者微信号
*/
        private String ToUserName;
        /**
* 发送方帐号(一个OpenID)
*/
        private String FromUserName;
        /**
* 信息
*/
        private String Content;
        /**
* 图片链接(由系统生成)
*/
        private String PicUrl;
        /**
* 语音消息媒体id,可以调用获取临时素材接口拉取数据
*/
        private String MediaId;
        /**
* 语音格式,如amr,speex等
*/
        private String Format;
        /**
* 语音识别结果,UTF8编码
*/
        private String Recognition;
        /**
* 视频消息缩略图的媒体id,可以调用多媒体文件下载接口拉取数据。
*/
        private String ThumbMediaId;
        /**
* 消息创建时间 (整型)
*/
        private String CreateTime;
        /**
* 消息类型,event
*/
        private String MsgType;
        /**
* 消息id,64位整型
*/
        private String MsgId;
        /**
* 消息的数据ID(消息如果来自文章时才有)
*/
        private String MsgDataId;
        /**
* 多图文时第几篇文章,从1开始(消息如果来自文章时才有)
*/
        private String Idx;
        /**
* 事件类型,subscribe(订阅)、unsubscribe(取消订阅)
*/
        private String Event;
        /**
* 事件 KEY 值,qrscene_为前缀,后面为二维码的参数值
*/
        private String EventKey;
        /**
* 二维码的ticket,可用来换取二维码图片
*/
        private String Ticket;
        /**
* 地理位置纬度
*/
        private String Latitude;
        /**
* 地理位置经度
*/
        private String Longitude;
        /**
* 地理位置精度
*/
        private String Precision;
        /**
* 地理位置维度
*/
        private String Location_X;
        /**
* 地理位置经度
*/
        private String Location_Y;
        /**
* 地图缩放大小
*/
        private String Scale;
        /**
* 地理位置信息
*/
        private String Label;

        /**
* 消息标题
*/
        private String Title;
        /**
* 消息描述
*/
        private String Description;
        /**
* 消息链接
*/
        private String Url;
        /**
* 模板消息返回结果状态
*/
        private String Status;
        /**
* 群发文章id
*/
        private String ArticleId;
        /**
* 群发消息url
*/
        private String ArticleUrl;
        /**
*以上是微信服务号相关参数
*/

        /**
* 以下是小程序相关参数
*/

        /**
* 订阅消息事件
*/
        private List<SubscribeMsgEventEntity> SubscribeMsgPopupEvent;
        /**
* 当用户在手机端服务通知里消息卡片右上角“...”管理消息时,
* 相应的行为事件会推送至开发者所配置的服务器地址或微信云托管服务。(目前只推送取消订阅的事件,即对消息设置“拒收”)
*/
        private List<SubscribeMsgEventEntity> SubscribeMsgChangeEvent;

        /**
* 调用订阅消息接口发送消息给用户的最终结果,会推送下发结果事件至开发者所配置的服务器地址或微信云托管服务
*/
        private List<SubscribeMsgEventEntity> SubscribeMsgSentEvent;
        /**
* 小程序页面地址
*/
        private String PagePath;

        private WeixinNewsBean WeixinNewsBean;
    }

将接收到的xml数据转换为ReceiveXmlEntity对象

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.*;

/**
 * @package: 
 * @className: WxXmlUtil
 * @Description: 微信xml数据转换
 * @author: Created By Lg
 * @date: 2023-02-21 20:35
 */
@Slf4j
public class WxXmlUtil {



    /**
     * 解析微信xml消息
     *
     * @param strXml
     * @return
     */
    public static ReceiveXmlEntity xmlToEntity(String strXml) {
        ReceiveXmlEntity entity = null;
        try {
            if (StrUtil.isBlank(strXml)) {
                return null;
            }
            // 将字符串转化为XML文档对象
            Document document = DocumentHelper.parseText(strXml);
            // 获得文档的根节点
            Element root = document.getRootElement();
            // 遍历根节点下所有子节点
            Iterator<?> iter = root.elementIterator();
            // 遍历所有结点
            entity = new ReceiveXmlEntity();
            //利用反射机制,调用set方法
            //获取该实体的元类型
            Class<?> c = ReceiveXmlEntity.class;
            entity = (ReceiveXmlEntity) c.newInstance();//创建这个实体的对象
            while (iter.hasNext()) {
                Element ele = (Element) iter.next();
                //获取set方法中的参数字段(实体类的属性)
                Field field = c.getDeclaredField(ele.getName());
                //获取set方法,field.getType())获取它的参数数据类型
                Method method = c.getDeclaredMethod("set" + ele.getName(), field.getType());
                String typeName = field.getType().getTypeName();
                if (typeName.startsWith("java.lang") || typeName.startsWith("java.util")) {
                    String simpleName = field.getType().getSimpleName();
                    if (simpleName.equals("List")) {
                        dealListData(entity, field, ele, method);
                    } else {
                        //调用set方法
                        method.invoke(entity, ele.getText());
                    }
                } else {
                    dealEntityData(entity, field, ele, method);
                }
            }
        } catch (Exception e) {
            // TODO: handle exception
            log.error("=xmlToEntity=xml 格式异常:{}", strXml);
            log.error("=xmlToEntity= 格式异常:", e);
        }
        return entity;
    }


    /**
     * 处理实体类中有List类型字段
     *
     * @param entity
     * @param field
     * @param ele
     * @param method
     * @throws Exception
     */
    public static void dealListData(ReceiveXmlEntity entity, Field field, Element ele, Method method) {
        try {
            ParameterizedTypeImpl type = (ParameterizedTypeImpl) field.getAnnotatedType().getType();
            Type[] types = type.getActualTypeArguments();
            Class<?> cSun = (Class<?>) types[0];
            List<Element> elements = ele.elements();
            List list = new ArrayList<>();
            elements.forEach(a -> {
                // 遍历根节点下所有子节点
                Iterator<?> iterSun = a.elementIterator();
                Map map = new HashMap<>();
                while (iterSun.hasNext()) {
                    Element eleSun = (Element) iterSun.next();
                    map.put(eleSun.getName(), eleSun.getText());
                }
                list.add(JSONUtil.toBean(JSONUtil.toJsonStr(map), cSun));
            });
            //调用set方法
            method.invoke(entity, list);
        } catch (Exception e) {
            log.error("====dealListData异常", e);
        }
    }


    /**
     * 处理实体类中有其他实体类类型数据
     *
     * @param entity
     * @param field
     * @param ele
     * @param method
     * @throws Exception
     */
    public static void dealEntityData(ReceiveXmlEntity entity, Field field, Element ele, Method method) {
        try {
            Class<?> cSun = field.getType();
            // 遍历根节点下所有子节点
            Iterator<?> iterSun = ele.elementIterator();
            Map map = new HashMap<>();
            while (iterSun.hasNext()) {
                Element eleSun = (Element) iterSun.next();
                map.put(eleSun.getName(), eleSun.getText());
            }
            //调用set方法
            method.invoke(entity, JSONUtil.toBean(JSONUtil.toJsonStr(map), cSun));
        } catch (Exception e) {
            log.error("====dealEntityData", e);
        }
    }
}

获取XML中固定对象(根据对象名获取)

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import java.util.*;

/**
 * @package: 
 * @className: XmlToObjUtil
 * @Description: 微信xml数据转换
 * @author: Created By Lg
 * @date: 2023-02-21 20:35
 */
@Slf4j
public class XmlToObjUtil {

    /**
     * 获取xml 数组对象
     *
     * @param strXml    xml数据
     * @param filedName 对象名称
     * @param c         对象类型class
     * @return
     */
    public static JSONArray getListByXml(String strXml, String filedName, Class<?> c) {
        List list = new ArrayList<>();
        try {
            if (StrUtil.isBlank(strXml)) {
                return null;
            }
            // 将字符串转化为XML文档对象
            Document document = DocumentHelper.parseText(strXml);
            // 获得文档的根节点
            Element root = document.getRootElement();
            // 遍历根节点下所有子节点
            Iterator<?> iter = root.elementIterator();
            while (iter.hasNext()) {
                Element ele = (Element) iter.next();
                if (ele.getName().equals(filedName)) {
                    List<Element> elements = ele.elements();
                    elements.forEach(a -> {
                        // 遍历根节点下所有子节点
                        Iterator<?> iterSun = a.elementIterator();
                        Map map = new HashMap<>();
                        while (iterSun.hasNext()) {
                            Element eleSun = (Element) iterSun.next();
                            map.put(eleSun.getName(), eleSun.getText());
                        }
                        list.add(JSONUtil.toBean(JSONUtil.toJsonStr(map), c));
                    });
                }
            }
        } catch (Exception e) {
            log.error("=getListByXml=xml :{}", strXml);
            log.error("=getListByXml= 格式异常:", e);
        }
        return JSONUtil.parseArray(JSONUtil.toJsonStr(list));
    }


    /**
     * 获取xml 数组对象
     *
     * @param strXml    xml数据
     * @param filedName 对象名称
     * @param T         对象类型class
     * @return
     */
    public static JSONObject getObjByXml(String strXml, String filedName, Class<?> T) {
        try {
            if (StrUtil.isBlank(strXml)) {
                return null;
            }
            // 将字符串转化为XML文档对象
            Document document = DocumentHelper.parseText(strXml);
            // 获得文档的根节点
            Element root = document.getRootElement();
            // 遍历根节点下所有子节点
            Iterator<?> iter = root.elementIterator();
            while (iter.hasNext()) {
                Element ele = (Element) iter.next();
                if (ele.getName().equals(filedName)) {
                    // 遍历根节点下所有子节点
                    Iterator<?> iterSun = ele.elementIterator();
                    JSONObject object = new JSONObject();
                    while (iterSun.hasNext()) {
                        Element eleSun = (Element) iterSun.next();
                        object.set(eleSun.getName(), eleSun.getText());
                    }
                    return object;
                }
            }
        } catch (Exception e) {
            log.error("=getObjByXml=xml :{}", strXml);
            log.error("=getObjByXml= 格式异常:", e);
        }
        return new JSONObject();
    }

    /**
     * 获取xml 数组对象
     *
     * @param strXml     xml数据
     * @param StringName 对象名称
     * @return
     */
    public static String getStringByXml(String strXml, String StringName) {
        try {
            if (StrUtil.isBlank(strXml)) {
                return null;
            }
            // 将字符串转化为XML文档对象
            Document document = DocumentHelper.parseText(strXml);
            // 获得文档的根节点
            Element root = document.getRootElement();
            // 遍历根节点下所有子节点
            Iterator<?> iter = root.elementIterator();
            while (iter.hasNext()) {
                Element ele = (Element) iter.next();
                if (ele.getName().equals(StringName)) {
                    return ele.getText();
                }
            }
        } catch (Exception e) {
            log.error("=getStringByXml=xml :{}", strXml);
            log.error("=getStringByXml= 格式异常:", e);
        }
        return "";
    }

备注:目前只处理了string类型、List类型、以及自定义对象(对象里没有其他对象字段)字段的赋值,如果ReceiveXmlEntity对象中有其他类型字段,只需要判断重新处理一下就行