Passing dates between c# and javascript - javascript

I have an ASP.Net MVC app that returns a view model, which when converted to JSON using system.web.mvc.jsonresult looks as follows:
On the client I'm using KnockoutJS. I use MomentJS to format the value for the VoucherDate so that it can be displayed for humans:
var recsArray = [];
$.each(data.Vouchers, function (key, value) {
recsArray.push(
new edited(
interchangeId,
value.SupplierIsValid,
value.VoucherNo,
value.LegacySupplierId,
value.Transactions,
moment(value.OriginalVoucher.VoucherDate).format('YYYY/MM/DD HH:mm'),
value.OriginalVoucher
)
);
As you can see from the previous code snippet, in addition to pushing the data into an observable array for display in a KOGrid, I also push the entire "OriginalVoucher". This enables the user to edit the value for "LegacySupplierId" and click "Resubmit" which posts back the entire view model as seen below:
self.resubmit = function () {
var data = {
Vouchers: ko.toJS(this.recs),
BatchId: self.batchId(),
InterchangeId: interchangeId,
IsReadWrite: self.isReadWrite,
Interface: self.interface,
ReportClient: self.reportClient
};
$.ajax({
type: "POST",
url: BASE_URL + 'EditBatch/ResubmitRejectedVouchersAsNewBatch',
data: ko.toJSON(data),
I've checked using Fiddler and confirmed that the VoucherDate fields contain values such as /Date(14543712000000)/. My problem is, when reading in the C# controller, all of the dates are presented as 1/1/0001 12:00:00 AM.
I've read that javascript has a date.toISOString() function to convert to a format that C# will be happy with. Is there a better way that trying to find each date field in the javascript view model and executing a conversion against each before posting back to the C# controller
I think I have previously solved this problem using automapper by following instructions here: enter link description here
In my source code I can see that I created the following class but I don't know how / if this gets used:
public class JsonDateTimeTypeConvertor : ITypeConverter<string, DateTime>
{
public DateTime Convert(ResolutionContext context)
{
string jsonDate = context.SourceValue.ToString();
string offsetAsString = Regex.Match(jsonDate, #"\d+").Value;
double offset = System.Convert.ToDouble(offsetAsString);
DateTime ret = DataUtils.ConvertFromUnixTimestamp(offset);
return ret;
}
}
I think it was being used but I've inadvertently disabled it. This probably happened when I changed the type for the ViewModel received by the controller. The previous version of the application worked, so I guess the datetime was being converted correctly - probably by this automapper extension. I have pulled the old version of the code from TFS but can't figure out how it's doing the magic - I wish I'd made better notes at the time! All I have is:
I needed to create a custom type convertor to deal with JSON dates being passed to the EditBatch controller in the format of number of milliseconds since 1900.
I followed the wiki documentation from here:
https://github.com/AutoMapper/AutoMapper/wiki/Custom-type-converters

May be you can use a regex pattern like this. The following code shows ToJavaScriptDate() function that does this for you:
function ToJavaScriptDate(value)
{
var pattern = /Date\(([^)]+)\)/;
var results = pattern.exec(value);
var dt = new Date(parseFloat(results[1]));
return (dt.getMonth() + 1) + "/" + dt.getDate() + "/" + dt.getFullYear();
}
The ToJavaScriptDate() function accepts a value in /Date(ticks)/ format and returns a date string in MM/dd/yyyy format. Inside, the ToJavaScriptDate() function uses a regular expression that represents a pattern /Date(([^)]+))/.
The exec() method accepts the source date value and tests for a match in the value. The return value of exec() is an array. In this case the second element of the results array (results[1]) holds the ticks part of the source date. For example, if the source value is /Date(836418600000)/ then results[1] will be 836418600000. Based on this ticks value a JavaScript Date object is formed. The Date object has a constructor that accepts the number of milliseconds since 1 January 1970. Thus dt holds a valid JavaScript Date object. The ToJavaScriptDate() function then formats the date as MM/dd/yyyy and returns to the caller.

I'm really not a fan of the way the default JavaScriptConverter handles dates. I use the following class. (The CustomString class is kind of a dirty hack to get around the fact that the Serialize command is expected to return an IDictionary. See blog post here: http://blog.calyptus.eu/seb/2011/12/custom-datetime-json-serialization/)
public class DateTimeJsonSerializer : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
if (dictionary == null)
return null;
return new JavaScriptSerializer().ConvertToType(dictionary, type);
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
if (!(obj is DateTime)) return null;
return new CustomString(((DateTime) obj).ToString("yyyy-MM-ddTHH:mm:ss.fffZ"));
}
public override IEnumerable<Type> SupportedTypes
{
get { return new ReadOnlyCollection<Type>(new List<Type> { typeof(DateTime), typeof(DateTime?) }); }
}
}
public class CustomString : Uri, IDictionary<string, object>
{
public CustomString(string str) : base(str, UriKind.Relative)
{}
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
throw new NotImplementedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Add(KeyValuePair<string, object> item)
{
throw new NotImplementedException();
}
public void Clear()
{
throw new NotImplementedException();
}
public bool Contains(KeyValuePair<string, object> item)
{
throw new NotImplementedException();
}
public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public bool Remove(KeyValuePair<string, object> item)
{
throw new NotImplementedException();
}
public int Count { get; private set; }
public bool IsReadOnly { get; private set; }
public bool ContainsKey(string key)
{
throw new NotImplementedException();
}
public void Add(string key, object value)
{
throw new NotImplementedException();
}
public bool Remove(string key)
{
throw new NotImplementedException();
}
public bool TryGetValue(string key, out object value)
{
throw new NotImplementedException();
}
public object this[string key]
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
public ICollection<string> Keys { get; private set; }
public ICollection<object> Values { get; private set; }
}
To use this in asp.net, I have the following section in my web.config (make sure to replace "AssemblyNameGoesHere" with the assembly that contains the converter class:
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="2147483644">
<converters>
<add name="DateTimeConverter" type="DateTimeJsonSerializer,AssemblyNameGoesHere" />
</converters>
</jsonSerialization>
</webServices>
</scripting>
</system.web.extensions>

I managed to work-around this by changing the data type on the "VoucherDate" field in the C# model from DateTime to String. I don't currently understand why this works. I also think there must be a better way!?

Related

How To Send Request To C# Method And Get Its Parameters by Jquery

Is there any way to call an API to know its required parameters?
for example, if there is a controller has a method takes a name and date as a parameters
[HttpPost]
public string testUrl(string name,string date)
{
return "success";
}
now what I am trying to do is to make a dynamic form take an API URL and generate all required inputs so I have to find some way to call the URL and return the required parameters.
The quick and dirty way would be to just hard code it:
[HttpPost]
public string testUrl(string name,string date)
{
return "success";
}
public APISchema TestUrlSchema(){
return new APISchema() {
Parameters = new List<Parameter>(){
new Parameter(){ Name = "name", Type = "String" },
new Parameter(){ Name = "date", Type = "String" }
}
};
}
public class APISchema {
public List<Parameter> Parameters {get;set;}
}
public class Parameter {
public String Name {get;set;}
public String Type {get;set;}
}
You could use Reflection to auto-generate this if you want to do it for a large number of actions.
As comments have mentioned, there are libraries Swagger which will do this for you.

Error deserializing JSON with Newtonsoft, C#

I'm receiving the following error:
Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'System.Collections.Generic.Dictionary`2[System.String,System.Collections.Generic.List`1[ReportingDataSchema.CurrentBusinessUnits]]'
because the type requires a JSON object (e.g. {\"name\":\"value\"}) to deserialize correctly.
To fix this error either change the JSON to a JSON object (e.g. {\"name\":\"value\"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List<T> that can be deserialized from a JSON array.
JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array. Path 'selectedBusinessUnits', line 1, position 26."} System.Exception {Newtonsoft.Json.JsonSerializationException}
My JSON:
{
"selectedBusinessUnits": [{
"guidNode": "some value",
"businessUnit": "some value",
"fileName": "some value"
}, {
...
}]
}
I'm trying to first transform this response into the following:
public class EnAFileGenerator
{
private Dictionary<string, List<CurrentBusinessUnits>> selectedBusinessUnits;
public Dictionary<string, List<CurrentBusinessUnits>> SelectedBusinessUnits
{
get { return selectedBusinessUnits; }
set { selectedBusinessUnits = value; }
}
}
So that ultimately I can access the array in the JSON, using the following:
public class CurrentBusinessUnits
{
private string guidNode;
private string businessUnit;
private string fileName;
public string GuidNode { get; set; }
public string BusinessUnit { get; set; }
public string FileName { get; set; }
}
The code that's generating the error:
// JSON Data is the parameter from the client, containing the above JSON object
EnAFileGenerator resultArray = JsonConvert.DeserializeObject<EnAFileGenerator>(JSONData);
From what I've read, it seems like my error is a result of the parsing the array (the value for the property selectedBusinessUnits) into the desired C# collection.
After implementing #DavidG's suggestion, I'm still receiving the following:
Error converting value \"{\"guidNode\":\"some value\",\"businessUnit\":\"some value\",\"fileName\":\"some value.xlsx\"}\"
to type 'ReportingDataSchema.CurrentBusinessUnits'. Path 'selectedBusinessUnits[0]', line 1, position 159."}
System.Exception {Newtonsoft.Json.JsonSerializationException}
Prany's solution nearly got me there. I was able to modify that code to utilize the objects I already had:
var files = JObject.Parse(JSONData);
var recList = files.SelectToken("$..selectedBusinessUnits").ToList();
foreach (string item in recList)
{
JObject businessUnit = JObject.Parse(item);
CurrentBusinessUnits currentBusinessUnit = businessUnit.ToObject<CurrentBusinessUnits>();
}
The problem is you are trying to deserialise into the wrong type. you have specified a Dictionary<string, List<CurrentBusinessUnits>> but really you only need a List<CurrentBusinessUnits>:
public class EnAFileGenerator
{
public List<CurrentBusinessUnits> SelectedBusinessUnits { get; set; }
}
You can use Jobject since you're using Newtonsoft. For getting values based on selectedBusinessUnits. Use below
var files = JObject.Parse(YourJson);
var recList = files.SelectToken("$..selectedBusinessUnits").ToList();
foreach (JObject item in recList)
{
foreach (JProperty prop in item.Children())
{
string key = prop.Name.ToString();
string value = prop.Value.ToString();
}
}

Is it possible to expose a .NET object to JavaScript which has a method returning an IEnumerable?

I'm trying to create a .NET class that will be exposed to JavaScript via the Jurassic JavaScript engine. The class represents an HTTP response object. Since an HTTP response may have multiple headers with the same name, I would like to include a method that returns an IEnumerable of the headers with a particular name.
This is what I have so far:
public class JsResponseInstance : ObjectInstance
{
private IDictionary<string, IList<string>> _headers;
public JsResponseInstance(ObjectInstance prototype)
: base(prototype)
{
this.PopulateFunctions();
_headers = new Dictionary<string, IList<string>>();
}
[JSFunction(Name = "addHeader")]
public virtual void addHeader(string name, string value)
{
IList<string> vals;
bool exists = _headers.TryGetValue(name, out vals);
if (!exists)
{
vals = new List<string>();
_headers[name] = vals;
}
vals.Add(value);
}
[JSFunction(Name = "getHeaders")]
public virtual IList<string> getHeaders(string name)
{
IList<string> vals;
bool exists = _headers.TryGetValue(name, out vals);
if (!exists)
{
return new List<string>();
}
return vals;
}
}
When I test the getHeaders method I get a JavascriptException: Unsupported type: System.Collections.Generic.IList'1[System.String]
I've tried changing the return type of the getHeaders method from IList to string[] and also adding the optional IsEnumerable property to the JSFunction attribute decorating the method. Neither change made a difference, I was still seeing the same exception.
Is there any way of returning an IEnumerable from a method in a .NET class that is exposed to JavaScript?
Paul Bartrum, the maintainer of Jurassic, answered this question on GitHub.
He stated that the method has to return a type derived from ObjectInstance. Since we need an enumerable, that return type should be an ArrayInstance.
The final working .NET code is:
[JSFunction(Name = "getHeaders")]
public virtual ArrayInstance getHeaders(string name)
{
IList<string> vals;
bool exists = _headers.TryGetValue(name, out vals);
if (!exists)
{
return this.Engine.Array.New();
}
return this.Engine.Array.New(vals.ToArray());
}

Return JavaScript object literal, not JSON string, from ASP.NET MVC endpoint

For various reasons, I have switched from ASP.NET MVC's built in JSON serializer (the one that returns a System.Web.Mvc.JsonResult object (see edit below)) to Newtonsoft. I didn't realize until after I began testing that the former returns a JavaScript object literal, while Newtonsoft returns a JSON formatted string.
I like not having to parse JSON strings on the client side — having it already as an object literal is very convenient — but I want to stick with Newtonsoft for other technical reasons.
For example, instead of seeing this result on my client...
"{"Errors":["Please enter a valid email address."],"HasErrors":true}"
...I'd like to see this result:
{"Errors":["Please enter a valid email address."],"HasErrors":true} // no quotes
Is there a way to make Newtonsoft return JS object literals instead of strings?
EDIT
The way my question was framed wasn't the best. There's nothing wrong with the JsonResult type. In fact, the solution still uses it. The only problem was the default Controller.Json methods, which can be overridden to use Newtonsoft (Json.NET) instead of the built-in serializer.
Just write a custom JsonResult that uses Newtonsoft serializer:
Something along the lines:
public abstract class BaseController : Controller
{
protected override JsonResult Json(object data, string contentType, System.Text.Encoding contentEncoding)
{
return new JsonNetResult
{
ContentType = contentType,
ContentEncoding = contentEncoding,
Data = data
};
}
protected override JsonResult Json(object data, string contentType, System.Text.Encoding contentEncoding, JsonRequestBehavior behavior)
{
return new JsonNetResult
{
ContentType = contentType,
ContentEncoding = contentEncoding,
Data = data,
JsonRequestBehavior = behavior
};
}
}
JsonNetResult.cs:
using System;
using System.Web;
using System.Web.Mvc;
using Newtonsoft.Json;
public class JsonNetResult : JsonResult
{
public JsonSerializerSettings SerializerSettings { get; set; }
public Formatting Formatting { get; set; }
public JsonNetResult()
{
Formatting = Formatting.None;
SerializerSettings = new JsonSerializerSettings();
JsonRequestBehavior = JsonRequestBehavior.DenyGet;
}
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
throw new ArgumentNullException("context");
if (JsonRequestBehavior == JsonRequestBehavior.DenyGet
&& String.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException("This request has been blocked because sensitive information could be disclosed to third party web sites when this is used in a GET request. To allow GET requests, set JsonRequestBehavior to AllowGet.");
}
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = !string.IsNullOrEmpty(ContentType)
? ContentType
: "application/json";
if (ContentEncoding != null)
response.ContentEncoding = ContentEncoding;
if (Data != null)
{
var writer = new JsonTextWriter(response.Output) { Formatting = Formatting };
var serializer = JsonSerializer.Create(SerializerSettings);
serializer.Serialize(writer, Data);
writer.Flush();
}
}
}
Credit: https://gist.github.com/jpoehls/1424538
Answer is here: How to force ASP.NET Web API to always return JSON?
Excerpt:
Clear all formatters and add Json formatter back.
GlobalConfiguration.Configuration.Formatters.Clear();
GlobalConfiguration.Configuration.Formatters.Add(new JsonMediaTypeFormatter());
EDIT
I added it to Global.asax inside Application_Start().

ASP.NET MVC - How to "reverse" model binding to convert a C# model back to a query string representation

I have a custom javascript on the client side that I use to build up a querystring and pass over to my asp.net-mvc controller
var templateQueryString = BuildTemplate();
$.ajax({
url: '/MyController/Save?' + templateQueryString,
type: 'post',
dataType: 'json',
success: function (data) {
}
}
and on my controller all of the properties leverage the model binding so it comes in as a single object on the server side. NOTE: that this is a pretty complex object with arrays and arrays of sub objects:
public ActionResult Save(MyTemplate template)
{
}
the issue now is that I need to be able to convert from my C# object back to a string that represents "myTemplateQueryString" on the client side.
Is there any recommended way to take an object and do the "reverse" model binding. They key here is that it generates a string that I could use as a query string again in the future to pass into another asp.ent-mvc controller action.
Here is an example of the querystring that I am storing locally:
<input type="hidden" value="showIds=false&showRisks=false&
amp;statusIds=2&statusIds=1&statusIds=6&statusIds=8&
amp;statusIds=3&statusIds=9&showCompleted=0"
name="filterQueryString" id="filterQueryString">
As #haim770 said it would be easier if you used JSON in the request payload, and not the query string to pass your complex object to the server.
Regarding creating the query string from a model there is not a built-in method that does something like that or any recommended approach as far as i know. An obvious solution is to use reflection and build the query string from your properties.
Assuming your BuildTemplate class looks something like:
public class BuildTemplate
{
public bool ShowIds { get; set; }
public bool ShowRisks { get; set; }
public bool ShowCompleted { get; set; }
public int[] StatusIds { get; set; }
}
You can develop an extension method to convert any object to a QueryString. Here is some initial code you can start with:
public static class ObjectExtensions
{
public static string ToQueryString(this Object obj)
{
var keyPairs = obj.GetType().GetProperties().Select(p =>
new KeyValuePair<string, object>(p.Name.ToLower(), p.GetValue(obj, null)));
var arr = new List<string>();
foreach (var item in keyPairs)
{
if (item.Value is IEnumerable && !(item.Value is String))
{
foreach (var arrayItem in (item.Value as IEnumerable))
{
arr.Add(String.Format("{0}={1}", item.Key, arrayItem.ToString().ToLower()));
}
}
else
arr.Add(String.Format("{0}={1}", item.Key, item.Value.ToString().ToLower()));
}
return "?" + String.Join("&", arr);
}
}
Then you can easily invoke this code on any object to generate a query string:
var person = new BuildTemplate() { StatusIds = new []{ 1, 5, 8, 9 }, ShowRisks = true };
var queryString = person.ToQueryString();
This would generate a query string like:
"?showids=false&showrisks=true&showcompleted=false&statusids=1&statusids=5&statusids=8&statusids=9"
This query string should work just fine with the default model binder for the BuildTemplate class.

Categories

Resources