Neutral Scent

App developments & Gadgets

圏央道神奈川区間開通前後の渋滞状況をGoogle Mapsで比較してみた #GIFアニメ

http://f.st-hatena.com/images/fotolife/k/kaorun/20140705/20140705133134_original.gif
どうも、プログラマ兼ライト建設ヲタのid:kaorunです。
先週末は開通直後の圏央道神奈川区間(海老名JCT〜八王子JCT)を東名から中央道まで走ってきたのですが、

今週ふと思い立って、Google Mapsの曜日・時間毎渋滞情報と現在の渋滞情報を比べたら、圏央道の開通効果が素人でも比較できるんじゃないか? と思い立ち、アニメーションGIFを作ってみたのがこちらです。

http://f.st-hatena.com/images/fotolife/k/kaorun/20140705/20140705133134_original.gif

Google Mapsは曜日と時間指定でその時間帯の平均交通量を元に渋滞量を表示できるのですが、現在の渋滞状況と、この平均値を比べると、先週までの情報しか含まれていない圏央道開通前と同一曜日と開通後の状況をパカパカ切り替えて比較できるというわけです。
今週はこの辺りのキーワードでTwitter等もウォッチしていて、初の平日で月末の6月30日(月)には「なんか道空いてない?」とか「月末なのにこれ圏央道効果?」みたいなツイートが散見されていたんですが、7月1日は月初という事もあり平均値と比較すると如実に空いてるのが見て取れますね。
お時間があれば、Google Mapsの左上の検索ボックスのあたりにマウスを持っていくと「交通状況」のリンクが表示されるので、ご自分でも試してみてください。平均値に反映される前の今限定です。

そんなわけで、その後もちまちまとスクリーンショットを保存していたので、エントリーとしてまとめてみました。
仕事の合間などに保存したモノなのでちょっと不定期かつ表示範囲やサイズなどもガタガタなのはご容赦ください。
比較対象があくまでも平均値であり、その日その日の交通量も変動しているため、一概にこれだけ効果があったとは言えませんが、全体の傾向を見るのには参考になるのではないでしょうか?
クリックで拡大表示。

7月3日(木曜日/曇り)

開通後の平日で、交通量が比較的多くなってきたね、という感じの木曜日。それでも、パッと見で全体の緑の量が大きく増えているのがわかります。特に環八など環状道路の変化が大きいですね。16号はそこそこ渋滞していますが、渋滞に隙間ができ地元の人のツイートなどを見るとだいぶ流れるようになったようです。

8:30
http://f.st-hatena.com/images/fotolife/k/kaorun/20140705/20140705133906_original.gif
11:30
http://f.st-hatena.com/images/fotolife/k/kaorun/20140705/20140705133900_original.gif
15:30
http://f.st-hatena.com/images/fotolife/k/kaorun/20140705/20140705133854_original.gif
18:30
http://f.st-hatena.com/images/fotolife/k/kaorun/20140705/20140705133848_original.gif

7月4日(金曜日/雨)

雨の金曜日、交通量も多くさすがに首都高もだいぶ渋滞していますが、環七・山手通りあたりは意外と流れています。圏央道とは直接関係ないはずですが、保土ヶ谷バイパスも総じて渋滞が減っているように見えるところも気になりますね。

8:00
http://f.st-hatena.com/images/fotolife/k/kaorun/20140705/20140705134709_original.gif
12:00
http://f.st-hatena.com/images/fotolife/k/kaorun/20140705/20140705134704_original.gif

7月5日(土曜日/雨)

開通2週目の土曜日の昼時、天気はあいにくの雨、知名度が上がってさあ通ってみようか、という流れで東名方面の圏央道内回り海老名JCT手前が渋滞しています。雨という事もあって、各所で渋滞していますが、それでも全体的には渋滞の距離が減っている印象。
11:30
http://f.st-hatena.com/images/fotolife/k/kaorun/20140705/20140705134658_original.gif

