WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

日付時刻と JSON 文字列

by WebSurfer 2017年8月27日 15:01

JavaScript の Date オブジェクトや .NET Framework の DateTime オブジェクトの JSON 文字列への変換について調べましたので備忘録として書いておきます。(逆の、下の表の JSON 文字列を Newtonsoft.Json および System.Text.Json 名前空間の JsonSerializer クラスを使って DateTime オブジェクトにデシリアライズする話は別の記事「JSON 文字列の日付時刻のデシリアライズ」に書きましたので興味があれば見てください)

ASP.NET ajax によるパース結果

JSON の紹介よると、その記事に書いてある value に直接設定できるのは string, number, object, array, true, false, null だけということですので、JavaScript の Date オブジェクトや .NET Framework の DateTime オブジェクトは string に変換して JSON 文字列に設定せざるを得ません。

変換は自力で行わなくても、例えば、JavaScript なら JSON.stringify() メソッド、.NET Framework なら DataContractJsonSerializer クラスを利用すれば自動的に変換してくれます。

その他、JavaScriptSerializer クラス、ASP.NET Web サービス、WCF、ASP.NET Web API でも DateTime 型を自動的に文字列に変換してくれますが、それぞれどのような結果になるかを以下にまとめておきます。

No. 方法 結果
(1) JSON.stringify() 2017-02-01T03:15:45.000Z
(2) DataContractJsonSerializer \/Date(1503727168573+0900)\/
(3) WCF 上記 (2) の結果と同じ
(4) JavaScriptSerializer \/Date(1030806000000)\/
(5) ASP.NET Web サービス 上記 (4) の結果と同じ
(6) ASP.NET Web API (注) 2017-08-26T15:39:32.6330349+09:00

(注): Newtonsoft.Json と同じ。.NET Framework 版および Core v2.x 版の ASP.NET Web API はシリアライザに Newtonsoft.Json を使っています。ASP.NET Core 3.x 以降の Web API は System.Text.Json 名前空間JsonSerializer クラスがシリアライズに用いられるようになりました。なお、シリアライズの結果は (6) と同じになります。

以下に上記 (1) ~ (6) のそれぞれの検証方法や備忘録として残しておいた方がよさそうな情報を書いておきます。

(1) JSON.stringify() メソッド

ECMAScript 2017 Language Specification (ECMA-262, 8th edition, June 2017) の 20.3.1.16 Date Time String Format のセクションによると、ISO 8601 Extended Format 即ち:

YYYY-MM-DDTHH:mm:ss.sssZ

という形式に変換することになっているそうです。

全てのブラウザで確認したわけではありませんが、IE11, Chrome 60.0.3112.113, Firefox 55.0.3 では確かにその通りになるのを確認しました。

なお、JavaScript の Date オブジェクトはローカルタイムとして扱われまずが、JSON.stringify() メソッドで文字列に変換すると UTC になるので注意してください(YYYY-MM-DDTHH:mm:ss.sssZ の末尾の Z は UTC であることを意味します)。

例えば、以下のコードを実行すると、

var dateTime = new Date(2017, 1, 1, 12, 15, 45);
var dateObject = { DateTime: dateTime };
var dateJson = JSON.stringify(dateObject);

dateJson は {"DateTime":"2017-02-01T03:15:45.000Z"} という JSON 文字列になります。

Date コンストラクタの引数で月を表す整数値は 0 (1月) から 11 (12月) なので、1 ⇒ 02 という結果になっています。時間 12 が 03 になるのは東京ローカルタイムが UTC に変換されるためです。

(2) DataContractJsonSerializer クラス

DataContractJsonSerializer クラスを使うと、別の記事「w2ui Grid」に書きましたように、.NET Framework の DateTime 型は:

\/Date(1503727168573+0900)\/

のような文字列に変換されます。

この例の最初の数字 1503727168573 は UTC 1970 年 1 月 1 日午前 0 時からカウントしたミリ秒、+0900 はオプションで、ローカルタイムの UTC からの時差だそうです。

詳しくは以下の MSDN ライブラリの記事「スタンドアロン JSON のシリアル化」の「高度な情報 / DateTime ワイヤ形式」のセクションの説明を見てください。

(3) WCF

先の記事「WCF と jQuery AJAX」の CarService.svc.cs のコードに以下のメソッドを追加して試してみました。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Text;

