Skip to content

Commit

Permalink
Add support to deserialize & serialize TimeSpan (#183)
Browse files Browse the repository at this point in the history
  • Loading branch information
josesimoes authored Feb 12, 2022
1 parent 0052f73 commit 6b89a64
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 2 deletions.
48 changes: 48 additions & 0 deletions nanoFramework.Json.Test/JsonUnitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ public class JsonTestClassTimestamp
public DateTime FixedTimestamp { get; set; }
}

public class JsonTestClassTimespan
{
public TimeSpan Duration1 { get; set; } = TimeSpan.FromMinutes(69);
public TimeSpan Duration2 { get; set; }
public TimeSpan Duration3 { get; set; }
public TimeSpan Duration4 { get; set; }
public TimeSpan Duration5 { get; set; }
public int DummyValue1 { get; set; } = -999;
public uint DummyValue2 { get; set; } = 777;
}

public class JsonTestClassComplex
{
public int aInteger { get; set; }
Expand Down Expand Up @@ -276,6 +287,43 @@ public void Can_serialize_deserialize_timestamp()
Debug.WriteLine("");
}

[TestMethod]
public void Can_serialize_deserialize_timespan()
{
Debug.WriteLine("Can_serialize_deserialize_timespan() - Starting test...");

var randomValue = new Random();

var timeSpanTests = new JsonTestClassTimespan()
{
Duration2 = TimeSpan.FromSeconds(randomValue.Next(59)),
Duration3 = TimeSpan.FromMilliseconds(randomValue.Next(999)),
Duration4 = TimeSpan.FromHours(randomValue.Next(99)),
Duration5 = TimeSpan.FromDays(randomValue.Next(999)),
};

Debug.WriteLine($"Fixed timespan used for test = {timeSpanTests.Duration1}");
Debug.WriteLine($"variable timespan 2 used for test = {timeSpanTests.Duration2}");
Debug.WriteLine($"variable timespan 3 used for test = {timeSpanTests.Duration3}");
Debug.WriteLine($"variable timespan 4 used for test = {timeSpanTests.Duration4}");
Debug.WriteLine($"variable timespan 5 used for test = {timeSpanTests.Duration5}");

var result = JsonConvert.SerializeObject(timeSpanTests);
Debug.WriteLine($"Serialized class: {result}");

var dserResult = (JsonTestClassTimespan)JsonConvert.DeserializeObject(result, typeof(JsonTestClassTimespan));
Debug.WriteLine($"After Type deserialization: {dserResult}");

Assert.Equal(timeSpanTests.Duration1.ToString(), dserResult.Duration1.ToString(), $"wrong value for Duration1, expected 1:09:00, got {dserResult.Duration1.ToString()}");
Assert.Equal(timeSpanTests.Duration2.Ticks.ToString(), dserResult.Duration2.Ticks.ToString(), $"wrong value for Duration2, expected {timeSpanTests.Duration2}, got {dserResult.Duration2}");
Assert.Equal(timeSpanTests.Duration3.Ticks.ToString(), dserResult.Duration3.Ticks.ToString(), $"wrong value for Duration3, expected {timeSpanTests.Duration3}, got {dserResult.Duration3}");
Assert.Equal(timeSpanTests.Duration4.Ticks.ToString(), dserResult.Duration4.Ticks.ToString(), $"wrong value for Duration4, expected {timeSpanTests.Duration4}, got {dserResult.Duration4}");
Assert.Equal(timeSpanTests.Duration5.Ticks.ToString(), dserResult.Duration5.Ticks.ToString(), $"wrong value for Duration5, expected {timeSpanTests.Duration5}, got {dserResult.Duration5}");

Debug.WriteLine("Can_serialize_deserialize_timespan() - Finished - test succeeded.");
Debug.WriteLine("");
}

[TestMethod]
public void Can_serialize_short_array()
{
Expand Down
11 changes: 11 additions & 0 deletions nanoFramework.Json/JsonConvert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,17 @@ private static object PopulateObject(JsonToken rootToken, Type rootType, string
memberPropSetMethod.Invoke(rootInstance, new object[] { Convert.ToBoolean(Convert.ToByte(val.Value.ToString())) });
break;

case nameof(TimeSpan):
if (TimeSpanExtensions.TryConvertFromString(val.Value.ToString(), out TimeSpan value))
{
memberPropSetMethod.Invoke(rootInstance, new object[] { value });
break;
}
else
{
return null;
}

default:
memberPropSetMethod.Invoke(rootInstance, new object[] { memberPropertyValue.Value });
break;
Expand Down
6 changes: 4 additions & 2 deletions nanoFramework.Json/JsonValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,11 @@ public override string ToString()

var type = Value.GetType();

if (type == typeof(string) || type == typeof(char))
if (type == typeof(string)
|| type == typeof(char)
|| type == typeof(TimeSpan))
{
return "\"" + this.Value.ToString() + "\"";
return "\"" + Value.ToString() + "\"";
}
else if (type == typeof(DateTime))
{
Expand Down
58 changes: 58 additions & 0 deletions nanoFramework.Json/TimeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,4 +213,62 @@ internal static bool ConvertFromString(string value, out DateTime dateTime)
return dateTime != DateTime.MaxValue;
}
}

internal static class TimeSpanExtensions
{
/// <summary>
/// Try converting a string value to a <see cref="TimeSpan"/>.
/// </summary>
/// <param name="value"><see cref="string"/> to convert.</param>
/// <param name="timeSpan"><see cref="TimeSpan"/> converted from <paramref name="value"/>.</param>
/// <returns><see langword="true"/> if conversion was successful. <see langword="false"/> otherwise.</returns>
internal static bool TryConvertFromString(string value, out TimeSpan timeSpan)
{
timeSpan = TimeSpan.MinValue;

// split string value with all possible separators
// format is: ddddd.HH:mm:ss.fffffff
var timeSpanBits = value.Split(':', '.');

// sanity check
if (timeSpanBits.Length < 3)
{
return false;
}

int days = 0;
int ticks = 0;

// figure out where the separators are
int indexOfFirstDot = value.IndexOf('.');
int indexOfFirstColon = value.IndexOf(':');
int indexOfSecondDot = indexOfFirstDot > -1 ? value.IndexOf('.', indexOfFirstDot) : -1;

bool hasDays = indexOfFirstDot > 0 && indexOfFirstDot < indexOfFirstColon;
bool hasTicks = hasDays ? indexOfSecondDot > indexOfFirstDot : indexOfSecondDot > -1;

// start parsing
if (hasDays)
{
int.TryParse(timeSpanBits[0], out days);
}

int processIndex = hasDays ? 1 : 0;

int.TryParse(timeSpanBits[processIndex++], out int hours);
int.TryParse(timeSpanBits[processIndex++], out int minutes);
int.TryParse(timeSpanBits[processIndex++], out int seconds);

if (hasTicks)
{
int.TryParse(timeSpanBits[processIndex], out ticks);
}

// we should have everything now
timeSpan = new TimeSpan(ticks).Add(new TimeSpan(days, hours, minutes, seconds, 0));

// done here
return true;
}
}
}

0 comments on commit 6b89a64

Please sign in to comment.