Force.com-Toolkit-for-NET
Force.com-Toolkit-for-NET copied to clipboard
ForceClient Query methods going through HttpGetAsync in JsonHttpClient lose correct offset in DateTimeOffset objects by use of JObject.Parse
I'm querying User objects and using json classes with DateTimeOffset fields. The strings coming back from salesforce in the json include explicit offset info (UTC in my case), but due to the current deserialization round-tripping through JObject.Parse (which uses DateTime instead of DateTimeOffset on such strings), the correct timezone offset is lost and you get your local offset instead. The time is still correct, but we've lost the offset info that was in the json we got back from the API call.
https://github.com/developerforce/Force.com-Toolkit-for-NET/blob/03a0e068032111263a3e04b1069569d4ef219358/src/CommonLibrariesForNET/JsonHttpClient.cs#L52-L60
This problem doesn't happen if you just use JsonConvert.DeserializeObject directly (like what the catch clause does) so ideally we just do that instead of the JObject round trip which causes this problem?
Hitting this using ForceClient will happen with any query method, but an example call (the Dump call is just for displaying in linqpad, it doesn't affect the object) is:
var user = await forceClient.QueryByIdAsync<User>("User", "0051U000000234WQAU");
user.Dump("from api");
using a simple POCO of
public class User
{
public string Id { get; set; }
public DateTimeOffset CreatedDate { get; set; }
}
You can also see the 'timezone offset lost in JObject roundtrip' behavior manually:
var testJson = @"
{
""Id"": ""0051U000000123WQAU"",
""CreatedDate"": ""2018-12-04T15:05:29.000+0000"",
}
";
var deserializedUser = JsonConvert.DeserializeObject<User>(testJson);
deserializedUser.Dump();
var jobject = JObject.Parse(testJson);
var jobjectJson = jobject.ToString().Dump("JObject.Parse().ToString()");
var deserializedViaJObjectUser = JsonConvert.DeserializeObject<User>(jobjectJson);
deserializedUser.Dump();
This Json.Net behavior isn't well documented but has been mentioned before and seems to be intentional (although I'm going to try to file a new explicit bug about it just to confirm)
https://github.com/JamesNK/Newtonsoft.Json/issues/1110
If the JObject round trip is needed for some reason, could the code add a comment (and ideally unit test that would break if I removed that round trip) to explain why? It certainly doesn't seem to be helping and only hurting in this particular scenario, so it'd be great to understand if there was a scenario where this round trip was necessary.
Thanks!
Also filed a bug on Json.Net just to attempt to definitively establish whether the JObject.Parse behavior is By Design or whether it should preserve offset (presumably by deserializing into a DateTimeOffset instead of a DateTime)
https://github.com/JamesNK/Newtonsoft.Json/issues/1993
This should help at least determine whether Json.Net is behaving as expected or not. I still think the JsonHttpClient code should just use JsonConvert.DeserializeObject<T> directly since the JObject.Parse doesn't seem to provide any value (and causes this breakage), though.
I also face similar behavior. I have to manually look at CreatedDate/LastModifiedDate's Kind
field and change datetime to UTC.