実感としての話、勤務先からは首都高5号線の飯田橋付近を見下ろせるのですが、今までは、渋滞がデフォ、お、今日は空いてるね、あーもう首都高が空いている時間になってしまったね、みたいな感じだったのが、少なくとも今週は、おー流れてるねぇ、が通常状態で、時々詰まってるぐらいに変わってきているように感じました。
5号線が真昼間からこんな状態(写真奥がC1方面、左下が小石川橋、水道橋方面)

いかがでしょうか? 正直、まぁ今週は空いてただけなんじゃないの? と思ってしまうぐらい都心全体の交通の流れが変わっているように感じられ、ここでは取り上げませんでしたが、JARTICの渋滞情報を見ていても首都高がガラガラの時間帯が増えています。まぁ、しばらくすれば、本家NEXCO東日本や首都高あたりからも公式なデータが出てくると思いますが、果たして圏央道開通効果はどこまで持続するのでしょうか?
ちなみに、余談ですが、東名川崎ICからスムースに流れると高尾山ICまで1時間半ぐらいといったところ 先週の高尾山はまだガラガラでいい感じにおそばを食べて帰ってきたのですが、今後知名度が上がると日帰り観光気分で確実に人出が増え客層も変わり、これまでのような静かな登山口という趣は薄れていくのではないかと思います。京王線沿線住民の憩いの場所、古き良き高尾山口を堪能したければ今のうちかもしれませんね。
追記(2014/07/09):
公式の速報が出ました。

圏央道 相模原愛川IC〜高尾山IC間 <開通1週間後の速報> | ニュースリリース | NEXCO 中日本
http://www.c-nexco.co.jp/corporate/pressroom/news_release/3520.html

追記(2014/08/08):
さらに公式のお知らせが出ました。

国道468号 圏央道 相模原愛川IC〜高尾山ICの開通1ヶ月後の整備効果についてお知らせします | ニュースリリース | NEXCO 中日本
http://www.c-nexco.co.jp/corporate/pressroom/news_release/3535.html
≪交通:都心経由から圏央道経由への転換≫
• 東名←→関越間で都心を経由する交通が約9割から約3割に大幅に減少
• 大型車の多くは圏央道を利用

東京2020計画地図

東京2020計画地図

2020 東京・首都圏未来予想図 (別冊宝島 2116)

2020 東京・首都圏未来予想図 (別冊宝島 2116)

東京大改造マップ2020 (日経BPムック)

東京大改造マップ2020 (日経BPムック)

東京大改造マップ2020ハンディ版 (日経BPムック)

東京大改造マップ2020ハンディ版 (日経BPムック)

Windows Phone 8.1用はてなブックマーク登録アプリNatehaR.Express Version 1.1を公開しました


Windows Phone 8.1用、共有コントラクト型はてなブックマーク登録専用アプリ、NatehaR.ExpressをVersion 1.1へバージョンアップしました。無料です。

Version 1.1での変更点:

  • タグ入力候補リストの実装
  • 起動等パフォーマンスの改善

ようやく、タグ入力の補完機能が付きました。出てきた候補から選んでタップするだけでタグを入力できます。
候補に表示されるタグは、ブックマークの既存タグと、自分のブクマのタグ一覧を取得して合わせて表示しています。これまでのブックマークで多数のタグを使用している場合、候補が出るのが若干遅くなる場合があります(はてブからのタグ取得に時間がかかります)。
Windows Phone Storeからダウンロード:
http://www.windowsphone.com/ja-jp/store/app/natehar-express/95acd3a5-8e04-4a83-9c47-0e755050dd85
http://www.windowsphone.com/ja-jp/store/app/natehar-express/95acd3a5-8e04-4a83-9c47-0e755050dd85
アプリ詳細について、

Windows Phone 8.1用はてなブックマーク登録アプリNatehaR.Expressを公開しました - Neutral Scent
http://d.hatena.ne.jp/kaorun/20140510/1399709947

