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?
Related
I have a post method that accepts object containing decimal property as below
[HttpPost]
public async Task<IActionResult> Create(CreateDto createDto)
{
...do stuff here
}
public class CreateDto
{
[Required]
public string Id{ get; set; }
[Required]
public decimal? Rate{ get; set; }
}
when a value containing leading 0 being passed in for Rate field, eg: 0015, it is always coming in as 10.
Would appreciate if somebody could explain this phenomena.
The issue lies within aspnet core mvc JSON parser which allows leading zeros before numbers that are all in the range [0-7], and treats them as octal, to match the sort of results one would get using eval in javascript.
This behaviour contradicts with guildlines stated in on http://json.org:
"A number is very much like a C or Java number, except that the octal
and hexadecimal formats are not used."
and the syntax graph does not allow a number to have a leading non-significant zero.
RFC 4627 Section 2.4. agrees:
"Octal and hex forms are not allowed. Leading zeros are not allowed."
So in short, the only way is to use JSON parser that implements the Json specs correctly.
Reference: Override Json deserializing a number with a leading zero as a decimal and not an octal value
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.
Have a Json API model with a date property defined as:
[Required]
[DataType(DataType.Date, ErrorMessage = "Invalid expiry date")]
public DateTime ExpiryDate { get; set; }
when posting an incorrect value for the ExpiryDate, examples:
"ExpiryDate":"2020-02-31T00:00:00",
"ExpiryDate":"2020-01-99T00:00:00",
"ExpiryDate":"abc",
the ModelState.Values.Errors[0].ErrorMessage is empty. Instead there is Model exception that I can not return to the API consumer, looks ugly.
ModelState.Values.Errors[0].Exception = {"Could not convert string to DateTime: 2020-02-31T00:00:00. Path 'CardDetails.ExpiryDate', line 13, position 39."}
My question are: how can I make the data annotation generate an error instead of an exception? Why is not the current data annotation giving an error, is not the job of [DataType(DataType.Date...] to do that?
The main issue here is that a DateTime value in JSON (at least according to the JSON parser) has a specific format, and not following that format is a parsing error, which is what's currently happening.
I think you'll have to take the value in as a string and do a conversion and validation on top of it. There's a couple of options. One is a custom ValidationAttribute like #erikscandola mentioned. Another is to implmenent IValidatableObject interface on your model.
You could also convert the model property to a string and simply do a check in the controller action:
DateTime expiryDate;
if (!DateTime.TryParse(model.ExpiryDate, out expiryDate))
{
ModelState.AddModelError("", string.Format(
"The given ExpiryDate '{0}' was not valid", model.ExpiryDate));
}
The approach depends upon how much reuse you need of the validation logic.
You should create a custom data annotation:
public class RequiredDateTimeAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
// Here your code to check date in value
}
}
In one .cs file I have the following:
public string DateEntered { get; set; }
In another .cs file then I have another string to grab a certain date.
public string HighestDate
{
get
{
...
return d.DateEntered;
}
}
Wondering how to convert the output from yyyy-MM-dd to MM-dd-yyyy? The scope in which it is used is as follows:
data.HighestDate
Thanks in advance.
EDIT:
Another part of the code that I think is integral:
var s = this...(i => i.DateEntered == DateTime.UtcNow.ToString("MM-dd-yyyy"));
However this only changes certain instances of the date format.
You should always store dates as DateTime variables:
public DateTime DateEntered { get; set; }
Then you can render the date however you like in your consumer application (UI):
public string HighestDate
{
get
{
...
return d.DateEntered.ToString("MM-dd-yyyy");
}
}
If you save them both as DateTime, then you can use data.HighestDate.ToString("MM-dd-yyyy"). Otherwise, you could use:
DateTime.ParseExact(data.HighestDate, "yyyy-MM-dd", CultureInfo.InvariantCulture).ToString("MM-dd-yyyy")
Although you should still try to modify your code to save them as DateTime objects, this is the right-proper way to do it. (And saves memory space.)
https://msdn.microsoft.com/en-us/library/w2sa9yss%28v=vs.110%29.aspx
https://msdn.microsoft.com/en-us/library/zdtaw1bw(v=vs.110).aspx
Quick Googling would have net you your result.
Firstly, I recommend using a DateTime to store the value and turn it into a string at the last possible moment.
Secondly, use DateTime.ParseExact to read a particular format from a string and use ToString("some format") to format a DateTime to a string
DateTime parsedDate;
if (DateTime.TryParse(data.HighestDate, out parsedDate))
{
var reformatted = parsedDate.ToString("MM-dd-yyyy");
}
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.