Fixing JSON Date serialization from .Net web method - c#

I am currently working on a project where I am sending a .Net type via ajax to a client application via ajax. I have no issues with the object being serialized and set to the client.
I run into issues when I take the exactly same object and post it back to the server via a web method with the following error: /Date(1373950800000)/ is not a valid value for DateTime. Which is pretty annoying as that is how Microsoft gave it to me, but that's besides the point.
Does anyone have a quick fix for this? I want a seamless way this can be accomplished without having to change the object right before returning it from the ajax call.

Your issue comes down to the server-side JavaScript serializer you are using; either JsonDataContractSerializer (default serializer for ASP.NET MVC) or NewtonSoft Json Serializer (default serializer for ASP.NET Web API).
For a visual example of this date mangling issue as well as possible solutions, check out JSON Dates are Different in ASP.NET MVC and Web API.

Handle the DateFormat while serialization
following code will resolve your problem
JsonConvert.SerializeObject(yourobject, Formatting.Indented,
new JsonSerializerSettings
{
DateFormatHandling = DateFormatHandling.IsoDateFormat
});
It will result the dates in 2009-02-15T00:00:00Z format

This one will help you with the error: click me
var yourDateTimeObject = ...
var converter = new IsoDateTimeConverter();
string isoDateTime = Newtonsoft.Json.JsonConvert.SerializeObject(yourDateTimeObject, converter);