買ってみた:車を始動できるスターター機能付きモバイルバッテリー チャージアンドスターター(チャースタ)


うちの車わりとバッテリーに厳しく、とくに何かしたわけでなくても、冬場に数回バッテリーが上がります。
まー、非力な1.2Lのイタ車(痛く無い方)にサイバーナビとかETCとか電装系満載なのも良く無いわけですが...。
そんなわけで、冬場はロードサービスとお友達「あ、去年もでしたね」とか言われる始末、結果バッテリースターター系のグッズも幾つか試したのですが、あまり芳しくありませんでした。

そう思いつつも色々試したくなるのはガジェオタの性、スターターにも使えるモバイルバッテリーがあると聞いて、すぐには手を出さなかったモノの、どうなのかなー、と様子を探っていました。で、ん?これはよさそうじゃない? と思えた製品が出てきたので、購入トライしてみたわけです。

今回購入したのは、チャージ&スターター、略称チャースタという製品。
公式ページ: http://charge-and-starter.com/
Amazonで1万5000円ぐらいですかね。ちょっとお高めですが、この手のカー用品だとこんなものかな、という感じ。
ちょっと大き目のモバイルバッテリーに専用バッテリー始動用ジャンブケーブルがぶっ刺さる仕組み。リチウムイオンで8000mAhでふつうのLEDライト搭載モバイルバッテリーとしても利用できます。
見た目がこちら、

パッケージとかもわりとしっかりしてます、

コネクタ類はこういう状態、

で、こんな感じで車のバッテリーのターミナルに接続して始動すれば、エンジンがかかる、というわけです。

説明としては、以上終了という感じ。
あっけないぐらい普通に使えます。こんなちっこいバッテリーでも何気なくエンジンが始動。
今年の大雪の直後にプチ切れて購入したので、実は購入してから3か月ほど経ってますが、今年はもうバッテリーが瀕死らしく4回ほどお世話になってます(どんだけやねん)。その間2回は間に充電してません。始動に使っても1か月放置しても充電ゲージはほぼ減らない感じ、車に乗せっぱなしです。

基本的なスペックは、

容量: 8000mAh
重量: 300g
外寸: 131x75x25mm
出力: 5V/2A, 12V/200〜400A
充電: 専用ACアダプタ, 約4時間
付属品: USB充電用三又ケーブル(miniUSB/microUSB/Apple Dockコネクタ), ACアダプター, ジャンプスタート用ケーブル, アクセサリソケット(シガーライター)充電ケーブル

これまでの鉛シールドバッテリーなどを使用したポータブルスターターと比べると格段にコンパクトかつメンテフリーなので、車に乗せっぱなしにしても邪魔にも無駄にもなりませんし、LiIonだけに過放電などになることもないでしょう、普通のスマフォ用モバイルバッテリーや超長時間利用できるLEDランプにもなるので、実用性も高く出先での安心感も上がります。
製品としての見た目もしっかりしており、色の好みはあるかもしれませんが、この手の製品としては上質な部類かと思います。うちにあるQi対応モバイルバッテリー達のような中国産でございます、といった雰囲気もありません(素材からして違う感じ)。それから、付属品(ジャンプケーブルやACアダプタ等)を別売りしてるのもいいですね。

難点としては、

  • USBポートに市販のmicroUSBケーブルなどを接続した場合、充電出力が0.5Aまでしか上がらない点
  • 付属のUSB充電ケーブルがかさばること
  • ジャンプスターターケーブルはさらにかさばること

などがありますが、出力の件は付属のケーブルだと1.0A出たりしているのでおそらくUSBの結線や相手を見て出力をコントロールしているのだと思われます。ケーブルや機器との食い合わせがありそうですが、まぁとりあえずは急速充電には付属ケーブルが必要で、市販ケーブルでも最低限の充電は問題なくできる、という感じで。
とりあえずケーブル類が邪魔なので小さなポーチに入れて車に載せてあります。
ちなみに知人に勧めたところ、3.5L V6の国産車でも問題なく使用できたとのこと。

