Если вы пропустили их, предыдущие статьи о том, как собрать MangoTree, клиент для Windows Phone в Твиттере, доступны здесь:
- Клиент OAuth и базовая инфраструктура
- Аутентификация с помощью PIN
- Сохранение данных аутентификации и проверка того, что пользователя запомнили
- Создание основного интерфейса
- Отправка вашего первого твита
MangoTree может твитнуть, но в настоящее время не может видеть, что пишут другие люди. Однако существует метод API, который может получить текущий локальный канал для аутентифицированного пользователя. Это называется statuses / home_timeline . У меня уже есть ядро OAuth, работающее довольно хорошо, поэтому все, что мне нужно сделать, это выполнить метод по URL-адресу, который Twitter дает разработчику, предоставляя правильные учетные данные. Вот фрагмент:
public static void GetHomeTimeline() { Dictionary<string, string> parameters = new Dictionary<string, string>(); parameters.Add("oauth_consumer_key", AuthConstants.ConsumerKey); parameters.Add("oauth_signature_method", "HMAC-SHA1"); parameters.Add("oauth_timestamp", StringHelper.UNIXTimestamp); parameters.Add("oauth_nonce", Guid.NewGuid().ToString().Replace("-", "")); parameters.Add("oauth_version", "1.0"); parameters.Add("oauth_token", App.Token); OAuthClient client = new OAuthClient(); client.PerformRequest(parameters, "http://api.twitter.com/1/statuses/home_timeline.json", "GET", AuthConstants.ConsumerSecret, App.TokenSecret, string.Empty, data => { string returned = (string)data[0]; Debug.WriteLine(returned); }, RequestType.Read); }
Этот метод является частью статического класса Methods , который предназначен для доступа к различным возможностям Twitter. Когда этот метод успешно выполняется, я получаю необработанные данные JSON — для целей тестирования я помещаю их в консоль вывода. Количество данных, подключенных к одному твиту, довольно мало — посмотрите, что это такое:
{ "in_reply_to_user_id_str":"14307150", "truncated":false, "created_at":"Mon Apr 09 04:44:12 +0000 2012", "coordinates":null, "retweeted":false, "place":null, "in_reply_to_screen_name":"tylerjameslee", "possibly_sensitive":false, "contributors":null, "user":{ "id":1312381, "listed_count":57, "contributors_enabled":false, "profile_sidebar_border_color":"8f0000", "geo_enabled":true, "profile_background_image_url_https":"https:\/\/si0.twimg.com\/profile_background_images\/75507540\/250px-Porkins.jpg", "friends_count":921, "profile_image_url":"http:\/\/a0.twimg.com\/profile_images\/2048185458\/IMG_20120405_155554_normal.jpg", "profile_background_tile":true, "followers_count":746, "is_translator":false, "show_all_inline_media":true, "follow_request_sent":false, "statuses_count":47764, "utc_offset":-25200, "profile_sidebar_fill_color":"cccccc", "name":"Joshua Rivera", "default_profile_image":false, "protected":false, "profile_background_color":"961313", "favourites_count":275, "lang":"en", "url":"http:\/\/www.thejoshuarivera.com\/", "verified":false, "created_at":"Fri Mar 16 20:54:27 +0000 2007", "profile_background_image_url":"http:\/\/a0.twimg.com\/profile_background_images\/75507540\/250px-Porkins.jpg", "description":"Older white women say I'm very articulate. I'm also a huge dick.", "profile_link_color":"424240", "profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/2048185458\/IMG_20120405_155554_normal.jpg", "default_profile":false, "following":true, "profile_use_background_image":true, "location":"burque", "notifications":false, "id_str":"1312381", "profile_text_color":"7a7a7a", "time_zone":"Mountain Time (US & Canada)", "screen_name":"supersloth" }, "retweet_count":0, "favorited":false, "in_reply_to_user_id":14307150, "source":"\u003Ca href=\"http:\/\/www.tweetdeck.com\" rel=\"nofollow\"\u003ETweetDeck\u003C\/a\u003E", "in_reply_to_status_id_str":"189211447128104960", "geo":null, "in_reply_to_status_id":189211447128104960, "id":189212091205419008, "id_str":"189212091205419008", "text":"@tylerjameslee http:\/\/t.co\/sDmfpuXq and of course http:\/\/t.co\/7W24zRMc" }
Это, очевидно, требует реализации двух пользовательских моделей — Tweet и User . Я создал новую папку с именем Models и добавил два исходных файла cs для каждой из упомянутых моделей.
Tweet.cs:
namespace MangoTree.Models { public class Tweet { public string InReplyToUserIdStr { get; set; } public bool Truncated { get; set; } public string CreatedAt { get; set; } public string Coordinates { get; set; } public bool Retweeted { get; set; } public string Place { get; set; } public string InReplyToScreenName { get; set; } public string Contributors { get; set; } public User Author { get; set; } public int RetweetCount { get; set; } public bool Favorited { get; set; } public int InReplyToUserId { get; set; } public string Source { get; set; } public string InReplyToStatusIdStr { get; set; } public ulong InReplyToStatusId { get; set; } public ulong Id { get; set; } public string IdStr { get; set; } public string Text { get; set; } } }
User.cs:
namespace MangoTree.Models { public class User { public int ID { get; set; } public int ListedCount { get; set; } public bool ContributorsEnabled { get; set; } public string ProfileBackgroundImageUrlHTTPS { get; set; } public int FriendsCount { get; set; } public string ProfileSidebarColor { get; set; } // better to keep the raw color code public bool GeoEnabled { get; set; } public string ProfileImageUrl { get; set; } public bool ProfileBackgroundTile { get; set; } public int FollowersCount { get; set; } public bool IsTranslator { get; set; } public bool ShowAllInlineMedia { get; set; } public bool FollowRequestSent { get; set; } public int StatusesCount { get; set; } public int UTCOffset { get; set; } public string ProfileSidebarFillColor { get; set; } // again, better to keep the raw color public string Name { get; set; } public bool DefaultProfileImage { get; set; } public bool Protected { get; set; } public string ProfileBackgroundColor { get; set; } public int FavoritesCount { get; set; } public string Language { get; set; } public string Url { get; set; } public bool Verified { get; set; } public string CreatedAt { get; set; } public string ProfileBackgroundImageUrl { get; set; } public string Description { get; set; } public string ProfileLinkColor { get; set; } public string ProfileImageUrlHTTPS { get; set; } public bool DefaultProfile { get; set; } public bool Following { get; set; } public bool ProfileUseBackgroundImage { get; set; } public string Location { get; set; } public bool Notifications { get; set; } public string IdString { get; set; } public string ProfileTextColor { get; set; } public string TimeZone { get; set; } public string ScreenName { get; set; } } }
Синтаксический анализ Json можно выполнить с помощью стандартной Json DLL — System.Json.dll (обычно находится в C: \ Program Files (x86) \ Microsoft SDKs \ Silverlight \ v4.0 \ Libraries \ Client ). В папке Utility я создал новый класс под названием TweetParser . Он будет использоваться для анализа отдельных твитов из необработанного ответа. Вот как выглядит конвертер «сырье-модель»:
using System; using MangoTree.Models; using System.Collections.ObjectModel; using System.Json; using System.Diagnostics; namespace MangoTree.Utility { public static class TweetParser { public static ObservableCollection<Tweet> Parse(string raw) { ObservableCollection<Tweet> returnable = new ObservableCollection<Tweet>(); try { JsonArray array = JsonObject.Parse(raw) as JsonArray; foreach (JsonObject tweet in array) { Tweet timelineItem = new Tweet() { InReplyToUserIdStr = (tweet["in_reply_to_user_id_str"] ?? string.Empty), Truncated = (bool)tweet["truncated"], CreatedAt = tweet["created_at"], Retweeted = tweet["retweeted"], Place = (tweet["place"] ?? string.Empty), InReplyToScreenName = (tweet["in_reply_to_screen_name"] ?? string.Empty), Contributors = (tweet["contributors"] ?? string.Empty), RetweetCount = tweet["retweet_count"], Favorited = tweet["favorited"], InReplyToUserId = (tweet["in_reply_to_user_id"] ?? 0), Source = (tweet["source"] ?? string.Empty), InReplyToStatusIdStr = (tweet["in_reply_to_status_id_str"] ?? string.Empty), InReplyToStatusId = (tweet["in_reply_to_status_id"] ?? 0), Id = tweet["id"], IdStr = tweet["id_str"], Text = tweet["text"], Author = GetUser(tweet["user"]) }; returnable.Add(timelineItem); } } catch (Exception ex) { // The collection failed parsing. Debug.WriteLine("Collection failed parsing.\n" + ex.InnerException + Environment.NewLine + ex.Data); } return returnable; } private static User GetUser(JsonValue rawUser) { User returnable = new User() { ID = rawUser["id"], ListedCount = rawUser["listed_count"], ContributorsEnabled = rawUser["contributors_enabled"], ProfileSidebarColor = rawUser["profile_sidebar_border_color"], GeoEnabled = rawUser["geo_enabled"], ProfileBackgroundImageUrlHTTPS = (rawUser["profile_background_image_url_https"] ?? string.Empty), FriendsCount = rawUser["friends_count"], ProfileImageUrl = rawUser["profile_image_url"], ProfileBackgroundTile = rawUser["profile_background_tile"], FollowersCount = rawUser["followers_count"], IsTranslator = rawUser["is_translator"], ShowAllInlineMedia = rawUser["show_all_inline_media"], FollowRequestSent = rawUser["follow_request_sent"], StatusesCount = rawUser["statuses_count"], UTCOffset = rawUser["utc_offset"], ProfileSidebarFillColor = rawUser["profile_sidebar_fill_color"], Name = rawUser["name"], DefaultProfileImage = rawUser["default_profile_image"], Protected = rawUser["protected"], ProfileBackgroundColor = rawUser["profile_background_color"], FavoritesCount = rawUser["favourites_count"], Language = rawUser["lang"], Url = (rawUser["url"] != null ? rawUser["url"].ToString() : string.Empty), Verified = rawUser["verified"], CreatedAt = rawUser["created_at"], ProfileBackgroundImageUrl = (rawUser["profile_background_image_url"] ?? string.Empty), Description = (rawUser["description"] ?? string.Empty), ProfileLinkColor = rawUser["profile_link_color"], ProfileImageUrlHTTPS = rawUser["profile_image_url_https"], DefaultProfile = rawUser["default_profile"], Following = rawUser["following"], ProfileUseBackgroundImage = rawUser["profile_use_background_image"], Location = (rawUser["location"] ?? string.Empty), Notifications = rawUser["notifications"], IdString = rawUser["id_str"], ProfileTextColor = rawUser["profile_text_color"], TimeZone = rawUser["time_zone"], ScreenName = rawUser["screen_name"] }; return returnable; } } }
Я активно использую оператор объединения нулей, чтобы проверить, пытаюсь ли я прочитать нулевое значение. В целом, процесс синтаксического анализа может быть выполнен со всеми необходимыми вам сантехническими операциями — просто используйте JSON.NET для десериализации модели, учитывая, что у вас есть правильное связывание атрибутов. Я отказался от этого, поскольку он добавит дополнительную ссылку на сборку, которая не будет интенсивно использоваться. System.Json довольно легок для этой конкретной ситуации и работает достаточно хорошо.
Возвращаясь к методу GetHomeTimeline , я теперь получаю ObservableCollection <Tweet> — теперь я могу легко связать его с чем угодно, включая ListBox, который будет помещен в MainPage.xaml .