-Added nullable DateTime support to IsoDateTimeConverter and JavaScriptDateTimeConverter

-Added WriteValue overloads to JsonWriter for nullable types
-Fixed JsonTextWriter.WriteValue(string) to write null instead of an empty string for a null value
-Fixed JsonWriter.WriteValue(object) to no longer error on null values
This commit is contained in:
JamesNK
2008-09-24 09:39:30 +00:00
parent 33c2d69b54
commit 500440e04f
13 changed files with 431 additions and 10 deletions
@@ -174,6 +174,26 @@ namespace Newtonsoft.Json.Tests.Converters
Assert.AreEqual(@"{""PreField"":""Pre"",""DateTimeField"":""2008-01-01T01:01:01.0000000Z"",""DateTimeOffsetField"":""2008-01-01T01:01:01.0000000+00:00"",""PostField"":""Post""}", json);
}
[Test]
public void NullableSerializeUTC()
{
NullableDateTimeTestClass c = new NullableDateTimeTestClass();
c.DateTimeField = new DateTime(2008, 12, 12, 12, 12, 12, 0, DateTimeKind.Utc).ToLocalTime();
c.DateTimeOffsetField = new DateTime(2008, 12, 12, 12, 12, 12, 0, DateTimeKind.Utc).ToLocalTime();
c.PreField = "Pre";
c.PostField = "Post";
string json = JavaScriptConvert.SerializeObject(c, new IsoDateTimeConverter() { DateTimeStyles = DateTimeStyles.AssumeUniversal });
Assert.AreEqual(@"{""PreField"":""Pre"",""DateTimeField"":""2008-12-12T12:12:12.0000000Z"",""DateTimeOffsetField"":""2008-12-12T12:12:12.0000000+00:00"",""PostField"":""Post""}", json);
//test the other edge case too
c.DateTimeField = null;
c.DateTimeOffsetField = null;
c.PreField = "Pre";
c.PostField = "Post";
json = JavaScriptConvert.SerializeObject(c, new IsoDateTimeConverter() { DateTimeStyles = DateTimeStyles.AssumeUniversal });
Assert.AreEqual(@"{""PreField"":""Pre"",""DateTimeField"":null,""DateTimeOffsetField"":null,""PostField"":""Post""}", json);
}
[Test]
public void DeserializeUTC()
{
@@ -194,6 +214,34 @@ namespace Newtonsoft.Json.Tests.Converters
Assert.AreEqual("Post", c2.PostField);
}
[Test]
public void NullableDeserializeUTC()
{
NullableDateTimeTestClass c =
JavaScriptConvert.DeserializeObject<NullableDateTimeTestClass>(@"{""PreField"":""Pre"",""DateTimeField"":""2008-12-12T12:12:12Z"",""DateTimeOffsetField"":""2008-12-12T12:12:12Z"",""PostField"":""Post""}", new IsoDateTimeConverter() { DateTimeStyles = DateTimeStyles.AssumeUniversal });
Assert.AreEqual(new DateTime(2008, 12, 12, 12, 12, 12, 0, DateTimeKind.Utc).ToLocalTime(), c.DateTimeField);
Assert.AreEqual(new DateTimeOffset(2008, 12, 12, 12, 12, 12, 0, TimeSpan.Zero), c.DateTimeOffsetField);
Assert.AreEqual("Pre", c.PreField);
Assert.AreEqual("Post", c.PostField);
NullableDateTimeTestClass c2 =
JavaScriptConvert.DeserializeObject<NullableDateTimeTestClass>(@"{""PreField"":""Pre"",""DateTimeField"":null,""DateTimeOffsetField"":null,""PostField"":""Post""}", new IsoDateTimeConverter() { DateTimeStyles = DateTimeStyles.AssumeUniversal });
Assert.AreEqual(null, c2.DateTimeField);
Assert.AreEqual(null, c2.DateTimeOffsetField);
Assert.AreEqual("Pre", c2.PreField);
Assert.AreEqual("Post", c2.PostField);
}
[Test]
[ExpectedException(typeof(Exception), ExpectedMessage = "Cannot convert null value to System.DateTime.")]
public void DeserializeNullToNonNullable()
{
DateTimeTestClass c2 =
JavaScriptConvert.DeserializeObject<DateTimeTestClass>(@"{""PreField"":""Pre"",""DateTimeField"":null,""DateTimeOffsetField"":null,""PostField"":""Post""}", new IsoDateTimeConverter() { DateTimeStyles = DateTimeStyles.AssumeUniversal });
}
[Test]
public void SerializeShouldChangeNonUTCDates()
{
@@ -58,6 +58,32 @@ namespace Newtonsoft.Json.Tests.Converters
Assert.AreEqual("new Date(976918263055)", result);
}
[Test]
public void SerializeNullableDateTimeClass()
{
NullableDateTimeTestClass t = new NullableDateTimeTestClass()
{
DateTimeField = null,
DateTimeOffsetField = null
};
JavaScriptDateTimeConverter converter = new JavaScriptDateTimeConverter();
string result;
result = JavaScriptConvert.SerializeObject(t, converter);
Assert.AreEqual(@"{""PreField"":null,""DateTimeField"":null,""DateTimeOffsetField"":null,""PostField"":null}", result);
t = new NullableDateTimeTestClass()
{
DateTimeField = new DateTime(2000, 12, 15, 22, 11, 3, 55, DateTimeKind.Utc),
DateTimeOffsetField = new DateTimeOffset(2000, 12, 15, 22, 11, 3, 55, TimeSpan.Zero)
};
result = JavaScriptConvert.SerializeObject(t, converter);
Assert.AreEqual(@"{""PreField"":null,""DateTimeField"":new Date(976918263055),""DateTimeOffsetField"":new Date(976918263055),""PostField"":null}", result);
}
[Test]
public void DeserializeDateTime()
{
@@ -67,6 +93,14 @@ namespace Newtonsoft.Json.Tests.Converters
Assert.AreEqual(new DateTime(2000, 12, 15, 22, 11, 3, 55, DateTimeKind.Utc), result);
}
[Test]
[ExpectedException(typeof(Exception), ExpectedMessage = "Cannot convert null value to System.DateTime.")]
public void DeserializeNullToNonNullable()
{
DateTimeTestClass c2 =
JavaScriptConvert.DeserializeObject<DateTimeTestClass>(@"{""PreField"":""Pre"",""DateTimeField"":null,""DateTimeOffsetField"":null,""PostField"":""Post""}", new JavaScriptDateTimeConverter());
}
[Test]
public void DeserializeDateTimeOffset()
{
@@ -1325,11 +1325,15 @@ keyword such as type of business.""
}
[Test]
public void DeserializeStringDate()
public void DeserializeNullable()
{
string dateText = @"""\/Date(1221644506800-0700)\/""";
string json;
json = JavaScriptConvert.SerializeObject((int?)null);
Assert.AreEqual("null", json);
json = JavaScriptConvert.SerializeObject((int?)1);
Assert.AreEqual("1", json);
}
}
}
@@ -51,14 +51,15 @@ namespace Newtonsoft.Json.Tests
jsonWriter.WriteValue(0.99);
jsonWriter.WriteValue(0.000000000000000001d);
jsonWriter.WriteValue(0.000000000000000001m);
jsonWriter.WriteValue(null);
jsonWriter.WriteValue((string)null);
jsonWriter.WriteValue((object)null);
jsonWriter.WriteValue("This is a string.");
jsonWriter.WriteNull();
jsonWriter.WriteUndefined();
jsonWriter.WriteEndArray();
}
string expected = @"[""@"",""\r\n\t\f\b?{\\r\\n\""'"",true,10,10.99,0.99,1E-18,0.000000000000000001,"""",""This is a string."",null,undefined]";
string expected = @"[""@"",""\r\n\t\f\b?{\\r\\n\""'"",true,10,10.99,0.99,1E-18,0.000000000000000001,null,null,""This is a string."",null,undefined]";
string result = sb.ToString();
Console.WriteLine("ValueFormatting");
@@ -67,6 +68,84 @@ namespace Newtonsoft.Json.Tests
Assert.AreEqual(expected, result);
}
[Test]
public void NullableValueFormatting()
{
StringWriter sw = new StringWriter();
using (JsonTextWriter jsonWriter = new JsonTextWriter(sw))
{
jsonWriter.WriteStartArray();
jsonWriter.WriteValue((char?)null);
jsonWriter.WriteValue((char?)'c');
jsonWriter.WriteValue((bool?)null);
jsonWriter.WriteValue((bool?)true);
jsonWriter.WriteValue((byte?)null);
jsonWriter.WriteValue((byte?)1);
jsonWriter.WriteValue((sbyte?)null);
jsonWriter.WriteValue((sbyte?)1);
jsonWriter.WriteValue((short?)null);
jsonWriter.WriteValue((short?)1);
jsonWriter.WriteValue((ushort?)null);
jsonWriter.WriteValue((ushort?)1);
jsonWriter.WriteValue((int?)null);
jsonWriter.WriteValue((int?)1);
jsonWriter.WriteValue((uint?)null);
jsonWriter.WriteValue((uint?)1);
jsonWriter.WriteValue((long?)null);
jsonWriter.WriteValue((long?)1);
jsonWriter.WriteValue((ulong?)null);
jsonWriter.WriteValue((ulong?)1);
jsonWriter.WriteValue((double?)null);
jsonWriter.WriteValue((double?)1.1);
jsonWriter.WriteValue((float?)null);
jsonWriter.WriteValue((float?)1.1);
jsonWriter.WriteValue((decimal?)null);
jsonWriter.WriteValue((decimal?)1.1m);
jsonWriter.WriteValue((DateTime?)null);
jsonWriter.WriteValue((DateTime?)new DateTime(JavaScriptConvert.InitialJavaScriptDateTicks, DateTimeKind.Utc));
jsonWriter.WriteValue((DateTimeOffset?)null);
jsonWriter.WriteValue((DateTimeOffset?)new DateTimeOffset(JavaScriptConvert.InitialJavaScriptDateTicks, TimeSpan.Zero));
jsonWriter.WriteEndArray();
}
string json = sw.ToString();
string expected = @"[null,""c"",null,true,null,1,null,1,null,1,null,1,null,1,null,1,null,1,null,1,null,1.1,null,1.1,null,1.1,null,""\/Date(0)\/"",null,""\/Date(0+0000)\/""]";
Assert.AreEqual(expected, json);
}
[Test]
public void WriteValueObjectWithNullable()
{
StringWriter sw = new StringWriter();
using (JsonTextWriter jsonWriter = new JsonTextWriter(sw))
{
char? value = 'c';
jsonWriter.WriteStartArray();
jsonWriter.WriteValue((object)value);
jsonWriter.WriteEndArray();
}
string json = sw.ToString();
string expected = @"[""c""]";
Assert.AreEqual(expected, json);
}
[Test]
[ExpectedException(typeof(ArgumentException), ExpectedMessage = @"Unsupported type: System.Version. Use the JsonSerializer class to get the object's JSON representation.")]
public void WriteValueObjectWithUnsupportedValue()
{
StringWriter sw = new StringWriter();
using (JsonTextWriter jsonWriter = new JsonTextWriter(sw))
{
jsonWriter.WriteStartArray();
jsonWriter.WriteValue(new Version(1, 1, 1, 1));
jsonWriter.WriteEndArray();
}
}
[Test]
public void StringEscaping()
{
@@ -51,7 +51,7 @@ namespace Newtonsoft.Json.Tests
jsonWriter.WriteValue(0.99);
jsonWriter.WriteValue(0.000000000000000001d);
jsonWriter.WriteValue(0.000000000000000001m);
jsonWriter.WriteValue(null);
jsonWriter.WriteValue((string)null);
jsonWriter.WriteValue("This is a string.");
jsonWriter.WriteNull();
jsonWriter.WriteUndefined();
@@ -74,6 +74,7 @@
<Compile Include="Linq\JTokenTests.cs" />
<Compile Include="Linq\JValueTests.cs" />
<Compile Include="Linq\LinqToJsonTest.cs" />
<Compile Include="NullableDateTimeTestClass.cs" />
<Compile Include="PerformanceTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SilverlightTests.cs" />
@@ -130,6 +130,7 @@
<Compile Include="Linq\JTokenTests.cs" />
<Compile Include="Linq\JValueTests.cs" />
<Compile Include="Linq\LinqToJsonTest.cs" />
<Compile Include="NullableDateTimeTestClass.cs" />
<Compile Include="PerformanceTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SilverlightTests.cs" />
@@ -73,6 +73,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="NullableDateTimeTestClass.cs" />
<Compile Include="DateTimeTestClass.cs" />
<Compile Include="Converters\IsoDateTimeConverterTests.cs" />
<Compile Include="JavaScriptConvertTest.cs" />
@@ -0,0 +1,40 @@
#region License
// Copyright (c) 2007 James Newton-King
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Newtonsoft.Json.Tests
{
public class NullableDateTimeTestClass
{
public string PreField { get; set; }
public DateTime? DateTimeField { get; set; }
public DateTimeOffset? DateTimeOffsetField { get; set; }
public string PostField { get; set; }
}
}
@@ -92,12 +92,24 @@ namespace Newtonsoft.Json.Converters
/// <returns>The object value.</returns>
public override object ReadJson(JsonReader reader, Type objectType)
{
Type t = (ReflectionUtils.IsNullableType(objectType))
? Nullable.GetUnderlyingType(objectType)
: objectType;
if (reader.TokenType == JsonToken.Null)
{
if (!ReflectionUtils.IsNullableType(objectType))
throw new Exception("Cannot convert null value to {0}.".FormatWith(CultureInfo.InvariantCulture, objectType));
return null;
}
if (reader.TokenType != JsonToken.String)
throw new Exception("Unexpected token parsing date. Expected String, got {0}.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType));
string dateText = reader.Value.ToString();
if (objectType == typeof(DateTimeOffset))
if (t == typeof(DateTimeOffset))
{
if (!string.IsNullOrEmpty(_dateTimeFormat))
return DateTimeOffset.ParseExact(dateText, _dateTimeFormat, Culture, _dateTimeStyles);
@@ -120,9 +132,13 @@ namespace Newtonsoft.Json.Converters
/// </returns>
public override bool CanConvert(Type objectType)
{
if (typeof(DateTime).IsAssignableFrom(objectType))
Type t = (ReflectionUtils.IsNullableType(objectType))
? Nullable.GetUnderlyingType(objectType)
: objectType;
if (typeof(DateTime).IsAssignableFrom(t))
return true;
if (typeof(DateTimeOffset).IsAssignableFrom(objectType))
if (typeof(DateTimeOffset).IsAssignableFrom(t))
return true;
return false;
@@ -47,6 +47,18 @@ namespace Newtonsoft.Json.Converters
/// <returns>The object value.</returns>
public override object ReadJson(JsonReader reader, Type objectType)
{
Type t = (ReflectionUtils.IsNullableType(objectType))
? Nullable.GetUnderlyingType(objectType)
: objectType;
if (reader.TokenType == JsonToken.Null)
{
if (!ReflectionUtils.IsNullableType(objectType))
throw new Exception("Cannot convert null value to {0}.".FormatWith(CultureInfo.InvariantCulture, objectType));
return null;
}
if (reader.TokenType != JsonToken.StartConstructor || string.Compare(reader.Value.ToString(), "Date", StringComparison.Ordinal) != 0)
throw new Exception("Unexpected token or value when parsing date. Token: {0}, Value: {1}".FormatWith(CultureInfo.InvariantCulture, reader.TokenType, reader.Value));
@@ -64,7 +76,7 @@ namespace Newtonsoft.Json.Converters
if (reader.TokenType != JsonToken.EndConstructor)
throw new Exception("Unexpected token parsing date. Expected EndConstructor, got {0}.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType));
if (objectType == typeof(DateTimeOffset))
if (t == typeof(DateTimeOffset))
return new DateTimeOffset(d);
return d;
@@ -79,6 +91,10 @@ namespace Newtonsoft.Json.Converters
/// </returns>
public override bool CanConvert(Type objectType)
{
Type t = (ReflectionUtils.IsNullableType(objectType))
? Nullable.GetUnderlyingType(objectType)
: objectType;
if (typeof(DateTime).IsAssignableFrom(objectType))
return true;
if (typeof(DateTimeOffset).IsAssignableFrom(objectType))
+1 -1
View File
@@ -276,7 +276,7 @@ namespace Newtonsoft.Json
public override void WriteValue(string value)
{
base.WriteValue(value);
WriteValueInternal(JavaScriptConvert.ToString(value, _quoteChar), JsonToken.String);
WriteValueInternal((value != null) ? JavaScriptConvert.ToString(value, _quoteChar) : JavaScriptConvert.Null, JsonToken.String);
}
/// <summary>
+181
View File
@@ -737,6 +737,186 @@ namespace Newtonsoft.Json
AutoComplete(JsonToken.Date);
}
/// <summary>
/// Writes a <see cref="Nullable{Int32}"/> value.
/// </summary>
/// <param name="value">The <see cref="Nullable{Int32}"/> value to write.</param>
public virtual void WriteValue(int? value)
{
if (value == null)
WriteNull();
else
WriteValue(value.Value);
}
/// <summary>
/// Writes a <see cref="Nullable{UInt32}"/> value.
/// </summary>
/// <param name="value">The <see cref="Nullable{UInt32}"/> value to write.</param>
public virtual void WriteValue(uint? value)
{
if (value == null)
WriteNull();
else
WriteValue(value.Value);
}
/// <summary>
/// Writes a <see cref="Nullable{Int64}"/> value.
/// </summary>
/// <param name="value">The <see cref="Nullable{Int64}"/> value to write.</param>
public virtual void WriteValue(long? value)
{
if (value == null)
WriteNull();
else
WriteValue(value.Value);
}
/// <summary>
/// Writes a <see cref="Nullable{UInt64}"/> value.
/// </summary>
/// <param name="value">The <see cref="Nullable{UInt64}"/> value to write.</param>
public virtual void WriteValue(ulong? value)
{
if (value == null)
WriteNull();
else
WriteValue(value.Value);
}
/// <summary>
/// Writes a <see cref="Nullable{Single}"/> value.
/// </summary>
/// <param name="value">The <see cref="Nullable{Single}"/> value to write.</param>
public virtual void WriteValue(float? value)
{
if (value == null)
WriteNull();
else
WriteValue(value.Value);
}
/// <summary>
/// Writes a <see cref="Nullable{Double}"/> value.
/// </summary>
/// <param name="value">The <see cref="Nullable{Double}"/> value to write.</param>
public virtual void WriteValue(double? value)
{
if (value == null)
WriteNull();
else
WriteValue(value.Value);
}
/// <summary>
/// Writes a <see cref="Nullable{Boolean}"/> value.
/// </summary>
/// <param name="value">The <see cref="Nullable{Boolean}"/> value to write.</param>
public virtual void WriteValue(bool? value)
{
if (value == null)
WriteNull();
else
WriteValue(value.Value);
}
/// <summary>
/// Writes a <see cref="Nullable{Int16}"/> value.
/// </summary>
/// <param name="value">The <see cref="Nullable{Int16}"/> value to write.</param>
public virtual void WriteValue(short? value)
{
if (value == null)
WriteNull();
else
WriteValue(value.Value);
}
/// <summary>
/// Writes a <see cref="Nullable{UInt16}"/> value.
/// </summary>
/// <param name="value">The <see cref="Nullable{UInt16}"/> value to write.</param>
public virtual void WriteValue(ushort? value)
{
if (value == null)
WriteNull();
else
WriteValue(value.Value);
}
/// <summary>
/// Writes a <see cref="Nullable{Char}"/> value.
/// </summary>
/// <param name="value">The <see cref="Nullable{Char}"/> value to write.</param>
public virtual void WriteValue(char? value)
{
if (value == null)
WriteNull();
else
WriteValue(value.Value);
}
/// <summary>
/// Writes a <see cref="Nullable{Byte}"/> value.
/// </summary>
/// <param name="value">The <see cref="Nullable{Byte}"/> value to write.</param>
public virtual void WriteValue(byte? value)
{
if (value == null)
WriteNull();
else
WriteValue(value.Value);
}
/// <summary>
/// Writes a <see cref="Nullable{SByte}"/> value.
/// </summary>
/// <param name="value">The <see cref="Nullable{SByte}"/> value to write.</param>
public virtual void WriteValue(sbyte? value)
{
if (value == null)
WriteNull();
else
WriteValue(value.Value);
}
/// <summary>
/// Writes a <see cref="Nullable{Decimal}"/> value.
/// </summary>
/// <param name="value">The <see cref="Nullable{Decimal}"/> value to write.</param>
public virtual void WriteValue(decimal? value)
{
if (value == null)
WriteNull();
else
WriteValue(value.Value);
}
/// <summary>
/// Writes a <see cref="Nullable{DateTime}"/> value.
/// </summary>
/// <param name="value">The <see cref="Nullable{DateTime}"/> value to write.</param>
public virtual void WriteValue(DateTime? value)
{
if (value == null)
WriteNull();
else
WriteValue(value.Value);
}
/// <summary>
/// Writes a <see cref="Nullable{DateTimeOffset}"/> value.
/// </summary>
/// <param name="value">The <see cref="Nullable{DateTimeOffset}"/> value to write.</param>
public virtual void WriteValue(DateTimeOffset? value)
{
if (value == null)
WriteNull();
else
WriteValue(value.Value);
}
/// <summary>
/// Writes a <see cref="Object"/> value.
/// An error will raised if the value cannot be written as a single JSON token.
@@ -747,6 +927,7 @@ namespace Newtonsoft.Json
if (value == null)
{
WriteNull();
return;
}
else if (value is IConvertible)
{