追記: Wiredに同種の製品が載ってましたが、なんとなくこれケーブル類が似てるので製造元一緒なんじゃないかという気がしています。
クルマのジャンプスタートにも使えるモバイルバッテリー(ただしノートPCは不可) « WIRED.jp
http://wired.jp/2014/04/28/juno-power-jump/
さらに追記: 上記記事の製品がチャージ&スターター Lite(チャースタLite)として発売されたようです。容量が一回り小さくなって、軽量化したようですね。
公式ページ: http://charge-and-starter.com/charge-and-starter-lite/
Amazon: チャージ&スターター Lite (オレンジ/ピンク/ブルー)

AsyncOAuthとWebAuthenticationBrokerで作るuniversal App用Twitter APIラッパークラス

世界同時WP&W8 Devハッカソンイベント//Publish/、がりがり書いてたアプリが結局仕様的に簡単には実現できなそうな袋小道に迷い込んでしまったため、結果出ませんでした、無念...。

とはいえ、会場で結構話題になっていた、universal App(特にWindows Phone版)でのWebAuthenticationBrokerの挙動をまるっとラップして、AsyncOAuthと合わせてモダンにさくっとTwitterアプリが作れるラッパークラスを書いてみたので、universalでTwitterアプリやってみたい人のご参考になれば幸いです。ベースが切り張なので突っ込みどころはあると思いますが。
HTTP周りはAsyncOAuthがまるっと包んでくれますし、OS標準のWebAuthenticationBrokerのおかげでWebBrowserコントロールを張ってごにょごにょする必要もなく、今なら最短に近い組み合わせではないかと思います。

public class TwitterHandler
{
    //neue cc - AsyncOAuth - C#用の全プラットフォーム対応の非同期OAuthライブラリ
    //http://neue.cc/2013/02/27_398.html
    //AsyncOAuth/AsyncOAuth.ConsoleApp/Twitter.cs at master · neuecc/AsyncOAuth
    //https://github.com/neuecc/AsyncOAuth/blob/master/AsyncOAuth.ConsoleApp/Twitter.cs
    internal const string API_CONSUMER_TOKEN = "";
    internal const string API_CONSUMER_SECRET = "";
    internal const string API_CONSUMER_CALLBACK_URL = "";

    private readonly OAuthAuthorizer authorizer;
    private static RequestToken requestToken { get; set; }
    private static AccessToken accessToken { get; set; }
        
    public TwitterHandler()
    {
        AsyncOAuth.OAuthUtility.ComputeHash = (key, buffer) =>
        {
            var crypt = Windows.Security.Cryptography.Core.MacAlgorithmProvider.OpenAlgorithm("HMAC_SHA1");
            var keyBuffer = Windows.Security.Cryptography.CryptographicBuffer.CreateFromByteArray(key);
            var cryptKey = crypt.CreateKey(keyBuffer);

            var dataBuffer = Windows.Security.Cryptography.CryptographicBuffer.CreateFromByteArray(buffer);
            var signBuffer = Windows.Security.Cryptography.Core.CryptographicEngine.Sign(cryptKey, dataBuffer);

            byte[] value;
            Windows.Security.Cryptography.CryptographicBuffer.CopyToByteArray(signBuffer, out value);
            return value;
        };
        authorizer = new OAuthAuthorizer(API_CONSUMER_TOKEN, API_CONSUMER_SECRET);
    }

    public async Task Auth()
    {
        accessToken = null;
        var res = await authorizer.GetRequestToken("https://api.twitter.com/oauth/request_token");
        requestToken = res.Token;

        var uriAuthorize = new Uri(authorizer.BuildAuthorizeUrl("https://api.twitter.com/oauth/authorize", requestToken));
        var uriCallback = new Uri(API_CONSUMER_CALLBACK_URL);
#if WINDOWS_PHONE_APP
        WebAuthenticationBroker.AuthenticateAndContinue(uriAuthorize, uriCallback, null, WebAuthenticationOptions.None);
#else // !WINDOWS_PHONE_APP
        var webAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None, uriAuthorize, uriCallback);
        if (webAuthenticationResult.ResponseStatus == WebAuthenticationStatus.Success)
        {
            Debug.WriteLine(webAuthenticationResult.ResponseData.ToString());
            await GetAccessToken(webAuthenticationResult.ResponseData.ToString());
        }
        else if (webAuthenticationResult.ResponseStatus == WebAuthenticationStatus.ErrorHttp)
        {
            MessageDialog ErrMsg = new MessageDialog(string.Format("HTTP Error returned by AuthenticateAsync() : " + webAuthenticationResult.ResponseErrorDetail.ToString()), "Sorry");
            await ErrMsg.ShowAsync();
        }
        else
        {
            MessageDialog ErrMsg = new MessageDialog(string.Format("Error returned by AuthenticateAsync() : " + webAuthenticationResult.ResponseStatus.ToString()), "Sorry");
            await ErrMsg.ShowAsync();
        }
#endif // !WINDOWS_PHONE_APP
    }

#if WINDOWS_PHONE_APP
    //Windows Phone Web authentication broker sample in C#, C++, JavaScript for Visual Studio 2013
    //http://code.msdn.microsoft.com/wpapps/Web-Authentication-d0485122
    //How to continue your Windows Phone Store app after calling an AndContinue method (Windows)
    //http://msdn.microsoft.com/en-us/library/dn631755.aspx
    //How to Use the WebAuthenticationBroker for oAuth in a Windows Phone Runtime WP8.1 App | .NET Zone
    //http://dotnet.dzone.com/articles/how-use
    public async void ContinueWebAuthentication(WebAuthenticationBrokerContinuationEventArgs args)
    {
        WebAuthenticationResult result = args.WebAuthenticationResult;

        if (result.ResponseStatus == WebAuthenticationStatus.Success)
        {
            await GetAccessToken(result.ResponseData.ToString());
        }
        else if (result.ResponseStatus == WebAuthenticationStatus.ErrorHttp)
        {
            MessageDialog ErrMsg = new MessageDialog(string.Format("There was an error connecting to Twitter: \n {0}", result.ResponseErrorDetail.ToString()), "Sorry");
            await ErrMsg.ShowAsync();
        }
        else
        {
            MessageDialog ErrMsg = new MessageDialog(string.Format("Error returned: \n{0}", result.ResponseStatus.ToString()), "Sorry");
            await ErrMsg.ShowAsync();
        }
    }
#endif // WINDOWS_PHONE_APP

    private async Task GetAccessToken(string webAuthResultResponseData)
    {
        string responseData = webAuthResultResponseData.Substring(webAuthResultResponseData.IndexOf("oauth_token"));
        string oauthVerifier = null;
        String[] keyValPairs = responseData.Split('&');

        for (int i = 0; i < keyValPairs.Length; i++)
        {
            String[] splits = keyValPairs[i].Split('=');
            switch (splits[0])
            {
                case "oauth_verifier":
                    oauthVerifier = splits[1];
                    break;
            }
        }
        var accessTokenResponse = await authorizer.GetAccessToken("https://api.twitter.com/oauth/access_token", requestToken, oauthVerifier);

        accessToken = accessTokenResponse.Token;
    }

    public HttpClient CreateTwitterHttpClient()
    {
        if (!IsAuthrized)
        {
            return null;
        }
        return OAuthUtility.CreateOAuthClient(API_CONSUMER_TOKEN, API_CONSUMER_SECRET, new AccessToken(accessToken.Key, accessToken.Secret));
    }

    public bool IsAuthrized
    {
        get { return accessToken != null && !string.IsNullOrWhiteSpace(accessToken.Key) && !string.IsNullOrWhiteSpace(accessToken.Secret); }
    }

    // Example API Call Handlers
    // AsyncOAuth/AsyncOAuth.ConsoleApp/Twitter.cs at master &middot; neuecc/AsyncOAuth
    // https://github.com/neuecc/AsyncOAuth/blob/master/AsyncOAuth.ConsoleApp/Twitter.cs
    public async Task<string> GetTimeline(int count, int page)
    {
        var client = CreateTwitterHttpClient();

        var json = await client.GetStringAsync("https://api.twitter.com/1.1/statuses/home_timeline.json?count=" + count + "&page=" + page);
        return json;
    }

    // Your Twitter Application must have Read & Write permission in Twitter API for Post handlers
    public async Task<string> PostUpdate(string status)
    {
        var client = CreateTwitterHttpClient();

        var content = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("status", status) });

        var response = await client.PostAsync("https://api.twitter.com/1.1/statuses/update.json", content);
        var json = await response.Content.ReadAsStringAsync();
        return json;
    }
}

