DataContractJsonSerializerでnew DateTime()をパースできない

関連
stackoverflow:http://stackoverflow.com/questions/4025851/why-can-datetime-minvalue-not-be-serialized-in-timezones-ahead-of-utc

次のようなコードはもちろんJSONにシリアル化もできるし、逆シリアル化もできます。

//using System.Runtime.Serialization;
//using System.Runtime.Serialization.Json;

class Program
{
static void Main()
{
Hoeee h = new Hoeee();
h.Date = DateTime.Today;

//JSONへパース
string json;
using(var ms = new MemoryStream())
{
var ser = new DataContractJsonSerializer(typeof(Hoeee));
ser.WriteObject(ms, h);
ms.Position = 0;
using(var sr = new StreamReader(ms))
{
json = sr.ReadToEnd();
}
Console.WriteLine("オブジェクト→JSONのパース");
Console.WriteLine(json);
//オブジェクト→JSONのパース
//{"Date":"\/Date(1444230000000+0900)\/"}
}
//逆シリアル化
Hoeee h2;
var serializer = new DataContractJsonSerializer(typeof(Hoeee));
using(var ms = new MemoryStream(Encoding.UTF8.GetBytes(json)))
{
h2 = (Hoeee)serializer.ReadObject(ms);
}
Console.WriteLine("JSON→オブジェクトの逆シリアル化");
Console.WriteLine(h2.Date);
//JSON→オブジェクトの逆シリアル化
//2015/10/08 0:00:00
//続行するには何かキーを押してください . . . }
}
}

[DataContract]
public class Hoeee
{
[DataMember]
public DateTime Date { get; set; }
}



ところが、「h.Date = DateTime.Today;」をコメントアウト、実質的にはh.Dateをnew DateTime()にするとエラーを吐いてしまいます。

ハンドルされていない例外: System.Runtime.Serialization.SerializationException: U
TC への変換時、DateTime.MaxValue より大きい DateTime 値、または DateTime.MinValu
e より小さい DateTime 値は、JSON にシリアル化することはできません。 ---> System.
ArgumentOutOfRangeException: 指定された引数は、有効な値の範囲内にありません。
パラメーター名:value
--- 内部例外スタック トレースの終わり ---
場所 System.Runtime.Serialization.Json.JsonWriterDelegator.WriteDateTimeInDef
aultFormat(DateTime value)
場所 System.Runtime.Serialization.Json.JsonWriterDelegator.WriteDateTime(Date
Time value)
場所 WriteHoeeeToJson(XmlWriterDelegator , Object , XmlObjectSerializerWriteC
ontextComplexJson , ClassDataContract , XmlDictionaryString[] )
場所 System.Runtime.Serialization.Json.JsonClassDataContract.WriteJsonValueCo
re(XmlWriterDelegator jsonWriter, Object obj, XmlObjectSerializerWriteContextCom
plexJson context, RuntimeTypeHandle declaredTypeHandle)
場所 System.Runtime.Serialization.Json.XmlObjectSerializerWriteContextComplex
Json.WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWri
ter, Object obj, RuntimeTypeHandle declaredTypeHandle)
場所 System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWi
thoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj
, RuntimeTypeHandle declaredTypeHandle)
場所 System.Runtime.Serialization.Json.DataContractJsonSerializer.InternalWri
teObjectContent(XmlWriterDelegator writer, Object graph)
場所 System.Runtime.Serialization.Json.DataContractJsonSerializer.InternalWri
teObject(XmlWriterDelegator writer, Object graph)
場所 System.Runtime.Serialization.XmlObjectSerializer.InternalWriteObject(Xml
WriterDelegator writer, Object graph, DataContractResolver dataContractResolver)

場所 System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExcept
ions(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractR
esolver)
場所 System.Runtime.Serialization.Json.DataContractJsonSerializer.WriteObject
(XmlDictionaryWriter writer, Object graph)
場所 System.Runtime.Serialization.Json.DataContractJsonSerializer.WriteObject
(Stream stream, Object graph)
場所 ConsoleApplication4.Program.Main() 場所 d:\…\ConsoleApplication4\Program.cs:行 28


詳しい解説は最初に張ったstackoverflowに載っていますが、要約すると、
・new DateTime()はDateTime.MinValueと等しい(DateTimeはstructであり、==で比較するとTrueを返す)
・DataContractJsonSerializerでシリアル化するにおいて、DateTimeのKindがDateTimeKind.Utcでなければ、現在のタイムゾーンからUTCに変換する計算をする
・日本の時差はUTC+9:00なので、new DateTime() == DateTime.MinValueから-9hをしてUTC時間に変換すると、DateTime.MinValueをマイナス方向のオーバーフローしてしまう。このときハンドルされない例外がスローされてしまう。


というものです。この解決策はいろいろ考えられますが、とりあえずDateTimeをDateTimeOffsetに変えて定義すると発生しなくなるそうです。(CoreTweetもDateTimeOffsetで定義していたしなるほどなと)。
        static void Main()
{
Hoeee h = new Hoeee();

//JSONへパース
string json;
using (var ms = new MemoryStream())
{
var ser = new DataContractJsonSerializer(typeof(Hoeee));
ser.WriteObject(ms, h);
ms.Position = 0;
using (var sr = new StreamReader(ms))
{
json = sr.ReadToEnd();
}
Console.WriteLine("オブジェクト→JSONのパース");
Console.WriteLine(json);
//オブジェクト→JSONのパース
//{"Date":{"DateTime":"\/Date(-62135596800000)\/","OffsetMinutes":0}}

}
//逆シリアル化
Hoeee h2;
var serializer = new DataContractJsonSerializer(typeof(Hoeee));
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(json)))
{
h2 = (Hoeee)serializer.ReadObject(ms);
}
Console.WriteLine("JSON→オブジェクトの逆シリアル化");
Console.WriteLine(h2.Date);
//JSON→オブジェクトの逆シリアル化
//0001/01/01 0:00:00 +00:00
//続行するには何かキーを押してください . . .
}
}

[DataContract]
public class Hoeee
{
[DataMember]
public DateTimeOffset Date { get; set; }
}


C#の入門書や入門サイトを見ると、日付けはとりあえずDateTimeと書いてあるのでここは要注意ですね。
スポンサーサイト
プロフィール

こしあん

Author:こしあん
(:3[____]
【TwitterID : koshian2】
【ほしい物リスト】http://goo.gl/bDtvG2

Twitter
カウンター
天気予報

-天気予報コム- -FC2-
カテゴリ
月別アーカイブ
最新記事
最新トラックバック
検索フォーム
リンク