JSON データのデシリアライズにおける日付型の変換

(軽めの jQuery Advent Calendar 2012 の 23 日目です。)

次のようなコードで jQuery の getJSON 関数で JSON Web サービスを呼び出すと、
コールバック関数の引数 data には JSON データがデシリアライズ (パース) されたオブジェクトが渡されます。
ここで、data に含まれている値の型について考えてみます。


$.getJSON("Services/Products")
    .done(function (data) {
        // 取得したデータに対する処理
        var value = data[0].prop1; // 何型?
    });


JSON 形式では、オブジェクト、配列、文字列、数値、ブール値、null の記法が定義されており、
それぞれ JavaScript の Object, Array, String, Number, Boolean, null の値に対応します。
しかし、Date に対応する日付型は定義されていません。

getJSON 関数では、JSON 形式の文字列をデシリアライズするために
既定では $.parseJSON 関数 (JSON.parse 関数を IE7 以前のために拡張したもの) を使用します。
この場合、たとえ ISO 8601 の日付形式である "2012-12-24T14:59:59.999Z" のような文字列であっても、
自動的に Date 型に変換されることはありません。

しかし jQuery の Ajax 機能の設定値の中に converters というものがあり、
デシリアライズ方法をカスタマイズできるようになっています。
この機能は、jQuery 1.5で追加されたようです。

今回は、この converters を利用して日付を表す文字列を Date 型に変換してみます。

■ ISO 8601 の日付形式の場合

converters を設定するには、Ajax で通信を開始する前に $.ajaxSetup 関数を呼び出します。
キー "text json" に対してデシリアライズ関数を指定します。
次のように実装します。


$.ajaxSetup({
    converters: {
        "text json": function (text) {
            return JSON.parse(text, reviveDate);
        }
    }
});

function reviveDate(key, value) {
    if (value == null ||
        value.constructor !== String ||
        value.search(/^\d{4}-\d{2}-\d{2}/g) === -1)
        return value;
    return new Date(value);
}


実は JSON.parse 関数の第 2 引数には、キーと値を受け取って任意の値に変換する関数を指定できます。
この reviveDate 関数の中で、正規表現で文字列の先頭部分が YYYY-MM-DD に一致した場合は Date 型に変換しています。

次のように $.ajaxSettings.converters に直接上書きしても同様の効果です。

$.ajaxSettings.converters["text json"] = function (text) {
    return JSON.parse(text, reviveDate);
};

また、$.getJSON 関数ではなく $.ajax 関数を使う場合は、引数 settings に converters を指定できます。

では実際に次の JSON データをデシリアライズして、確認のためにブラウザーで表示してみます。

JSON データ (ISO 8601 形式)

表示サンプル

ここでは、先頭部分が YYYY-MM-DD の形式かどうかを判定しましたが、
他にも例えば「key がサフィックスとして "Date" を持つ (例: startDate)」という判定条件も考えられるでしょう。

なお、IE で Date のコンストラクターに ISO 8601 の日付形式を指定できるのは、IE9 以降です。

■ ASP.NET の場合

JSON Web サービスを WCF サービスなどで実装する場合、
.NET Framework の DateTime 型のオブジェクトは
"\/Date(1356361199999)\/" または "\/Date(1356361199999+0900)\/" のような形式にシリアライズされます。

したがって、先ほどの reviveDate 関数を次のように実装します。


function reviveDate(key, value) {
    if (value == null ||
        value.constructor !== String)
        return value;
    var m = /^\/Date\((\d+)(.+)?\)\/$/g.exec(value);
    if (!m) return value;
    return new Date(parseInt(m[1]));
}


先ほどと同じ意味のデータを WCF サービスで返すと次のようになります。表示結果は同じです。

JSON データ (ASP.NET 形式)

■ その他の場合

その他にも、日付の形式が案件で指定されている場合や、ISO 8601 形式を IE8 でも使いたい場合などが考えられますが、
同様に変換ロジックをカスタマイズすれば実現できると思います。

例えば、"2012年12月24日" という形式の場合は、次のようにして実現できます。


function reviveDate(key, value) {
    if (value == null ||
        value.constructor !== String)
        return value;
    var m = /^(\d{4})年(\d{1,2})月(\d{1,2})日$/g.exec(value);
    if (!m) return value;
    return new Date(parseInt(m[1]), parseInt(m[2]) – 1, parseInt(m[3]));
}


これで、次のようなデータを変換できます。

JSON データ (カスタム形式)

作成したサンプル
JSON Converter Sample 1 (ISO 8601 形式)
JSON Converter Sample 2 (ASP.NET 形式)
JSON Converter Sample 3 (カスタム形式)

バージョン情報
JSON.parse 関数: Internet Explorer 8 以降、その他の主要なブラウザー
ISO 8601 形式を受け取る Date のコンストラクター: Internet Explorer 9 以降、その他の主要なブラウザー

参照
jQuery.ajax()
Extending Ajax: Prefilters, Converters, and Transports
JSON の紹介
Date and Time Formats (W3C)
jQuery parseJSON automatic date conversion for Asp.net and ISO date strings

Google URL Shortener API の利用

Google の短縮 URL サービスである Google URL Shortener (goo.gl) には、
Web サービス API として Google URL Shortener API が用意されています。
今回はこの Google URL Shortener API を .NET のコードから利用して URL を短縮・展開してみます。
技術的には、Web クライアントと JSON 形式について取り扱います。
 