Windows Phone版では、認証を終わらせるために、アプリが再アクティブ化されるため、App.xaml.csに以下のようなコードを追加する必要があります。(ViewModel.TwitterHandler〜は個々の実装に合わせて変更)

    public sealed partial class App : Application
    {
        protected override void OnActivated(IActivatedEventArgs args)
        {
            base.OnActivated(args);
#if WINDOWS_PHONE_APP
            if (args is WebAuthenticationBrokerContinuationEventArgs)
                ViewModel.TwitterHandler.ContinueWebAuthentication(args as WebAuthenticationBrokerContinuationEventArgs);
#endif // WINDOWS_PHONE_APP
        }
    }

あとは、適当なところでTwitterHandlerのインスタンスを作ってAuth()を呼んで、認証終わったらAPIをたたいていくだけです。詳しくはコード冒頭のneuecc先生のリンク等を参照のこと。
ふつうのuniversal AppテンプレートにnugetでAsyncOAuthを入れて、上記のコードを組み込み、あとは細かいAPIを実装していくだけで、わりとシンプルにTwitterアプリを作成できるかと思います。
(現状、nugetでAsyncOAuthをuniversalのWP8.1アプリに組み込むと依存関係の不足で起動時にFileNotFoundが出るかもしれません。その場合、nugetでさらにBCL Async(Microsoft Async)を追加してみてください)

Windows Phone 8.1用はてなブックマーク登録アプリNatehaR.Expressを公開しました


Windows Phone 8.1の新機能、共有コントラクト(Androidで言うIntentですね)を利用したはてなブックマークの登録専用アプリ、NatehaR.ExpressWindows Phone Storeにて公開しました。無料です。
基本的には、8.1対応の各種アプリからURLやWebページの共有(Share)を選択し、そこからはてブへブクマするだけ、というアプリです。
これまでは、Windows Phoneからブクマするには、Webページか別途他のアプリを使うしかありませんでしたが、これからはOS標準のInternet Explorerer 11 Mobileはもちろん、海外製のTwitterクライアントや定番RSSリーダーNextGen Readerなどからもダイレクトにはてブへ登録することが可能になります。
Windows Phone Storeからダウンロード:
http://www.windowsphone.com/ja-jp/store/app/natehar-express/95acd3a5-8e04-4a83-9c47-0e755050dd85
http://www.windowsphone.com/ja-jp/store/app/natehar-express/95acd3a5-8e04-4a83-9c47-0e755050dd85

スクリーンショット:

現在の主な機能:

  • 共有コントラクトからのブックマーク登録
  • スペース(空白)で直前の文字列をタグ表記に変換
  • 英語ローカライズ済(誰が使うのか?)

のみとなっています。
なかなか手が回らず、非常にシンプルなアプリになってしまいましたが、タグの取得はできているので、タグの選択や既存データの編集などは直近で対応予定です。
Windows Universal Appの習作にもなっているので、こちらをベースにMetroHatenaR後継のフルスペックWP8.1アプリにも手を付けていきたいと思っています。

