Если вы пропустили их, предыдущие статьи о том, как собрать 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 .