URL の短縮
<第 1 段階>
まずは通信を成功させましょう。
Getting Started にある通り、POST メソッドで JSON 形式のデータを送信します。結果も JSON 形式で受信します。
この場合、WebClient.UploadString メソッドを利用すると簡単です。

private const string UrlShortenerAddress = "https://www.googleapis.com/urlshortener/v1/url&quot;;
private const string OriginalUrl = "https://sakapon.wordpress.com/&quot;;

static void Main()
{
   
string jsonRequest = "{\"longUrl\": \"" + OriginalUrl + "\"}";
   
string jsonResponse = ShortenUrl(jsonRequest);
}

// Google URL Shortener API を呼び出して短縮 URL を取得します。
private static string ShortenUrl(string jsonRequest)
{
   
using (var client = new WebClient { Encoding = Encoding.UTF8 })
    {
        client
.Headers["Content-Type"] = "application/json";

        return client.UploadString(UrlShortenerAddress, jsonRequest);
    }
}


 
取得結果は次のようになります。
{
  "kind": "urlshortener#url",
}
注意点
(1) Encoding プロパティに UTF-8 を指定しておかないと、URL に日本語が含まれている場合に失敗します。
(2) この API から返される JSON 形式データの改行コードは LF (\n) です。
 
<第 2 段階>
次に、JSON 形式のデータをオブジェクトに変換して、短縮 URL を取得します。
JSON 形式を扱うライブラリとしては Json.NET もありますが、
ここでは標準ライブラリの DataContractJsonSerializer クラスを利用してみます。
System.Runtime.Serialization.dll への参照が必要になります。

private const string UrlShortenerAddress = "https://www.googleapis.com/urlshortener/v1/url&quot;;
private const string OriginalUrl = "https://sakapon.wordpress.com/&quot;;

static void Main()
{
   
var request = new UrlInfoRequest { LongUrl = OriginalUrl };
   
string jsonRequest = SerializeToJson(request);

    string jsonResponse = ShortenUrl(jsonRequest);

    var response = DeserializeFromJson<UrlInfoResponse>(jsonResponse);
   
string shortUrl = response.ShortUrl; // http://goo.gl/NCa9Y
}

// Google URL Shortener API を呼び出して短縮 URL を取得します。
private static string ShortenUrl(string jsonRequest)
{
   
using (var client = new WebClient { Encoding = Encoding.UTF8 })
    {
        client
.Headers["Content-Type"] = "application/json";

        return client.UploadString(UrlShortenerAddress, jsonRequest);
    }
}

// オブジェクトを JSON 形式にシリアル化します。
private static string SerializeToJson<T>(T obj)
{
   
var serializer = new DataContractJsonSerializer(typeof(T));
   
var stream = new MemoryStream();
    serializer
.WriteObject(stream, obj);
   
return Encoding.UTF8.GetString(stream.ToArray());
}

// JSON 形式のデータをオブジェクトに逆シリアル化します。
private static T DeserializeFromJson<T>(string jsonText)
{
   
var serializer = new DataContractJsonSerializer(typeof(T));
   
var stream = new MemoryStream(Encoding.UTF8.GetBytes(jsonText));
   
return (T)serializer.ReadObject(stream);
}

[DataContract]
public class UrlInfoRequest
{
    [
DataMember(Name = "longUrl")]
   
public string LongUrl { get; set; }
}

[DataContract]
public class UrlInfoResponse
{
    [
DataMember(Name = "kind")]
   
public string Kind { get; set; }
    [
DataMember(Name = "id")]
   
public string ShortUrl { get; set; }
    [
DataMember(Name = "longUrl")]
   
public string LongUrl { get; set; }
}


 
注意点
(1) エンティティを表すクラスには DataContractAttribute 属性が必要です。
(2) JSON 形式へのシリアル化の際、「/」はエスケープされて「\/」になります。ただし、動作に変わりはありません。
 
Json.NET での記述方法を見る限り、Json.NET を利用したほうが簡単のようです。
 
短縮 URL の展開
短縮 URL を展開する場合もほぼ同様に考えます。
この場合は GET メソッドでよいので、WebClient.DownloadString メソッドを利用します。

private const string UrlShortenerAddress = "https://www.googleapis.com/urlshortener/v1/url&quot;;
private const string ShortUrl = "http://goo.gl/NCa9Y&quot;;

static void Main()
{
   
string jsonResponse = ExpandUrl(ShortUrl);

    var response = DeserializeFromJson<UrlInfoResponse>(jsonResponse);
   
string originalUrl = response.LongUrl; // https://sakapon.wordpress.com/
}

// Google URL Shortener API を呼び出して短縮 URL を展開します。
private static string ExpandUrl(string shortUrl)
{
   
using (var client = new WebClient { Encoding = Encoding.UTF8 })
    {
       
return client.DownloadString(string.Format("{0}?shortUrl={1}", UrlShortenerAddress, shortUrl));
    }
}

// その他のコードは省略します。


 
ちなみに、Google URL Shortener API for .NET というものが存在するようです。
 
バージョン情報
.NET Framework 4
 
参照
 
カテゴリー: .NET Framework. タグ: . Leave a Comment »