Privacy Policy

(This contents translated by Machine Translation)
NatehaR (this app) is the application running and operating the personal information using your computer and the Microsoft account.

  • Use of personal information

This app is within the range necessary to achieve the purpose of use of the following personal information, we will use.

Involves advance informed consent to use personal information in the purposes of the following:

  1. to confirm that the user intended to service users login information
  2. services available continuously on etc Cookie needed save connection information.
  3. save for the convenience of the application search, browsing history, browsing, configuration information
  • Personal information view, edit, delete

The login information is stored in our app from within the app removal is possible.

Also, concomitantly with the login information and saved information is removed along with the login information delete.

  • Provision to third parties of personal information

If this app is prescribed personal information protection laws without first obtaining the consent personal information disclosure requests from Parties legally binding except, and will not be provided to third parties.

  • Scope of application

Our app is only client applications to use the various services and a bookmark providing by Hatana Inc. in the assumes no responsibility for the protection of personal information used in the service of Hatena.

This privacy policy applies only to within the operating range of our app. On personal information protection in the site viewed from our app assumes no responsibility.

.NETのCookieContainerをシリアライズする

.NETのCookieContainerってなんでSerializeできないんですかね?
HttpWebRequestでcookieを横取りしたり、そもそもそんなことしたいと思うのが筋悪なのか? と、思いつつ幾数年...。
8.1のWindows Universal Appを作っていたら、なんとHttpCookieManagerなどと言うモノが新設されており、こりゃ楽になる! ...と思ったのもつかの間、今作ってるアプリはHTTP関連が全部PCLに入っていたという。
そんなわけで、今更感もありますが、あらためてCookieContainerとは何者かを見直して、この値をDataContractSerializerでシリアライズして保存可能なCookieContainrラッパークラスを実装してみました。
とりあえずDataContractSerializerを使っていますが、XmlSerializerやSerializable用にも書き換えられると思います。
今の所ちゃんと動いているようですが、何かおかしなところがあればツッコミお願いします。

[DataContract]
public class DataContractCookieContainer
{
    private DataContractCookieContainer()
    {
        this.CookieContainer = new CookieContainer();
    }

    public DataContractCookieContainer(string targetDomain, CookieContainer cookieContainer)
    {
        this.CookieContainer = cookieContainer;
        this.Domain = targetDomain;
    }

    // Order is very important factor in deserializing order
    [DataMember(Order=0)]
    public string Domain { get; set; }
    [IgnoreDataMember]
    public CookieContainer CookieContainer { get; set; }
    [DataMember(Order=1)]
    private List<DataContractCookie> Cookies
    {
        get
        {
            if (string.IsNullOrWhiteSpace(Domain) || !Domain.StartsWith("http"))
                throw new InvalidOperationException("Domain must be set Uri initializable URL start with http...");
            var cookielist = new List<DataContractCookie>();
            var collection = this.CookieContainer.GetCookies(new Uri(Domain));
            foreach (Cookie c in collection)
            {
                cookielist.Add(new DataContractCookie(c));
            }
            return cookielist;
        }
        set
        {
            this.CookieContainer = new CookieContainer();
            if (value != null && !string.IsNullOrWhiteSpace(Domain))
            {
                var collection = new CookieCollection();
                foreach (var cookie in value)
                {
                    collection.Add(cookie.ToCookie());
                }
                this.CookieContainer.Add(new Uri(Domain), collection);
            }
        }
    }

    [DataContract]
    private sealed class DataContractCookie
    {
        // Error message when you serialize a class by using the XMLSerializer class: "System.InvalidOperationException"
        // http://support.microsoft.com/kb/330592/en
        // http://support.microsoft.com/kb/330592/ja (Japanese)
        private DataContractCookie()
        {
        }

        public DataContractCookie(Cookie cookie)
        {
            Comment = cookie.Comment;
            CommentUri = cookie.CommentUri;
            Discard = cookie.Discard;
            Domain = cookie.Domain;
            Expired = cookie.Expired;
            Expires = cookie.Expires;
            HttpOnly = cookie.HttpOnly;
            Name = cookie.Name;
            Path = cookie.Path;
            Port = cookie.Port;
            Secure = cookie.Secure;
            Value = cookie.Value;
            Version = cookie.Version;
        }

        public Cookie ToCookie()
        {
            var cookie = new Cookie()
            {
                Name = Name,
                Value = Value,
                Path = Path,
                Domain = Domain,
                Comment = Comment,
                CommentUri = CommentUri,
                Discard = Discard,
                Expired = Expired,
                Expires = Expires,
                HttpOnly = HttpOnly,
                Port = Port,
                Secure = Secure,
                Version = Version,
            };
            return cookie;
        }

        [DataMember]
        public string Comment { get; set; }
        [DataMember]
        public string CommentUrl { get; set; }
        // Uri == null cause exception
        [IgnoreDataMember]
        public Uri CommentUri
        {
            get { return string.IsNullOrWhiteSpace(CommentUrl) ? null : new Uri(CommentUrl); }
            set { CommentUrl = (value == null) ? "" : value.ToString(); }
        }
        [DataMember]
        public bool Discard { get; set; }
        [DataMember]
        public string Domain { get; set; }
        [DataMember]
        public bool Expired { get; set; }
        [DataMember]
        public DateTime Expires { get; set; }
        [DataMember]
        public bool HttpOnly { get; set; }
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public string Path { get; set; }
        [DataMember]
        public string Port { get; set; }
        [DataMember]
        public bool Secure { get; set; }
        [DataMember]
        public string Value { get; set; }
        [DataMember]
        public int Version { get; set; }
    }
}

使い方は、↓こんな感じ。
DataContractCookieContainerはアプリで使用するcookieドメインのURL(http〜)を指定して初期化してください。クッキーを列挙するのにCookieContainer.GetCookies(uri)を使うしか無いもので。(緩募: Uriで全ドメインを指定する方法)

[DataContract]
public class SampleSettings
{
    private SampleSettings()
    {
    }

    public SampleSettings(string targetDomain, CookieContainer cookieContainer = null)
    {
        CookieData = new DataContractCookieContainer(targetDomain, cookieContainer ?? new CookieContainer());
    }

    public string ToXml()
    {
        try
        {
            var serializer = new DataContractSerializer(typeof(SampleSettings));
            using (var ms = new MemoryStream())
            {
                serializer.WriteObject(ms, this);
                return Encoding.UTF8.GetString(ms.ToArray(), 0, (int)ms.Length);
            }
        }
        catch (Exception ex)
        {
            Debug.Assert(false, "Something Error Handling");
            return string.Empty;
        }
    }

    public static SampleSettings ParseXml(string xml)
    {
        if (string.IsNullOrWhiteSpace(xml))
            return null;
        var bytes = Encoding.UTF8.GetBytes(xml);
        try
        {
            var serializer = new DataContractSerializer(typeof(SampleSettings));
            using (var stm = new MemoryStream(bytes))
            {
                return serializer.ReadObject(stm) as SampleSettings;
            }
        }
        catch (Exception ex)
        {
            Debug.Assert(false,"Something Error Handling");
            return new SampleSettings();
        }
    }

    [DataMember]
    public string Username { get; set; }
    [DataMember]
    public int UserID { get; set; }
    [DataMember]
    private DataContractCookieContainer CookieData { get; set; }
    [IgnoreDataMember]
    public CookieContainer Cookie 
    {
        get { return CookieData.CookieContainer; }
        set { CookieData.CookieContainer = value; } 
    }
}

順に書いて行ってわかったのは、どうもCookieContainerがシリアライズできないのは、最終的に各cookieが格納されているCookieクラスのCommentUriメンバーにnullが入ってるのが原因っぽいですね。そんなことかよ、という...。もしかしたらもっと深遠な理由があるのかもですが。