namespace WebApplication1
{
    // web.config で aspNetCompatibilityEnabled="true" と設定され
    // ているので [AspNetCompatibilityRequirements(...)] が必要
    [ServiceContract(Namespace = "")]
    [AspNetCompatibilityRequirements(RequirementsMode = 
        AspNetCompatibilityRequirementsMode.Allowed)]
    public class CarService
    {
        // ・・・中略・・・

        [OperationContract]
        [WebGet(ResponseFormat = WebMessageFormat.Json,
            BodyStyle = WebMessageBodyStyle.Wrapped)]
        public DateTimeJsonTest GetToday()
        {
            DateTimeJsonTest today = new DateTimeJsonTest();
            today.Today = DateTime.Now;
            return today;
        }
    }

    // ・・・中略・・・

    [DataContract]
    public class DateTimeJsonTest
    {
        [DataMember]
        public DateTime Today { set; get; }
    }
}

ブラウザ から上記コードの GetToday を要求すると以下のような JSON 文字列が返されます:

{"GetTodayResult":{"Today":"\/Date(1503727168573+0900)\/"}}

すなわち、上記「(2) DataContractJsonSerializer クラス」の場合と同じになります。(内部的に DataContractJsonSerializer クラスを使って変換しているのであろうと思います。未確認ですが・・・)

(4) JavaScriptSerializer クラス

ASP.NET MVC の Controller クラスの Json メソッドの内部で .NET Framework のオブジェクトを JSON 文字列にシリアライズするために JavaScriptSerializer クラスが用いられているそうです。

先の記事「jQuery.ajax で JSONP」に JavaScriptSerializer クラスを使ってシリアル化を行っている例がありますので、これに少し手を加えて検証してみました。

その記事のコードを見てください。Controller の Jsonp アクションメソッドでは Address テーブルから AddressID, AddressLine1, City フィールドのデータを取得して匿名クラスのオブジェクトを作り、それを JavaScriptSerializer クラスの Serialize メソッドで JSON 文字列にシリアライズしています。

Address テーブルには ModifiedDate という datetime 型のフィールドがありますのでそれを匿名クラス address に追加してシリアライズしてみました。

Serialize(address) の結果は以下のような文字列となります。

{"AddressID":25,"AddressLine1":"9178 Jumping St.","City":"Dallas","ModifiedDate":"\/Date(1030806000000)\/"}

即ち、DateTime 型の変換結果は上記「(2) DataContractJsonSerializer クラス」とほぼ同じで、オプションのローカルタイムの UTC からの時差(上の例では +0900)が省略された形になっています。

(5) ASP.NET Web サービス

先の記事「ASP.NET AJAX と Web サービス」のコードに以下のメソッドを追加して試してみました。

<%@ WebService Language="C#" Class="CarService" %>

using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Web.Script.Services;
using System.Collections.Generic;
using System.Linq;

// ・・・中略・・・

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
public class CarService : WebService
{
    // ・・・中略・・・

    [WebMethod]
    public DateTimeJsonTest GetToday()
    {
        DateTimeJsonTest today = new DateTimeJsonTest();
        today.Today = DateTime.Now;
        return today;
    }
}

public class DateTimeJsonTest
{
    public DateTime Today { set; get; }
}

上記 GetToday() メソッドを、要求ヘッダに Content-Type: application/json を追加して POST 要求すると(注)以下のような JSON 文字列が返されます:

{"d":{"__type":"DateTimeJsonTest","Today":"\/Date(1503712714770)\/"}}

(注)ASP.NET Web サービスの制約上 POST 要求しないとエラーになります。Content-Type を指定しない、または application/x-www-form-urlencoded とすると XML 形式で返されます。XML の場合は形式は上記とは異なり 2017-08-26T10:36:27.0809765+09:00 のようになります。

即ち、上記「(4) JavaScriptSerializer クラス」と同じ形式の文字列に変換されます(+0900 も同じく省略されています)。

ASP.NET AJAX を利用すると、\/Date(1503712714770)\/ という形式の文字列は、Date 型の JavaScript オブジェクトに自動的に変換されます。具体例は以下の通りです。

記事「ASP.NET AJAX と Web サービス」の「(2) JavaScript (097_ASPNETAjaxAndWebService.js)」のセクションに記載されているコードを以下のように書き換えます。

var serviceProxy;

// プロキシの初期化とコールバック関数の設定
function pageLoad() {
    serviceProxy = new CarService();
    serviceProxy.set_defaultSucceededCallback(Succeeded);
    serviceProxy.set_defaultFailedCallback(Failed);
}

// ボタンクリックで呼び出されるサービスメソッド  
function getToday() {
    serviceProxy.GetToday();
}

// AJAX 通信が成功したときに呼び出され、戻ってきたデータ
// を処置するコールバック関数。
// 引数 data は JSON 文字列ではなく、DateTimeJsonTest 型
// オブジェクト。それから Today プロパティで取得できるの
// は、文字列ではなく Date 型に変換済みのオブジェクト。
function Succeeded(data) {
    $('#output').empty();
    var now = data.Today;  // now は Date 型オブジェクト
    $('#output').append(
        "<p>" + now.getFullYear() + "年" +
        (now.getMonth() + 1) + "月" + now.getDate() + "日" +
        now.getHours() + "時" + now.getMinutes() + "分" +
        now.getSeconds() + "秒</p>"
    );
}

// ・・・以下略・・・

上の getToday() メソッドを「(3) apsx ページ (097_ASPNETAjaxAndWebService.aspx) 」のセクションに記載されているコードと同等な aspx ページから呼び出すと、Date 型オブジェクトが上記コードの now に取得でき、この記事の一番上の画像のとおり表示されます。

MSDN ライブラリの記事「スタンドアロン JSON のシリアル化」の「高度な情報 / DateTime ワイヤ形式」のセクションの説明によると、"UTC からの時差を示す部分(例: +0900)が指定されていない場合、時刻は UTC として逆シリアル化されます" とありますが、上記コードの結果はローカル時刻になります。

ASP.NET AJAX に代えて jQuery ajax を使う場合は自動的に Date オブジェクトには変換してくれませんので、自力でコードを書いて変換することになります。

具体例は以下の通りです。数字の部分を抜き出して JavaScript の Date コンストラクタの引数として渡し、Date オブジェクトを生成すれば ASP.NET AJAX と同じ結果が得られます。

function getDateByJqueryAjax() {
  $.ajax({
    type: "POST",

    url: "097_jQueryAjaxAndWebService.asmx/GetToday",

    contentType: "application/json; charset=utf-8",

    success: function (data) {
      if (data.hasOwnProperty('d')) {
          data = data.d;
      }
      $('#output').empty();
      var re = /^\/Date\(([0-9]+)\)\/$/;
      var results = re.exec(data.Today);
      var now = new Date(Number(results[1]));
      $('#output').append(
        "<p>" + now.getFullYear() + "年" +
        (now.getMonth() + 1) + "月" + now.getDate() + "日" +
        now.getHours() + "時" + now.getMinutes() + "分" +
        now.getSeconds() + "秒</p>"
      );
    },

    error: function (jqXHR, textStatus, errorThrown) {
      $('#output').text('textStatus: ' + textStatus +
                  ', errorThrown: ' + errorThrown);
    }
  });
}

(6) ASP.NET Web API (Newtonsoft.Json と同じ)

Visual Studio 2010 Professional のテンプレートを使って自動生成される ASP.NET Web API アプリのコードに、以下のメソッドを追加して試してみました。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using WebApi1.Models;

namespace WebApi1.Controllers
{
    public class HeroesController : ApiController
    {
        public DateTimeJsonTest Get()
        {
            DateTimeJsonTest today = new DateTimeJsonTest();
            today.Today = DateTime.Now;
            return today;
        }

        // ・・・中略・・・
    }
}

jQuery ajax を利用して上の Get() メソッドを呼び出すと以下のような JSON 文字列が返されます:

{"Today":"2017-08-26T15:39:32.6330349+09:00"}

DateTime 型の文字列への変換は上の「(5) ASP.NET Web サービス」の場合の XML 形式の応答の場合と同じです。

上にも書きましたが、ASP.NET Web API はシリアライザに Newtonsoft.Json を使っています。この形式は Newtonsoft.Json 独自なのか、ISO 8601 などに準拠しているのかは調べ切れてません。

なお、この形式で、JavaScript の Date 型へのパースは IE11, Chrome 60.0.3112.113, Firefox 55.0.3 のいずれも可能です。例えば、jQuery ajax を使うと、success オプションに設定したコールバックの引数に上の JSON 文字列を JavaScript オブジェクトに変換して代入してくれますので、それから以下のようにして Date オブジェクトにパースできます。

success: function (data, textStatus, jqXHR) {
    var now = new Date(data.Today);
    $('#heroes').append(
        "<p>" + now.getFullYear() + "年" +
        (now.getMonth() + 1) + "月" + now.getDate() + "日" +
        now.getHours() + "時" + now.getMinutes() + "分" +
        now.getSeconds() + "秒</p>"
    );
},

Tags: , ,

JavaScript

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar