Is there anyway to change the default JSON serialization/deserialization of DateTime in WCF?
Currently, DateTime are serialized into the /Date(1372252162657+0200)/ format, which should've been fine but I'm running into issues when my server is not in UTC (which I can't change).
All date/time data that is being processed by this service is in UTC format. Everything works when the server is in UTC. However, the staging/prod environments are set to GMT+1(Paris) and the serializer is assuming that the dates/times are in GMT+1, completely ignoring the attribute Kind. So as you'd expect calling DateTime.SetKind() and setting it to UTC will not work. In effect, the serialized times are delayed by an hour.
I can either do two-way date conversations (it also makes the same assumption when deserializing so its always GMT+1) conversation of dates: UTC to/from server time, but this is to tedious. So I thought maybe I could just override the default serialization behavior.
Just to expand on tdelepine's code snippet, here the code I've used:
In my WCF JSON Service, I had a (nullable) DateTime value, and wanted my service to return the date in a more readable format, so my iPhone app would be able to interpret it.
Here's what my JSON looked like, after applying a few changes:
Notice the UpdateDateOriginal field, which is the default way that WCF writes DateTimes, and the friendlier UpdateDate field, which I created using the code below.
My original lines looked like this:
[DataMember]
public DateTime? UpdateDateOriginal { get; set; }
... and here are the lines to create the new friendlier UpdateDate JSON value.
[IgnoreDataMember]
public DateTime? UpdateDate { get; set; }
[DataMember(Name = "UpdateDate")]
private string UpdateDateString { get; set; }
[OnSerializing]
void OnSerializing(StreamingContext context)
{
if (this.UpdateDate == null)
this.UpdateDateString = "";
else
this.UpdateDateString = this.UpdateDate.Value.ToString("MMM/dd/yyyy HH:mm", CultureInfo.InvariantCulture);
}
[OnDeserialized]
void OnDeserializing(StreamingContext context)
{
if (this.UpdateDateString == null)
this.UpdateDate = null;
else
this.UpdateDate = DateTime.ParseExact(this.UpdateDateString, "MMM/dd/yyyy HH:mm", CultureInfo.InvariantCulture);
}
Actually, you may find it more useful to return DateTime values in ISO8601 format. For example:
UpdateTime: "2014-08-24T13:02:32",
To do this, simply use my code above, but change the string "MMM/dd/yyyy HH:mm" to "s" in both places.
And, if your DateTime values are stored in UTC, but you wanted your WCF services to return the values in the user's local timezone, you can follow my tips here:
Get DateTime in users local timezone
Isn't life easier, with a few simple examples !
you can use this workaround, In your json object definition
[IgnoreDataMember]
public DateTime dateObject;
public string dateCustomSerialize
{
get {
//Custom get
}
set {
//Custom set
}
}
In assessor place your custom format serialisation
Yes, this can be done using the concept called "Message Formatters"
But Message Formatter would be tough and out of scope to explain here on stack overflow.
You can refere WCF Extensibility : Message Formatters
If you don't want mess up with this then an hack is available.
Set the return type of each method to Stream.
e.g.
public Stream GetStaticData()
{
var objTobeReturned = something;
WebOperationContext.Current.OutgoingResponse.ContentType = "application/json; charset=utf-8";
return new MemoryStream(Encoding.UTF8.GetBytes(objTobeReturned.ToJson()));
}
here ToJson() is my own extension method which converts object into json string using NewtonSoft library.
WCF will skip the stream output for serializing and will pass it to your client as it is.
I hope you got your answer.
This does not solve your issue of timezones, but I'll post it here for others who are battling it out with WCF, ticks and DateTime.
If you don't want ticks, but human-readable time format, you can do it by introducing an additional string property. Then it's a matter of fiddling with the DateTime before turning the value into a string.
[IgnoreDataMember] // Ignore the original tick date.
public DateTime LastReminderDate { get { return _lastReminderDate; } set { _lastReminderDate = value; } }
[DataMember] // Make sure you have a public or private setter!
public string LastReminderDateText { get { return _lastReminderDate.ToString(); } set { _lastReminderDate = DateTime.Parse(value); } }
One way is to use a message formatter to change the default DataContractSerializer as described in WCF Extensibility – Message Formatters.
Another option is to write an extension method that loads your object into a stream and then you can apply whatever serializer you want to the object. See the accepted answer for Replace default JSON serializer in WCF 4 to JSON.NET for details on how to do this.
Related
There's not much that's really that remarkable about my code. Here's basically what I'm concerned about.
Node.cs
public class Node {
public string Name { get; set; }
public DateTime LastCheckin { get; set; }
}
NodeUpdateReceiver.cs Get's fired by an EventHandler. Process is provided with a Node
public class NodeUpdateReceiver {
IDatabaseService databaseService { get; }
public NodeUpdateReceiver(IDatabaseService _dataService)
{
dataService = _dataService;
}
public async Task Process(Node node)
{
var newDate = DateTime.UtcNow
Console.WriteLine(newDate); // 5/23/2018 1:58:41 PM - correct UTC
node.LastCheckin = newDate;
Console.WriteLine(node.LastCheckin) // 5/23/2018 9:25:19 AM - local time... wtf??
dataService.SaveNodeAsync(node);
}
}
How in the hell can that happen? What am I missing about assigning this DateTime that converts it to local time? Any pointers or resources would be greatly appreciated.
node.LastCheckin is of type DateTime
What you're seeing when you do:
Console.WriteLine(node.LastCheckin);
Is the local representation of that instance of DateTime
If that were to be executed on a different machine, with a different TimeZone, you'd see another result (still offset to UTC)
To get UTC back out, you'll need to use .ToUniversalTime()
Console.WriteLine(node.LastCheckin.ToUniversalTime());
It turns out it was something specific to my Database of choice (LiteDB, not shown in my example). I should have included it.
It's unclear what happened exactly. I can't seem to find a reason why, but the UTC time was being stored as Local time. Alex's suggestion of using check.ToUniversalTime() to evaluate the DateTime worked.
This github issue talks about some of the DateTime complexities involved with LiteDB, but not mine specifically:
https://github.com/mbdavid/LiteDB/issues/794
For DateTime type, if I have a DateTime property inside a class then I try to model bind that class with HTTP post json body, the DateTime property does not get bound, how so? But if I use parameter binding, it's working fine. So below code works
[Route("v1/taxiqi")]
[HttpPost]
public object UpdateTaxiQI(string status, DateTime updateTime)
{
...
}
But not for below code
[Route("v1/taxiqi")]
[HttpPost]
public object UpdateTaxiQI(TaxiQI taxiQI)
{
...
}
public class TaxiQI
{
public string Status;
public DateTime UpdateTime;
}
I am using the latest ASP.NET Web API. Fields are working as it is working on my other API, besides, Status field is successfully bound. And I already tried properties, same result.
Sorry, I found out the root cause, it is caused by our code base is using a custom DateTimeConverter for JSON.NET and it expects a JavaScript style ticks instead of a date string
Your TaxiQI class is incorrect.
The JSON works with public properties not public fields. Change your class to this:
public class TaxiQI
{
public string Status { get; set; }
public DateTime UpdateTime { get; set; }
}
In addition to Svek's answer, it may be wiser to use a Nullable<DateTime> or DateTime? instead. Null values are an intrinsic possibility whenever you deserialize data.
I found out the root cause, my bad, it is caused by our code base is using a custom DateTimeConverter for JSON.NET and it expects a JavaScript style ticks instead of a date string
I am working with the you-tube API, which returns JSON data. The video published date is in this format: "publishedAt": "2017-04-30T18:18:41.000Z".
After deserializing the JSON object, I want to get the date from the published DateTime in C#.
How can I do it, and what is this format of DateTime?
There's absolutely no need to manually parse a well-formatted ISO 8601 date.
Simply change the property on your model from string to DateTime:
public class VideoData
{
[JsonProperty("publishedAt")]
public DateTime PublishedAt { get; set; }
}
And then deserialize into that:
var model = JsonConvert.DeserializeObject<VideoData>(jsonString);
And Json.NET will handle the rest.
I'm using Json.NET/newtonsoft and I have the following C# class:
public class EntityDefinition
{
[DataMember]
public string CreatedBy { get; set; }
[DataMember]
[JsonProperty(ItemConverterType = typeof(IsoDateTimeConverter))]
public DateTime CreatedOn { get; set; }
}
When I try to return this class in my wcf I'm getting the following JSON:
{
"GetDefinitionResult": {
"CreatedBy": "Dor",
"CreatedOn": "/Date(1466428742000+0300)/"
}
}
How can I get the date to be parsed without the "Date(", meaning only the milliseconds or in iso format "yyy-mm-dd"
I tried using the JsonProperty convertor but it still returns the "Date()"
[JsonProperty(ItemConverterType = typeof(IsoDateTimeConverter))]
WCF is using DataContractSerializer by default to serialize/deserialize messages and the mentioned date format is its default format.
If you'd like to change the way your WCF service serialize/deserialize messages, you should replace some things in the service's behavior (mainly IDispatchMessageFormatter). However, it's too long to describe here and there's a great tutorial about it here
Good Luck
Try [JsonProperty(ItemConverterType = typeof(JavaScriptDateTimeConverter)) or use CustomDateConverter as explained here in Parsing JSON DateTime from Newtonsoft's JSON Serializer
I'm trying to deserialize the following:
{"ts":"2012-04-22 04:14:50,669", "msg":"Hello"}
into
public class LogEntry
{
public DateTime Ts { get; set; }
public string Msg { get; set; }
}
using
var logEntry = JsonConvert.DeserializeObject<LogEntry>(line);
But get a JsonSerializationException saying "{"Error converting value \"2012-04-22 04:14:28,478\" to type 'System.DateTime'. Line 1, position 31."}. I cannot change the log format.
I think I might need to parse the date string myself using a Converter. However, I cannot find any examples of JsonConverter that seem relevant. Specifically how to read the value from the reader in the ReadJson method.
Are there any simple examples that I should look at? Or am I going about this the wrong way?
The format on your DateTime string uses a comma for the decimal separator (,478). You may be able to initialise a JsonSerializerSettings object (documented here) with an appropriate Culture, and then deserialise using DeserializeObject<T>(value, settings) (documented here). This would deserialise using the culture you specify rather than the default InvariantCulture.
I suspect the issue is because the value you are getting is using a comma as the decimal separator which suggests it was created in a locale that uses commas (e.g. a lot of European languages other than English). You could try changing your locale to match so that the parsing would work?