美文网首页
核心技术-1、自定义JSON解析

核心技术-1、自定义JSON解析

作者: edutony | 来源:发表于2017-12-14 11:57 被阅读0次

    原文地址:https://www.cnblogs.com/codelove/p/5236488.html

    一、返回数据结构分析

    1、数据示例:

    {"menu":
        {"button":[
            {"type":"click","name":"今日歌曲","key":"V1001_TODAY_MUSIC","sub_button":[]},
            {"type":"click","name":"歌手简介","key":"V1001_TODAY_SINGER","sub_button":[]},
            {"name":"菜单","sub_button":[
                {"type":"view","name":"搜索","url":"http://www.soso.com/","sub_button":[]},
                {"type":"view","name":"视频","url":"http://v.qq.com/","sub_button":[]},
                {"type":"click","name":"赞一下我们","key":"V1001_GOOD","sub_button":[]}
            ]}
        ]}
    }
    

    2、数据分析:

    图1-1微信自定义菜单数据分析

    3、解决方案:

        public class MenuFull_RootButton
        {
            public string type { get; set; }
            public string key { get; set; }
            public string name { get; set; }
            public string url { get; set; }
            public string media_id { get; set; }
            public List<MenuFull_RootButton> sub_button { get; set; }
        }
    

    二、序列化解决方案

    1、定义接口方法:

        /// <summary>
        /// 自定义菜单接口
        /// http://mp.weixin.qq.com/wiki/10/0234e39a2025342c17a7d23595c6b40a.html
        /// </summary>
        public class MenuApi : ApiBase
        {
            const string APIName = "menu";
            /// <summary>
            /// 自定义菜单查询接口
            /// https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN
            /// </summary>
            /// <returns>菜单返回结果</returns>
            public MenuGetApiResultModel Get()
            {
                //获取api请求url
                var url = GetAccessApiUrl("get", APIName);
                return Get<MenuGetApiResultModel>(url, new MenuButtonsCustomConverter());
            }
        }
    

    重点是:MenuGetApiResultModelMenuButtonsCustomConverter

    注意:ApiBase和Get的封装请暂时忽略。Get在这里只是用于发起Get请求并且序列化JSON而已,其定义如下:

    /// <summary>
    /// GET提交请求,返回ApiResult对象
    /// </summary>
    /// <typeparam name="T">ApiResult对象</typeparam>
    /// <param name="url">请求地址</param>
    /// <param name="jsonConverts">Json转换器</param>
    /// <returns>ApiResult对象</returns>
    protected T Get<T>(string url, params JsonConverter[] jsonConverts) where T : ApiResult
    

    2、定义JSON模型:

    1)定义根
        /// <summary>
        /// 菜单返回结果
        /// </summary>
        public class MenuGetApiResultModel : ApiResult
        {
            [JsonProperty("menu")]
            public MenuInfo Menu { get; set; }
        }
        /// <summary>
        /// 菜单信息
        /// </summary>
        public class MenuInfo
        {
            [JsonProperty("button")]
            public List<MenuButtonBase> Button { get; set; }
        }
    
    2)定义菜单类型:
        /// <summary>
        /// 菜单类型
        /// </summary>
        public enum MenuButtonTypes
        {
            /// <summary>
            /// 点击推事件
            /// </summary>
            click = 1,
            /// <summary>
            /// 跳转URL
            /// </summary>
            view = 2,
            /// <summary>
            /// 扫码推事件
            /// </summary>
            scancode_push = 3,
            /// <summary>
            /// 扫码推事件且弹出“消息接收中”提示框
            /// </summary>
            scancode_waitmsg = 4,
            /// <summary>
            /// 弹出系统拍照发图
            /// </summary>
            pic_sysphoto = 5,
            /// <summary>
            /// 弹出拍照或者相册发图
            /// </summary>
            pic_photo_or_album = 6,
            /// <summary>
            /// 弹出微信相册发图器
            /// </summary>
            pic_weixin = 7,
            /// <summary>
            /// 弹出地理位置选择器
            /// </summary>
            location_select = 8,
            /// <summary>
            /// 下发消息(除文本消息)
            /// </summary>
            media_id = 9,
            /// <summary>
            /// 跳转图文消息URL
            /// </summary>
            view_limited = 10
        }
    
    3)定义菜单基类:
        /// <summary>
        /// 菜单按钮基类
        /// </summary>
        public class MenuButtonBase
        {
            /// <summary>
            /// 菜单标题,不超过16个字节,子菜单不超过40个字节
            /// </summary>
            [MaxLength(20)]
            [JsonProperty(PropertyName = "name", Required = Required.Always)]
            public virtual string Name { get; set; }
            /// <summary>
            /// 菜单类型(菜单的响应动作类型)
            /// </summary>
            [JsonConverter(typeof(StringEnumConverter))]
            [JsonProperty(PropertyName = "type")]
            public MenuButtonTypes Type { get; set; }
        }
    

    注意:
    JsonProperty:用于指定属性
    JsonConverter:用于设置转换器,这里使用了StringEnumConverter,用于将字符串转换为相应的枚举类型。
    MaxLength 目前没有意义

    4)定义按钮:
        /// <summary>
        /// 子菜单按钮
        /// </summary>
        public class SubMenuButton : MenuButtonBase
        {
            /// <summary>
            /// 菜单标题,不超过16个字节,子菜单不超过40个字节
            /// </summary>
            [MaxLength(8)]
            [JsonProperty(PropertyName = "name", Required = Required.Always)]
            public override string Name { get; set; }
            /// <summary>
            /// 子菜单(二级菜单数组,个数应为1~5个)
            /// </summary>
            [JsonProperty(PropertyName = "sub_button")]
            public List<MenuButtonBase> SubButtons { get; set; }
       }
       /// <summary>
       /// Click按钮(点击推事件)
       /// 用户点击click类型按钮后,微信服务器会通过消息接口推送消息类型为event的结构给开发者(参考消息接口指南),并且带上按钮中开发者填写的key值,开发者可以通过自定义的key值与用户进行交互;
       /// </summary>
       public class ClickButton : MenuButtonBase
       {
           public ClickButton()
           {
               this.Type = MenuButtonTypes.click;
           }
           /// <summary>
           /// 菜单KEY值,用于消息接口推送,不超过128字节
           /// </summary>
           [JsonProperty(PropertyName = "key", Required = Required.Always)]
           public string Key { get; set; }
       }
       /// <summary>
       /// 下发消息(除文本消息)    
       /// 用户点击media_id类型按钮后,微信服务器会将开发者填写的永久素材id对应的素材下发给用户,永久素材类型可以是图片、音频、视频、图文消息。请注意:永久素材id必须是在“素材管理/新增永久素材”接口上传后获得的合法id。
       /// </summary>
       public class MediaIdButton : MenuButtonBase
       {
           public MediaIdButton()
           {
               this.Type = MenuButtonTypes.media_id;
           }
           /// <summary>
           /// 调用新增永久素材接口返回的合法media_id
           /// </summary>
           [JsonProperty(PropertyName = "media_id", Required = Required.Always)]
           public string MediaId { get; set; }
       }
       /// 其他按钮封装省略
    
    5)定义自定义对象创建转换器(CustomCreationConverter):
    图2-1自定义对象转换器源码分析

    从源码中我们可以看出,Create方法必须实现,除此之外,因为我们的目的是为了解析微信返回的消息,只需要实现ReadJson方法即可(当然实现CanConvert方法能让代码更严谨)。

        /// <summary>
        /// 菜单按钮自定义对象创建转换器
        /// 根据菜单类型自定义创建
        /// </summary>
        public class MenuButtonsCustomConverter : CustomCreationConverter<MenuButtonBase>
        {
            /// <summary>
            /// 读取目标对象的JSON表示
            /// </summary>
            /// <param name="reader">JsonReader</param>
            /// <param name="objectType">对象类型</param>
            /// <param name="existingValue"></param>
            /// <param name="serializer"></param>
            /// <returns>对象</returns>
            public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
            {
                if (reader.TokenType == JsonToken.Null) return null;
                var jObject = JObject.Load(reader);
                MenuButtonBase target = default(MenuButtonBase);
                //获取type属性
                var type = jObject.Property("type");
                if (type != null && type.Count > 0)
                {
                    var typeValue = type.Value.ToString();
                    var menuButtonType = (MenuButtonTypes)Enum.Parse(typeof(MenuButtonTypes), typeValue);
                    #region 根据类型返回相应菜单类型
                    switch (menuButtonType)
                    {
                        case MenuButtonTypes.click:
                            target = new ClickButton();
                            break;
                        case MenuButtonTypes.view:
                            target = new ViewButton();
                            break;
                        case MenuButtonTypes.scancode_push:
                            target = new ScancodePushButton();
                            break;
                        case MenuButtonTypes.scancode_waitmsg:
                            target = new ScancodeWaitmsgButton();
                            break;
                        case MenuButtonTypes.pic_sysphoto:
                            target = new PicSysphotoButton();
                            break;
                        case MenuButtonTypes.pic_photo_or_album:
                            target = new PicPhotoOrAlbumButton();
                            break;
                        case MenuButtonTypes.pic_weixin:
                            target = new PicWeixinButton();
                            break;
                        case MenuButtonTypes.location_select:
                            target = new LocationSelectButton();
                            break;
                        case MenuButtonTypes.media_id:
                            target = new MediaIdButton();
                            break;
                        case MenuButtonTypes.view_limited:
                            target = new ViewLimitedButton();
                            break;
                        default:
                            throw new NotSupportedException("不支持此类型的菜单按钮:" + menuButtonType);
                    } 
                    #endregion
                }
                else
                {
                    target = new SubMenuButton();
                }
                serializer.Populate(jObject.CreateReader(), target);
                return target;
            }
            /// <summary>
            /// 创建对象(会被填充)
            /// </summary>
            /// <param name="objectType">对象类型</param>
            /// <returns>对象</returns>
            public override MenuButtonBase Create(Type objectType)
            {
                return new SubMenuButton();
            }
        }
    
    5)测试结果:
    image.png

    相关文章

      网友评论

          本文标题:核心技术-1、自定义JSON解析

          本文链接:https://www.haomeiwen.com/subject/yhzhwxtx.html