程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> Magcodes.WeiChat——自定義CustomCreationConverter之實現微信自定義菜單的序列化,magicodes.weichat

Magcodes.WeiChat——自定義CustomCreationConverter之實現微信自定義菜單的序列化,magicodes.weichat

編輯:C#入門知識

Magcodes.WeiChat——自定義CustomCreationConverter之實現微信自定義菜單的序列化,magicodes.weichat


微信自定義菜單接口是一個比較麻煩的接口,往往開發的小伙伴們看到下面的這段返回JSON,整個人就會不好了:

 
{"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":[]}]}]}}

N個類型,而且每種類型都有不同的屬性,而且還要sub_button!讓我們去淚奔一會。

碰到這種問題,一般的小伙伴是這麼玩的:

首先我們需要確認總共有哪些屬性,如下所示:

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; } }

然後就獲取到了一堆蹩腳對象。作為代碼潔癖者的我,沒法忍!(開始裝B了)

於是就開始悶著頭編碼了(B裝不下去了)~~~

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());
        }
    }

這裡值得注意的是MenuGetApiResultModel和MenuButtonsCustomConverter,就靠他倆出招了。

注意: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模型

先定義根:

/// <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; }
}

然後定義一個菜單類型:

/// <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
    }

枚舉方便維護。

然後,要定義菜單基類了,開始搞基了:

/// <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請暫時忽略,我是為將來接口自定義驗證預留的,當然你也可以當成我順手撸上的,不過當前我們不是來做驗證的,我們是來做接口滴。

好了,開始搞基。我們先來定義一級按鈕類型。比如含子菜單的情況:

/// <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; }
}

下面省略一千行代碼…

好了,JSON模型初步定義好了,看著像模像樣的,然並卵!好吧,不裝B了,我們繼續。

3. 定義自定義對象創建轉換器(CustomCreationConverter

這B又可以快樂的裝下去了,真開心。

我們先來看看其定義:

/// <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(); } }

至此,整個是OK的。那麼我們來看看結果:

image

image

這B總算裝完了。

這是Magicodes.WeiChat.Framework中,MenuApi的設計,上面只是介紹其原理,後續會完善個性化菜單以及相關接口。

Magicodes.WeiChat.Framework為本人輕量設計的微信SDK,框架基本成型後,會將此部分剝離Magicodes.WeiChat並且開源。希望能夠得到各位熱心觀眾的支持。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved