Add support for multiple ctors in JavaScriptDateTimeConverter

This commit is contained in:
James Newton-King
2018-09-23 13:57:43 -07:00
parent 70dabfb409
commit 700444a996
5 changed files with 165 additions and 40 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
cls
powershell -Command "& { [Console]::WindowWidth = 150; [Console]::WindowHeight = 50; Start-Transcript '%~dp0\Temp\runbuild.txt'; Import-Module '%~dp0..\Tools\PSake\psake.psm1'; Invoke-psake '%~dp0..\Build\build.ps1' %*; Stop-Transcript; exit !($psake.build_success); }"
powershell -Command "& { Start-Transcript '%~dp0\Temp\runbuild.txt'; Import-Module '%~dp0..\Tools\PSake\psake.psm1'; Invoke-psake '%~dp0..\Build\build.ps1' %*; Stop-Transcript; exit !($psake.build_success); }"
ECHO %ERRORLEVEL%
EXIT /B %ERRORLEVEL%
@@ -124,6 +124,79 @@ namespace Newtonsoft.Json.Tests.Converters
Assert.AreEqual(new DateTime(2000, 12, 15, 22, 11, 3, 55, DateTimeKind.Utc), result);
}
[Test]
public void DeserializeDateTime_MultipleArguments()
{
JavaScriptDateTimeConverter converter = new JavaScriptDateTimeConverter();
DateTime result;
result = JsonConvert.DeserializeObject<DateTime>("new Date(2000, 11)", converter);
Assert.AreEqual(new DateTime(2000, 12, 1, 0, 0, 0, 0, DateTimeKind.Utc), result);
result = JsonConvert.DeserializeObject<DateTime>("new Date(2000, 11, 12)", converter);
Assert.AreEqual(new DateTime(2000, 12, 12, 0, 0, 0, 0, DateTimeKind.Utc), result);
result = JsonConvert.DeserializeObject<DateTime>("new Date(2000, 11, 12, 20)", converter);
Assert.AreEqual(new DateTime(2000, 12, 12, 20, 0, 0, 0, DateTimeKind.Utc), result);
result = JsonConvert.DeserializeObject<DateTime>("new Date(2000, 11, 12, 20, 1)", converter);
Assert.AreEqual(new DateTime(2000, 12, 12, 20, 1, 0, 0, DateTimeKind.Utc), result);
result = JsonConvert.DeserializeObject<DateTime>("new Date(2000, 11, 12, 20, 1, 2)", converter);
Assert.AreEqual(new DateTime(2000, 12, 12, 20, 1, 2, 0, DateTimeKind.Utc), result);
result = JsonConvert.DeserializeObject<DateTime>("new Date(2000, 11, 12, 20, 1, 2, 3)", converter);
Assert.AreEqual(new DateTime(2000, 12, 12, 20, 1, 2, 3, DateTimeKind.Utc), result);
result = JsonConvert.DeserializeObject<DateTime>("new Date(2000, 11, 1, 0, 0, 0, 0)", converter);
Assert.AreEqual(new DateTime(2000, 12, 1, 0, 0, 0, 0, DateTimeKind.Utc), result);
}
[Test]
public void DeserializeDateTime_TooManyArguments()
{
JavaScriptDateTimeConverter converter = new JavaScriptDateTimeConverter();
ExceptionAssert.Throws<JsonSerializationException>(() =>
{
JsonConvert.DeserializeObject<DateTime>("new Date(1, 2, 3, 4, 5, 6, 7, 8)", converter);
}, "Unexpected number of arguments when reading date constructor. Path '', line 1, position 32.");
}
[Test]
public void DeserializeDateTime_NoArguments()
{
JavaScriptDateTimeConverter converter = new JavaScriptDateTimeConverter();
ExceptionAssert.Throws<JsonSerializationException>(() =>
{
JsonConvert.DeserializeObject<DateTime>("new Date()", converter);
}, "Date constructor has no arguments. Path '', line 1, position 10.");
}
[Test]
public void DeserializeDateTime_NotArgumentsNotClosed()
{
JavaScriptDateTimeConverter converter = new JavaScriptDateTimeConverter();
ExceptionAssert.Throws<JsonSerializationException>(() =>
{
JsonConvert.DeserializeObject<DateTime>("new Date(", converter);
}, "Unexpected end when reading date constructor. Path '', line 1, position 9.");
}
[Test]
public void DeserializeDateTime_NotClosed()
{
JavaScriptDateTimeConverter converter = new JavaScriptDateTimeConverter();
ExceptionAssert.Throws<JsonSerializationException>(() =>
{
JsonConvert.DeserializeObject<DateTime>("new Date(2, 3", converter);
}, "Unexpected end when reading date constructor. Path '[1]', line 1, position 13.");
}
[Test]
public void ConverterList()
{
@@ -91,22 +91,9 @@ namespace Newtonsoft.Json.Converters
throw JsonSerializationException.Create(reader, "Unexpected token or value when parsing date. Token: {0}, Value: {1}".FormatWith(CultureInfo.InvariantCulture, reader.TokenType, reader.Value));
}
reader.Read();
if (reader.TokenType != JsonToken.Integer)
if (!JavaScriptUtils.TryGetDateFromConstructorJson(reader, out DateTime d, out string errorMessage))
{
throw JsonSerializationException.Create(reader, "Unexpected token parsing date. Expected Integer, got {0}.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType));
}
long ticks = (long)reader.Value;
DateTime d = DateTimeUtils.ConvertJavaScriptTicksToDateTime(ticks);
reader.Read();
if (reader.TokenType != JsonToken.EndConstructor)
{
throw JsonSerializationException.Create(reader, "Unexpected token parsing date. Expected EndConstructor, got {0}.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType));
throw JsonSerializationException.Create(reader, errorMessage);
}
#if HAVE_DATE_TIME_OFFSET
+4 -24
View File
@@ -696,32 +696,12 @@ namespace Newtonsoft.Json
private void WriteConstructorDate(JsonReader reader)
{
List<long> DateParams = new List<long>();
while (true)
if (!JavaScriptUtils.TryGetDateFromConstructorJson(reader, out DateTime dateTime, out string errorMessage))
{
if (!reader.Read())
throw JsonWriterException.Create(this, "Unexpected end when reading date constructor.", null);
if (reader.TokenType == JsonToken.EndConstructor)
break;
if (reader.TokenType != JsonToken.Integer)
throw JsonWriterException.Create(this, "Unexpected token when reading date constructor. Expected Integer, got " + reader.TokenType, null);
DateParams.Add((long)reader.Value);
if (DateParams.Count > 7)
throw JsonWriterException.Create(this, "Unexpected token when reading date constructor. Wrong number of arguments", null);
throw JsonWriterException.Create(this, errorMessage, null);
}
DateTime date;
if (DateParams.Count == 1)
{
date = DateTimeUtils.ConvertJavaScriptTicksToDateTime(DateParams[0]);
}
else if (DateParams.Count == 7) //Add support: new Date(2018,5,26,11,14,31,36)
{
date = new DateTime((int)DateParams[0], (int)DateParams[1] + 1, (int)DateParams[2], (int)DateParams[3],
(int)DateParams[4], (int)DateParams[5], (int)DateParams[6]);
}
else
throw JsonWriterException.Create(this, "Unexpected token when reading date constructor. Wrong number of arguments", null);
WriteValue(date);
WriteValue(dateTime);
}
private void WriteEnd(JsonContainerType type)
@@ -564,5 +564,90 @@ namespace Newtonsoft.Json.Utilities
}
}
#endif
public static bool TryGetDateFromConstructorJson(JsonReader reader, out DateTime dateTime, out string errorMessage)
{
dateTime = default(DateTime);
errorMessage = null;
if (!TryGetDateConstructorValue(reader, out long? t1, out errorMessage) || t1 == null)
{
errorMessage = errorMessage ?? "Date constructor has no arguments.";
return false;
}
if (!TryGetDateConstructorValue(reader, out long? t2, out errorMessage))
{
return false;
}
else if (t2 != null)
{
// Only create a list when there is more than one argument
List<long> dateArgs = new List<long>
{
t1.Value,
t2.Value
};
while (true)
{
if (!TryGetDateConstructorValue(reader, out long? integer, out errorMessage))
{
return false;
}
else if (integer != null)
{
dateArgs.Add(integer.Value);
}
else
{
break;
}
}
if (dateArgs.Count > 7)
{
errorMessage = "Unexpected number of arguments when reading date constructor.";
return false;
}
// Pad args out to the number used by the ctor
while (dateArgs.Count < 7)
{
dateArgs.Add(0);
}
dateTime = new DateTime((int)dateArgs[0], (int)dateArgs[1] + 1, dateArgs[2] == 0 ? 1 : (int)dateArgs[2],
(int)dateArgs[3], (int)dateArgs[4], (int)dateArgs[5], (int)dateArgs[6]);
}
else
{
dateTime = DateTimeUtils.ConvertJavaScriptTicksToDateTime(t1.Value);
}
return true;
}
private static bool TryGetDateConstructorValue(JsonReader reader, out long? integer, out string errorMessage)
{
integer = null;
errorMessage = null;
if (!reader.Read())
{
errorMessage = "Unexpected end when reading date constructor.";
return false;
}
if (reader.TokenType == JsonToken.EndConstructor)
{
return true;
}
if (reader.TokenType != JsonToken.Integer)
{
errorMessage = "Unexpected token when reading date constructor. Expected Integer, got " + reader.TokenType;
return false;
}
integer = (long)reader.Value;
return true;
}
}
}