2009年6月29日
とりあえず、RSS を読み込んで、それぞれの中身を取り出せるようになりました。
まず、基本方針。XML を読み込んで、それが対応しているフォーマットであることを確認します。確認が出来たら、管理したい項目のみ取り出して、管理するのに都合の良いフォーマットに変換します。
フォーマットの確認に、リフレクションを使います。一度使ってみたかったので、使うことにしました(ぉぃ)。
using System.Xml.Linq;
using System.Reflection;
/// <summary>
/// インスタンスを生成します。
/// </summary>
/// <param name="document">解析対象の XML ドキュメント</param>
/// <returns>RSS フィードを解析する SyndicationAnalyzer のインスタンスを返します。</returns>
/// <exception cref="System.NotSupportedException">サポートしていないフォーマットの場合、発生します。</exception>
public static SyndicationAnalyzer CreateInstance(XDocument document) {
// サポートしている型を列挙
Type[] supports = new Type[] { typeof(AnalyzerRss10), typeof(AnalyzerRss20), typeof(AnalyzerAtom) };
foreach (var t in supports) {
// それぞれの "CanParse" メソッドを検索
var canParse = t.GetMethod("CanParse", BindingFlags.Static | BindingFlags.Public);
// メソッドを実行
bool result = (bool)canParse.Invoke(null, new object[] { document });
if (result == true) {
// サポートしているなら、
// 規定のコンストラクターを取り出す
var stcConstructor = t.GetConstructor(new Type[] { });
// 規定のコンストラクターを呼び出して、インスタンスを生成する
var instance = stcConstructor.Invoke(new object[] { });
// XDocument 1つを引数に取るコンストラクターを取り出す
var constructor = t.GetConstructor(new Type[] { typeof(XDocument) });
// 規定のコンストラクターで生成したインスタンスを元に、引数ありコンストラクターを実行
constructor.Invoke(instance, new object[] { document });
// できたインスタンスを返す
return (SyndicationAnalyzer)instance;
}
}
throw new NotSupportedException();
}
あまり、うれしくないです。AnalyzerRss10、AnalyzerRss20、AnalyzerAtom は、それぞれ static public bool CanParse(XDocument document) メソッドと、規定のコンストラクタ、XDocument 型を引数にとるコンストラクタが定義されているものとします。この、「定義されているものとする」という縛りがかけられないのが、不便。インターフェイスを定義して・・・そのインターフェイスを実装したクラスを一覧、って出来ないかなぁ?ここに上げたコードだと、新しい型が増えたとき、型情報の一覧に追加するだけで対応できます。if 文または switch - case 文で、分岐を増やすってのが、したくなかった。それだけの理由でリフレクションを使ってみたのだけど、あまりうれしくなかったです。
とにかく、それらがあるもとして、supports に、その型を列挙します。列挙されているものを順に、CanParse メソッドを呼び出して、解析できるかどうかを判断します。このコードは CanParse メソッドがあることを前提にしていますが、当然 canParse の null チェックをしておくべきでしょう。
引数のない規定のコンストラクタは「静的な」コンストラクタなのですが、引数のあるコンストラクタは「動的な」コンストラクタなのだそうです。知らなかった。動的なコンストラクタは、インスタンスがなければ呼び出せません。その為、一度規定のコンストラクタを検索して、呼び出してインスタンスを生成し、そのインスタンスを利用して、引数を持ったコンストラクタを呼び出しています。
AnalyzerRss10、AnalyzerRss20、AnalyzerAtom の親は、次のように定義します。CanParse メソッドは、静的メソッドなので、ここには定義しません。
abstract class SyndicationAnalyzer
{
protected XDocument document;
#region プロパティ
/// <summary>
/// ドキュメントが設定され、解析ができる場合は true を返します。
/// </summary>
public virtual bool CanUse {
get { return (this.document != null); }
}
/// <summary>
/// 配信サイトのタイトルを取得します。
/// </summary>
public abstract string FeedTitle {
get;
}
/// <summary>
/// 配信サイトへの URL を取得します。
/// </summary>
public abstract Uri FeedLink {
get;
}
/// <summary>
/// 配信サイトの説明を取得します。
/// </summary>
public abstract string FeedDescription {
get;
}
/// <summary>
/// 配信中のアイテムの個数を取得します。
/// </summary>
public abstract int ItemCount {
get;
}
public abstract string ItemTitle(int index);
public abstract Uri ItemLink(int index);
public abstract string ItemDescription(int index);
#endregion
public static SyndicationAnalyzer CreateInstance(XDocument document) {
// 省略
}
}
2009年6月26日
昔のエントリ→当たり前のことだけど、名言
プロは仕事を練習にしない。
ん。あのときは、名言だと思ったんだ。でも、よく考えてみると・・・
レストランに行って、料理を食べているとき、シェフが挨拶に来て「今日、新人に練習がてら作らせてみたものですが、いかがでしょうか?」と尋ねたら、どう思います?
野球で、リリーフ投手がブルペンで投げ込みをせずに出てきたら、どう思います?
ピアノのコンサートに行って、幕が開いてから、やおらピアノの調律を始めたら、どうでしょう?
シェフがどうかはわかりませんが、スポーツの選手に関して言えば、練習の時間も仕事の時間なのです。もちろん、「試合中」に、練習はしないでしょう。でも、練習も仕事なのです。
というわけで。なんだかなぁ。。。
2009年6月24日
はい、パクリです。
今、日産プレーリー・リバティに乗ってます。12年式で、走行距離が13万キロ。一昨年あたりから半年ごとに、何かの電気系を交換しています。セル モーター、セルをエンジンの方へ移動させるモーター、ガソリンの供給を制御する何か、など。次、クーラーがどうにかなりそうな予感がしています。そろそろ、買い換えかなぁ。
リバティはもう生産していないのですが、後継がラフェスタです。ラフェスタは、サンルーフです。これがいいなぁ。大きさも同じくらいだし、スライド ドアだし。でも、予算が... あ、でも、マーチもいいなぁ。やっぱり、小さい分、乗りやすいし。いつか代車で使ったとき、とっても乗りやすかったんだよね。
そんなとき、義父が言いました。「わし、iQ買うさかい、おまえらプリウス買え。」
義父は去年、定年退職を迎えています。定年まで積み立てた会社の株が、そこそこの値で売れたため、私達の予算と合わせて云々ということです。
私はトヨタのマークが好きではないのですが、まぁ、そんな、走りや安全とはなんの関係もないことにこだわっても仕方がありません。とはいえ、今リバティに乗っているのは、妻と私が「マツダのチョビ髭みたいなマークは嫌いや」「トヨタの牛みたいな、なんやわからんマークは嫌いや」と言い合った結果なのですが。。。
まぁ、そんなこんなで、プリウスを見に行き、試乗してきました。静かです。エンジン音は聞こえません。エンジン自体が静音設計なので、動いているのがわかりません。力強いです。変速がギヤではないので、カクン!となりません。それなのに、クリープします。大きいように思いますが、意外と小回りがききます。リバティの大きなサイド ミラーに慣れているので、サイド ミラーが見にくいかな?と思いましたが、そうでもありません。逆に、三角窓は、飾りのようなリバティに比べてちゃんと機能します。バック ミラーに映る黒い横線(リア ウインドウの下辺)が若干目障りですが、まぁ、何とかなるでしょう。後席に座ると頭がつっかえますが、まぁ、許容範囲ということで。試乗車の「燃費計」は、40km/l を指していました。すごいです。一番安いグレードで205万円です(税、諸費用別)。むぅ。。。マーチと100万円の差がありますが、燃費を考えると、10年乗れば同じになります。原油価格が上がれば、もっと早く同じになります。電気製品は8年が寿命、とか言います。プリウスはモーターで走るわけですが、その辺は大丈夫なのでしょうか?別のお店で聞いてみたところ、バッテリーについては5~6年で交換になりますが、モーターは大丈夫とのこと。
オプションを見てみます。新しい物好き、エレクトロニクス好きの心を鷲掴みにするオプションがあります。プリクラッシュ セーフティ システム・・・前方の車との距離や相対速度から、衝突の可能性を検知し、知らせてくれるシステムです。時々「前!前!」と言い合う夫婦にはぴったりな機能だと思います。ソーラー ベンチレーション システム・・・屋根に太陽電池を積み、それの発電する電気で車内換気を行います。暑い夏に、ドアを開けたとたん もあ~っ! とするのを抑えてくれます。ついでに(だけど、重要)ムーン ルーフです。でもこの機能、ツーリング セレクションには付けられません。妻はヘッドランプを LED (というか、ディスチャージャー)にしたいらしいので、そちらが優先です。なお、販売店の人には「初物ですし、効果のほどは、わかりません」と言われました。あの、売る気あります?どちらも試乗車には付いていませんでした。こういう初物こそ試乗車に付けて、試してもらうものじゃないでしょうか?売る気あるのか?
見積もり取りました。予算を大幅にオーバーです。ちなみにリバティですが、下取り価格はありません。駐車場に停めているときに、周囲を引っかかれたり、助手席側に追突されたりしています。まぁ、主に私と、妻が擦ったりぶつけたりもしているし。。。
で、何を削るか、悩みます。悩みました。そして、今日、別の販売店へ、別の見積もりを取りに、義父と妻が向かいました。
昼、メールが来ました。
「なぁ、シエンタでもええか?」
ところで。試乗中、妻が運転している横で「電気君、ガソリン君、電気君、ガソリン君」と拍子を取っていたら、販売店の人に不審な顔をされました。トヨタの人なのに、勉強不足だと思います。
話はこれで終わりません。ちゃぶ台返しが待っていました。
学校から帰ってきた息子に、義父が「シエンタにするんやって」というと、息子が「ええ~!?そんなぁ。」と、返事したそうです。翌朝、義父は妻に、ポツリとつぶやきました。
やっぱり、孫の夢は壊したらあかん
妻のターンより、孫のターンのほうが強いようです。
2009年6月23日
否定表現に溢れる日常(株式会社スターロジックの羽生章洋が書いてるブログ)より:
ある雨の日に3~4歳くらいの子を連れたお母さんが、事務所のある昭和通り商店街を歩いていました。子供も傘を差していて当然よたよたした歩き方になります。子供のいる家庭ならご承知の通り、雨の日には水溜まりに入らせないのは重要なことです(笑)。そこで、そのお母さんも当然子供に注意します。
そしてその注意の仕方で「!」と感じたのです。具体的には、「水溜まりに入っちゃ駄目!」とは言うのですが、「水溜まりの横を歩いて!」とは言わないのです。「水溜まりに入らないようにしなきゃ駄目」とも言いました。普通の日本語であり違和感はないのですが、よくよく考えると相当に難易度の高い指示であると感じます。
ん~?なんか、違和感がある。。。ところで、「前回」があるようなので、それも見てみる。
否定表現と仕様書(株式会社スターロジックの羽生章洋が書いてるブログ)より:
昔から私は駄目な仕様書に共通することとして「曖昧な表現が多いこと」というのを挙げていました。そしてこの「曖昧な表現」は具体的には「否定表現」だったりするのです。「ではない」「しない」という表現です。
例えば、「Aの場合にBでなく、あるいはCでなければ、当該処理は実施しない」というようなものです。
上記の表現は当該処理なるものを実施しない場合の話です。なので、プログラムを書く側とすると「いつ実施するのか?」というのが必要になるのです。何故なら実施しないというのは、その処理の実行についてのコードを「書かない」のですから、端的に言うとこの仕様はプログラミング上は無意味いうことになるのです。しかし大抵は上記の表現から「文意を読み取る」「行間を読む」という行為を通じて、処理の実施タイミングを理解することを求められます。
そこで頑張って「Aの場合で、BかつCであれば、当該処理を実施する」と読み取ったとします(要するに、if(A and B and C)ですね)。
んー。。。最初の、駄目な仕様書に共通することとして「曖昧な表現が多いこと」
というのは、同意。で、次の表現。う~ん?そうなのかなぁ?これって、このように書けるよね?
void someMethod() {
if (A && (!B || !C)) { return; }
// do some process...
}
非常に簡単で単純明快だと思うのですが。。。いえ、もちろん、((A && !B) || !C) かもしれません。ここは曖昧だと思いますが、「~の場合、当該処理を実施しない」というのが曖昧だとは思えないです。あるいは、「いつ実施するのか?」というのが必要になる
とは思いません(もちろん、場合によっては必要になることもあると思う)。全ての場合から、実施しない場合を除けば、実施する場合になります。上のコードは、そのようにコーディングした例です。もちろん、if が何重にも入れ子になるようなケースは、「見やすさ」の観点から、遠慮したいです。
で、一つ目の引用に戻る。
子どもが水たまりに入ろうとしている時。この時、親が思い浮かべるのは、次のうちのどれかではないでしょうか。「1.水たまりで、ビチャビチャ足踏みして遊び出す」「2.水を盛大にはねて、靴下やズボンを濡らす/汚す」
親が「入らないで!」というのは、「入ってはいけない」と言っているのではなく、入るという原因で発生する結果を避けようと思って言っているのではないでしょうか。つまり、実際に伝えたいのは、「入ってはいけない」のではなく、「入って、(子ども自身、親、周りの人の)服を汚さないで」ではないでしょうか。
このとき、最も大事なのは「服を汚さない/泥水を跳ね上げない」ということで、「水たまりに入らない」ということではありません。例えば、水たまりが浅ければ、そうっと入るのはかまわないのです。たいていは、それでも撥ねたり、意外に深くて濡らしてしまうことになるのですが。
確かに、指示を受けた方は、難しいでしょう。しかし、様々な方法を試みる自由がある、とも言えます。また、考える力を鍛えることが出来るとも言えるでしょう。
否定表現に溢れる日常(株式会社スターロジックの羽生章洋が書いてるブログ)より:
同様に私どもの仕事においても、例えば「バグを出すな」とは言われますが、では「このようにして」と具体的に明示されることは意外とないように感じます。つまり問題の指摘をしてそれをするなという否定表現は容易なのですが、何を持って正解とするのかを明示するというのは咄嗟には難しいということなのかもしれません。
これは、「難しい」どころか、ほぼ不可能ではないでしょうか。
「バグ」とは、なんでしょう?プログラムに対して、「こう動いて欲しい」という期待があります。その期待にそぐわないことが、「バグ」ではないでしょうか。「バグを出すな」ということは、「期待通りに動作させろ」ということです。
「期待」は、仕様によって規定されているでしょう。しかし、「期待通りに動作しない状態」は、規定されていません。そして、そのような状態になる方法は、無数にあります。それら一つひとつに対策を考え、対策方法を指示していくのでしょうか。ナンセンスだと思います。
もちろん、「このように書く」というのを規定して、「これに従って書いて」と言うこともできます。でも、このときでも、「これに従って、バグの無い様に書いて」と言うでしょう。それは、コーディング規定に沿っても、ロジックについては規定されていないからです。
ひとつの問題に対して、解決策はいくつもある場合が多いです。それらの中から最良なものを探して提示する、というのもひとつの教えかもしれません。しかし、「アレをするな、コレをするな」から、話し手が意図している「本当に禁止したいこと」を考え、それを回避する方法を考えること。それもひとつの教えではないでしょうか。
あるいは、「コレをしろ、アレをしろ」と、レールを敷いてその上を走ることだけを教えた場合、考えることをしなくなる可能性があります。自分が考えなくても、誰かが考えてくれるのですから。それって、とっても危険だと思います。
2009年6月18日
gtk2kさん、コードの提供ありがとうございました。早速、見てみました(エントリするのは遅かったけど、見たのは早かったんだよぅ)。そして、このあたりに注目。
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xdoc.NameTable);
foreach (XmlAttribute atr in xdoc.DocumentElement.Attributes)
nsmgr.AddNamespace(atr.Name.IndexOf(":") == -1 ? "nn" : atr.Name.Split(':')[1], atr.Value);
XmlNodeList itemList = itemList = xdoc.SelectNodes(ns[rn], nsmgr);
おお、なるほど!XmlNamespaceManager クラスに名前空間を登録して管理させるのか。
で、これの、XML LINQ バージョンを探す・・・無い。。。XNamespace クラスというのが見つかったけど、これは、前回 "{namespace}local name" と書いたのと同じように、文字列から生成(暗黙変換)して、文字列と演算できるようになっているだけのように思われます。
え~?本当に?
検索して、行き着いたページ→方法 : 名前空間内の XML に対するクエリを記述する
う~ん。本当らしい。。。
元ネタ。
心が変われば、態度が変わる・・・ヒンズー教の教え(名言から学ぶ幸せのヒント)より:
『心が変われば、態度が変わる。
態度が変われば、行動が変わる。
行動が変われば、習慣が変わる。
習慣が変われば、人格が変わる。
人格が変われば、運命が変わる。
運命が変われば、人生が変わる』
類似。
歯科(なかの歯科)より:
スティーブン、R、コビーの『7つの習慣』の中に
このような格言が書かれている。
思いの種を蒔き、行動を刈り取り、行動の種を蒔いて習慣を刈り取る。
習慣の種を蒔き、人格を刈り取り、人格の種を蒔いて人生を刈り取る。
人が行動するとき、思い、あるいは意思が伴います。ただし、ある刺激に対して同じ動作を続けていると、意思に関係なく体が動きます。私は、「技術」とはこういう、意思に関係なく体が動くもの(の一種)だと思っています。なので、動作の産物というより、思考の産物である情報に対して“技術”という言葉を用いることに違和感があります。行動を起こす前に意思を確認し、意識して意思と行動を一致させること、すなわち行動の伴う意思、または意思の伴う行動を、魔術と言います。どちらかというと、情報“技術”ではなく、情報“魔術”の方がしっくりくるのです。つまり、考えて、「こうさせよう」という意思でもってプログラミングし、結果を引き出すからです。が、これは別なお話。
上の引用を、意識していようがなかろうが、人は思いによって行いが行われることを知っています。そのため、人の行いを見て、その人の為人を判断します。
判断をするとき、判断を行う人の背景によって、基準が変わります。その基準を推し量ることはできても、押しつけることはできません。
「人が判断する“私”が、自分の思っているものと違う。」そんなふうに思うとき、自身の行いを振り返ってみてはいかがでしょうか。
2009年6月10日
前回の失敗。
Jitta:「XDocument って、RSS 2.0 しか解析してくれないの?!」
aetosさん:「名前空間の指定がないからです。」
むはー!単純明快な答え、ありがとうございます。
ということで、仕様を当たります。前々回に、英語の仕様へリンクを張りましたが、日本語に訳してくださっている方がいらっしゃいました。
済みません、これで、前々回にいただいているコメント厳密には名前空間 URI も見た方がいいでしょう。
がわかりました。
名前空間にはバージョン番号が含まれることがあります。現に、Atom は「2005」というのが、2005年に決まったことを表しているでしょうし、RSS 1.0 は、「1.0」と入っています。ここで調べたかったのは、どのフォーマットの系統なのかということなので、そこまで細かいのが必要かなぁ?と思っていたのですが、要るのね。。。
とりあえず、名前空間を指定すれば Element メソッドで取得できるか?確認します。
XElement.Element メソッドに指定するのは、XName クラスです。しかし、XName クラスにはコンストラクタがありません。これは、String クラスからのコンバーターが用意されています。では、名前空間は、どうやって指定するのでしょう?名前空間を定義するときには、名前空間の省略名が定義できます。そういったもので、省略名が変わっていたら、指定できないのでは?
ところが、XName クラスは、省略名を定義できません。全て XName クラスに展開されており、XName クラスでは、完全な名前空間を持っています。では、どのように参照するのでしょう?次のように指定します。
XName name = "{namespace}local name";
では、書き換えます。
static IEnumerable<XElement> GetItems() {
// とりあえず、全部 RSS 1.0
string[] feeds = {
"http://rss.rssad.jp/rss/itmatmarkit/rss2dc.xml",
"http://rss.rssad.jp/rss/news/rss2dc.xml",
"http://rss.rssad.jp/rss/fwin2k/rss2dc.xml",
"http://rss.rssad.jp/rss/fdotnet/rss2dc.xml"
};
foreach (var str in feeds) {
var feed = XDocument.Load(str);
var channel = feed.Root.Element("{http://purl.org/rss/1.0/}channel");
var items = channel.Elements("{http://purl.org/rss/1.0/}item");
foreach (var item in items)
yield return item;
}
}
無事、動きました。