2010年2月14日日曜日

第14回 Search APIを使ってみる

今回はSearchAPIを使ってみます。
SearchAPIはhttp://search.twitter.com/で検索した結果と同じようなものを取得できます。
今まで書き忘れていましたが、twitterのAPIは以下で公開されています。
http://apiwiki.twitter.com/

SearchAPI使う上で困るのは、戻ってくるJsonデータのフォーマットが違うことです。
どこで違いを吸収しようかなと、ViewModelレベルで分けたり試行錯誤しましたが、
Modelの最小限で違いを表すことにしました。
(ちなみにViewModelレベルまで違いを持ち込むと、
UI表示上の動作の違いは作りやすくなるかわりに
クラスの増加、コードの重複が多くなりそうな気がします。)

今回は小さな変更点が多数の箇所にあるので、
要点だけを書きます。詳細はソースコード参照。

まず、通常APIとSearchAPIの共通データを表現するインターフェイスを作ります。


 public interface ITimelineItem : IEquatable<ITimelineItem> {

  /// <summary>
  /// ID番号
  /// </summary>
  long Id { get; }

・・略
 }

前回までViewModelが直接Statusを参照していましたが
上記クラスを参照するようにします。


 public class TimelineItemViewModel {

  public TimelineItemViewModel(ITimelineItem item) {
   _item = item;
  }

あとはITimelineItemクラスを実装するクラスをそれぞれのAPI用に
2つ(TwitterItemクラスとSearchTwitterItemクラス)作り、
それぞれがAPI専用の実際のデータを持つStatusクラス(※APIごとに名前変えた方がよかったかも)
を作ります。

あとはそれらのデータをURLから読み込む部分が異なりますので、
そこだけクラスに抽出します。

まずインターフェイス。


 public interface IItemsReader {
  /// <summary>
  ///
  /// </summary>
  /// <param name="source">入力ストリーム</param>
  /// <returns>解析結果(タイムラインのアイテム)</returns>
  IEnumerable<ITimelineItem> Read(Stream source);
 }

SearchAPIの方の実装。(もう一方は略)


 class SearchTwitterItemsReader : IItemsReader {
  #region IItemsReader メンバ

  public IEnumerable<ITimelineItem> Read(System.IO.Stream source) {
   var serializer = new DataContractJsonSerializer(typeof(SearchResult));
   var result = serializer.ReadObject(source) as SearchResult;
   foreach (var item in result.Results) {
    yield return new SearchTwitterItem(item);
   }
  }

  #endregion
 }





SearchResultクラスとして読み込んでいますが、
これまでのAPIはStatusの配列を渡してきたのに対して
SearchAPIはStatusの配列を持った1つのオブジェクトを渡してくるため、
その違いを吸収するクラスです。

あとはSettingクラスも派生させて、検索ワードなどの差分を追加しています。
オプションダイアログ上の違い(クラスが異なるのに1つのリストに表示する)は
以下のように2つのDataTemplateを作ることで実現しています。


<Window x:Class="WTwitter.View.OptionDialog"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:opt="clr-namespace:WTwitter.Model.UserData"
    Title="オプション設定" Width="400" Height="400" Loaded="Window_Loaded">
 <Window.BindingGroup>
  <BindingGroup/>
 </Window.BindingGroup>
 
 <Window.Resources>
  <DataTemplate DataType="{x:Type opt:TimelineSetting}">
   <GroupBox Header="タイムライン">
    <StackPanel>
     <DockPanel>
      <TextBlock DockPanel.Dock="Left">表示名</TextBlock>
      <TextBox Text="{Binding Path=Name}" DockPanel.Dock="Left" MinWidth="50"/>
      <CheckBox Content="Auth" IsChecked="{Binding Path=IsAuthRequired}"/>
     </DockPanel>
     <DockPanel>
      <TextBlock DockPanel.Dock="Left">URL</TextBlock>
      <TextBox Text="{Binding Path=Url}"/>
     </DockPanel>
    </StackPanel>
   </GroupBox>
  </DataTemplate>

  <DataTemplate DataType="{x:Type opt:SearchTimelineSetting}">
   <GroupBox Header="検索タイムライン">
    <StackPanel>
     <DockPanel>
      <TextBlock DockPanel.Dock="Left">表示名</TextBlock>
      <TextBox Text="{Binding Path=Name}" DockPanel.Dock="Left" MinWidth="50"/>
      <CheckBox Content="日本語のみ対象" IsChecked="{Binding Path=IsJapaneseOnly}"/>
     </DockPanel>
     <DockPanel>
      <TextBlock DockPanel.Dock="Left">検索ワード</TextBlock>
      <TextBox Text="{Binding Path=SearchText}"/>
     </DockPanel>
    </StackPanel>
   </GroupBox>
  </DataTemplate>
 </Window.Resources>



毎回ですが、説明がうまくできない。。

今回のソースコード
http://wtwitter.codeplex.com/SourceControl/changeset/view/41131

0 件のコメント:

コメントを投稿