This is the method that I use for these things:
First you need to clean the junk out of the date parameter
String unixDate = "/Date(1373950800000)/";
unixDate = unixDate.Replace("/Date(","").Replace(")/", "");
Now, as .NET and unix measure time in a different way, you have to compensate for that by creating a date set to the 1st of Jan 1970 and then adding the numeric part of the date that you were passed
DateTime dotNetDate = new DateTime(1970, 1, 1);
dotNetDate = dotNetDate.AddMilliseconds(Convert.ToInt64(unixDate)
You should also note that there will a loss of precision here - .NET dates are to the nearest nanosecond where unix dates are to the nearest millisecond

Try sending the dates with the forward slashes escaped. I have an iPad client that posts JSON to an ASP.NET WebAPI service method and we have to send dates this way:
"due": "\/Date(1335830400000)\/"

Related

Maintaining source string format when reading a date-time from a JSON path and writing it to another file

How can I have Newtonsoft.Json read the value of a path without converting or otherwise meddling with values?
This code
jsonObject.SelectToken("path.to.nested.value").ToString()
Returns this string
03/07/2019 00:02:12
From this string in the JSON document
2019-07-03T00:02:12.1542739Z
It's lost its original formatting, ISO 8601 in this case.
I would like all values to come through as strings, verbatim. I'm writing code to reshape JSON into other formats and I don't want to effect the values as they pass through my .NET code.
What do I need to change? I am not wedded to Newtonsoft.Json btw.
I got it, I think.
jsonObject.SelectToken(path).ToString(Newtonsoft.Json.Formatting.None);
The other options were to supply nothing or this.
Newtonsoft.Json.Formatting.Indented
Which is strange logic in this API as you'd think None means not indented but it means not ... I don't know. Hang on....
Okay so None or Indented returns
"2019-07-03T00:02:12.1542739Z"
(including quotes) but using the overload taking no parameters returns
03/07/2019 00:02:12
That's an odd API design ¯\_(ツ)_/¯
Here's a screenshot which shows really simple repro code.

Assigning date field from dynamic context to string value in C#

I have come across this scenario where an API call is done from an angular service.
From angular Service:
param = {
date:"2018-10-10T17:03:38.000Z",
id:"1234"
}
//...
this.http.post(url, param).map(res => res.json()) // call to an MVC API
//MVC API
[HttpPost]
public IHttpActionResult UpdateDate(dynamic context)
{
string id = context.id; //stores value as is
string isoDate = context.date;
// iso formatted date string converts back to the following format
//"10/10/2018 15:20:55"
}
isoDate field now have "10/10/2018 15:20:55" instead of original value "2018-10-10T17:03:38.000Z"
Any idea?
I came up with the work around is pass the date with suffix and prefix as follows
"|2018-10-10T17:03:38.000Z|" and replaced "|" with empty string once the value of isoDate is assigned.
This seems to be a hack still but works.
Just wanted to know the inside why this happens.
Thank you.
The only real primitive types in json are strings, numbers, and booleans so conversion to dynamic (expando object) can be done only for those types. Everything else passed in as string (like your ISO8601 formatted date time) stays as string.
If you want conversion to a DateTime you would have to use a strongly typed model so that the deserializer (json.net by default for web-api) will attempt to parse the incoming string value to a DateTime type. Using a strong typed model is recommended as there is less chance of naming errors at run time (because you would catch those at compile time) and you can validate the model with model validation annotations like RequiredAttribute.
See also JSON Data Types
I encounter the same error today.
Inspecting the property of the dynamic object that contains the ISO string date, I saw that it was already converted to an object that contained a Value property that was a Date object (somne implicit conversion takes place, that's for sure).
In the image, parametros is a dynamic object and dataPartida the property that was a ISO date string sent as a POST body payload from Postman.
To solve my problem, I did:
string dataPartida = parametros["dataPartida"].Value.ToString("yyyy-MM-dd'T'HH:mm:ss");
Hope it helps!

How use a specific CultureInfo in Asp.net Web API

I've recently added WEB API to an existing VS desktop application and everything worked fine, until yesterday when I had to add a GET method that took three parameters, one of them a Date.
Well, at first I thought that that was going to be a piece of cake, but much to my surprise I noticed that when I sent 2014/07/09 (9th July) on the server where the application was installed it was treated like 2014/09/07 (7th September) and for that reason all my comparisons never worked.
I have tried things like changing from a GET method to a POST method, changing my Regional and Language Options settings to the same on the server, passing the date as a String a created a Datetime object on the server using the parts of the string. Unfortunately none of them worked.
Then I remember that this desktop application have some methods on its WCF project (which I'm passing now to web API) that passed dates with no problem at all. Looking in the code for a while I found that they used something like this on every class of they WCF project that uses dates:
Imports System.Globalization
Imports System.Security.Permissions
Imports System.Threading
Public Class ServicioRemotoVentas
Implements IServicioRemotoVentas
Public Sub New()
MyBase.New()
Thread.CurrentThread.CurrentCulture = New CultureInfo("es-PE", False)
End Sub
Surely this Thread.CurrentThread.CurrentCulture = New CultureInfo("es-PE", False), must be there for something.
Now I would like to know if you have used something like that in Web API before? if so how and where did you put such a configuration.
These are the settings on my pc :
And these are the server settings:
I almost forgot to mention that I pass all the dates using this format yyyy/M/d with all the other parameters using json.
Is it perhaps that when the string is deserialized in the Web API this is done using the system date format because I haven't specify the culture info to use?? or maybe it is a Json error when trying serialize/deserialize the dates??
As always, any advice or resources you could provide would be greatly appreciated.
As discussed in the comments, the ASP.NET runtime does have a solution for these scenarios: it is the web.cofig element <globalization> - (see MSDN <globalization> Element)
It's structure is defined as:
<configuration>
<system.web>
<globalization
enableClientBasedCulture="true|false"
requestEncoding="any valid encoding string"
responseEncoding="any valid encoding string"
fileEncoding="any valid encoding string"
responseHeaderEncoding = "any valid encoding string"
resourceProviderFactoryType = string
enableBestFitResponseEncoding = "true|false"
culture="any valid culture string"
uiCulture="any valid culture string"/>
So, in case, that we want to force server/dev workstation to act in en-US culture we should use these explicit settings:
<globalization
enableClientBasedCulture="false"
uiCulture="en-US"
culture="en-US" />
This will use the proper (desired and set) culture for any http request.
Also interesting could be the default setting overview:
<globalization
requestEncoding="utf-8"
responseEncoding="utf-8"
fileEncoding=""
culture=""
uiCulture=""
enableClientBasedCulture="false"
responseHeaderEncoding="utf-8"
resourceProviderFactoryType=""
enableBestFitResponseEncoding="false" />
See also similar here:
Set uiCulture automatically based on browser accept language
How to change my computer's cultureInfo
and the globalization Element itself
It is mentioned in your question that the input is a string not a datetime object ("I almost forgot to mention that I pass all the dates using this format yyyy/M/d").
Your service interface isn't posted but I guess that the parameter type is DateTime and this is why the deserialization is incorrect.
Although the setting posted by Radim works it's not a fix but a hack/workaround.
There are two recommended ways to implement this:
Use strings at both ends, i.e. the API parameter should also be string and the API specification should state what date format to use. Use DateTime.Parse(String, IFormatProvider) and specify the culture in contract (i.e. new CultureInfo("es-PE")) when the input string is converted to DateTime.
https://msdn.microsoft.com/en-gb/kc8s65zs
Use date objects at both ends. In this case the serializer will serialize the date object to a well known, culture independent format and the desrializer will deserialize the string to correct DateTime object.
public void GetOrders(DatetTime fromDate)
I'd go with #2 because is generic and doesn't force the client to use a specific culture.
See also:
The "right" JSON date format

WP7 Json serializer doesn't handle "space"?

I recently made a post here (which is now marked as "answered" - which it is) about parsing Google Calc json strings into WP7 http://www.google.com/ig/calculator?hl=en&q=100GBP=?SEK.
It's working great - unless Google returns a number above 999. A number above 999 is writenn 1 000, instead of 1000. It seems like the "space" makes the application crash/try-catch aware that there's something wrong.
I just wonder how I can make the json serializer (using System.Runtime.Serialization.Json;) (using StringBuilder) return sum/amount(s) above 999, without crashing?
Thanks :)
CODE:
Hello! I'm mainly using the code found here: Parse Google Calculator with Json in Windows Phone 7 / C#?
In order to get currency landcodes from listbox, I use:
ListBoxItem toExchangeSelected= toCurrencyList.ItemContainerGenerator.ContainerFromItem(this.toCurrencyListtaListe.SelectedItem) as ListBoxItem;
string toCurrency = toCurrencyList.Content.ToString();
ListBoxItem fromExchangeSelected= fromCurrencyList.ItemContainerGenerator.ContainerFromItem(this.fromCurrencyList.SelectedItem) as ListBoxItem;
string fromCurrency = fromExchangeSelected.Content.ToString();
Certain European cultures use spaces instead of commas for large numbers, so try using the appropriate CultureInfo before you parse the string:
CultureInfo ci = new CultureInfo("fr-FR");
double d = double.Parse("1 000", ci); // returns 1000.0
Try using the newtonsoft json serializer.
http://json.codeplex.com/
They have a binaries for wp7, and is better that the datacontractserializer (in my view)
I've just noticed that the second answer of your mentionned question on StackOverflow is the same as the one i'm talking about.
Use JsonConvert.Deserialize<T>(string json) with T as your result (ExchangeRate ?)

Javascript culture always en-us

I'm not sure if I understand this code or if I'm using it right, but I was under the impression that in an ASP.NET 2.0 AJAX website I could run javascript like:
var c = Sys.CultureInfo.CurrentCulture
and it would give me the culture/language settings the user had specified in their browser at the time of the visit. However, for me, it always comes back 'en-US' no matter what language I pick in firefox or IE.
This serverside code however:
string[] languages = HttpContext.Current.Request.UserLanguages;
if (languages == null || languages.Length == 0)
return null;
try
{
string language = languages[0].ToLowerInvariant().Trim();
return CultureInfo.CreateSpecificCulture(language);
}
catch (ArgumentException)
{
return null;
}
does return the language I have currently set. But I need to do this clientside, because I need to parse a string into a datetime and do some validations before I postback, and the string could be a MM/DD/YYYY or DD/MM/YYYY, or some other such thing.
What am I missing?
EDIT:
Ok, I came across this library http://www.datejs.com/. It looks pretty sweet, and it has ninjas, so I'm pretty much sold already. But in their doc I noticed that you need to include one of the language specific js files to include. I imagine you'd have to make that determination serverside and emit the proper script include tag, which got me thinking. Am I supposed to be doing this with the ASP.NET AJAX stuff? i.e. checking the serverside culture(which works) and setting the Sys.CultureInfo.CurrentCulture in js based on that? I was hoping it would just automagically get it from the browser clientside.
The value of Sys.CultureInfo.CurrentCulture is based on a value that is sent from the server. To get the server to send the correct value back to the client, you need to set the EnableScriptGlobalization property to true on the ScriptManager object (it's false by default).
TMK, the browser does not pass any timezone info to server in the first request. You need to do this with Javascript

Categories