Using:JavascriptConverter in ASP.NET - c#

I'm currently serializing objects like this:
public static string ObjToJson(List<MyObject> TheObjects){
JavascriptSerializer TheSerializer = new JavascriptSerializer();
TheSerializer.RegisterConverters( new JavascriptConverter [] { new ObjectToJson() });
string JsonObj = TheSerializer.Serialize(TheObjects);
return JsonObj; }
And then I have this:
public class TheObjToJson : JavascriptConverter{
public override IDictionary<string,object> Serialize (object obj, JavascriptSerializer serializer)
{
...
return jsonstring;
}
}
Is this the fastest/best way to do JSON in ASP.NET?

I think the most popular JSON library is json.net. It's fast and easy to use.

Related

Serialize and Deserialize AjaxFileUploadEventArgs - No parameterless constructor defined'

I'm changing a webform website to use StateServer and now I'm trying to find a way to serialize and deserialize AjaxFileUploadEventArgs, my code so far:
In the html I have:
<ajaxToolkit:AjaxFileUpload
ID="AAA"
runat="server"
OnUploadComplete="OnUploadComplete"
ViewStateMode="Enabled" />
Server:
protected void OnUploadComplete(object sender, AjaxFileUploadEventArgs file)
{
UpdateListInSession(file);
}
public static void UpdateListInSession(AjaxFileUploadEventArgs file)
{
var serializer = new JavaScriptSerializer();
var fileSerialized = serializer.Serialize(file);
}
public static AjaxFileUploadEventArgs GetLeadsListFromSession()
{
var serializer = new JavaScriptSerializer();
AjaxFileUploadEventArgs file = null;
AjaxFileUploadEventArgs deserializeFile =
serializer.Deserialize<AjaxFileUploadEventArgs>(
HttpContext.Current.Session[k_file] as string);
return deserializeFile;
}
The error:
System.MissingMethodException: 'No parameterless constructor defined for type of 'AjaxControlToolkit.AjaxFileUploadEventArgs'.'
Assuming you are using AjaxFileUploadEventArgs.cs from ajaxcontroltoolkit, the exception message is self-explanatory. The serializer you are using, JavaScriptSerializer, can only construct and deserialize a type with a parameterless constructor, but as shown in its reference source, AjaxFileUploadEventArgs only has a single constructor, which is parameterized:
public AjaxFileUploadEventArgs(string fileId, AjaxFileUploadState state, string statusMessage, string fileName, int fileSize, string contentType) {
// Initialize fields
}
So, what are your options to deserialize this type? Firstly, you could switch to json.net which supports parameterized constructors out of the box. Once Json.NET is installed, if you do:
var deserializeFile =
Newtonsoft.Json.JsonConvert.DeserializeObject<AjaxFileUploadEventArgs>(jsonString);
Then it simply works. Sample fiddle. Note that Microsoft's own documentation for JavaScriptSerializer states:
Json.NET should be used serialization and deserialization.
So this is likely the best solution.
If you cannot use Json.NET for whatever reason, you will need to write a custom JavaScriptConverter for AjaxFileUploadEventArgs such as the following:
public class AjaxFileUploadEventArgsConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
var args = new AjaxFileUploadEventArgs
(
serializer.ConvertItemToTypeOrDefault<string>(dictionary, "FileId"),
serializer.ConvertItemToTypeOrDefault<AjaxFileUploadState>(dictionary, "State"),
serializer.ConvertItemToTypeOrDefault<string>(dictionary, "StatusMessage"),
serializer.ConvertItemToTypeOrDefault<string>(dictionary, "FileName"),
serializer.ConvertItemToTypeOrDefault<int>(dictionary, "FileSize"),
serializer.ConvertItemToTypeOrDefault<string>(dictionary, "ContentType")
)
{ PostedUrl = serializer.ConvertItemToTypeOrDefault<string>(dictionary, "PostedUrl") };
return args;
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}
public override IEnumerable<Type> SupportedTypes
{
get { return new[] { typeof(AjaxFileUploadEventArgs) }; }
}
}
public static class JavaScriptSerializerExtensions
{
public static T ConvertItemToTypeOrDefault<T>(this JavaScriptSerializer serializer, IDictionary<string, object> dictionary, string key)
{
object value;
if (!dictionary.TryGetValue(key, out value))
return default(T);
return serializer.ConvertToType<T>(value);
}
}
Then deserialize as follows:
var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new JavaScriptConverter[] { new AjaxFileUploadEventArgsConverter() });
var deserializeFile = serializer.Deserialize<AjaxFileUploadEventArgs>(jsonString);

Deserialize an Anonymous Type From a Collection

I have my serialized JSON in this format:
string json = #"[{"Name": "std_id","Value": "111"}, {"Name": "cust_id","Value": "444"}]"
How do I deserialize it to a single anonymous object like this:
var paramObj = new {"std_id" = 111, "cust_id" = 444}
Since you said the values of the Name and Value properties in your JSON objects can vary, you will not be able to deserialize to an anonymous object. Anonymous types are defined at compile-time, which means you need to know the property names ahead of time to be able to define them. The only way to get around that is code generation, which I think is going to be overkill for this situation. Instead, I would suggest you deserialize into a JObject with a dynamic variable. This will get you pretty close to what you want. Here's how:
string json = #"[
{ ""Name"": ""std_id"", ""Value"": ""111"" },
{ ""Name"": ""cust_id"", ""Value"": ""444"" }
]";
dynamic obj = new JObject(JArray.Parse(json)
.Select(t => new JProperty((string)t["Name"], t["Value"])));
From there, you can access the properties like you would for an anonymous type (assuming you know what they are):
Console.WriteLine(obj.std_id);
Console.WriteLine(obj.cust_id);
If you don't know what the properties are, you can enumerate them like a dictionary:
foreach (var prop in obj)
{
Console.WriteLine(prop.Name + ": " + prop.Value);
}
Fiddle: https://dotnetfiddle.net/MRY2ny
Why anonymous object? you should deserialize to a type like below
public class RootObject
{
public string Name { get; set; }
public string Value { get; set; }
}
Then what you actually have is IEnumerable<RootObjct>. You can use use Linq and select First() from it like
RootObject = RootObjects.FirstOrDefault()
You could deserialize it into a dynamic. Like this:
var serializer = new JavaScriptSerializer();
var deserializedResult = serializer.Deserialize<dynamic>(json);
Reference:
JavaScriptSerializer Class
var definition = new { Name = "" };
string json1 = #"{'Name':'James'}";
var customer1 = JsonConvert.DeserializeAnonymousType(json1, definition);
Console.WriteLine(customer1.Name);
string json2 = #"{'Name':'Mike'}";
var customer2 = JsonConvert.DeserializeAnonymousType(json2, definition);
Console.WriteLine(customer2.Name);
source http://www.newtonsoft.com/json/help/html/DeserializeAnonymousType.htm
I know that this solution isn't perfect, but it works for the example provided by you and returns result that looks like paramObj in your example.
The idea is to create a custom Json converter.
First, let's create a DTO class to present a name-value item of incomming JSON.
public class NameValueJsonItem
{
public string Name { get; set; }
public string Value { get; set; }
}
Converter implementation:
public class DynamicJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
if (token == null || token.Type != JTokenType.Array)
{
return null;
}
List<NameValueJsonItem> parsedJson = token.ToObject<List<NameValueJsonItem>>();
ExpandoObject result = new ExpandoObject();
foreach (NameValueJsonItem item in parsedJson)
{
if (!String.IsNullOrEmpty(item.Name))
{
(result as IDictionary<string, object>)[item.Name] = item.Value;
}
}
return result;
}
public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Sure, you can make it more safe by adding some exceptions handling etc inside the method if you want.
You can use this converter like that:
dynamic result = JsonConvert.DeserializeObject<dynamic>(json, new DynamicJsonConverter());
Hope it will help.

How to Deserialize JSON with JavaScriptSerializer to Tuples

I'm looking way to Deserialize JSON string to c# List<Tuple<string, string>>.
"[{\"name\":\"OkeyTablePaired\",\"value\":\"true\"},
{\"name\":\"OkeyTableIndicator\",\"value\":\"true\"},
{\"name\":\"OkeyTableHued\",\"value\":\"true\"},
{\"name\":\"OkeyTableSpectatorQuiet\",\"value\":\"true\"},
{\"name\":\"OkeyTableEveryoneQuiet\",\"value\":\"true\"}]"
Tuple List:
List<Tuple<string, string>> tupleJson = new List<Tuple<string, string>>();
I would like to put them together as
[OkeyTablePaired]:[true]
[OkeyTableIndicator]:[false]
[OkeyTableHued]:[true]
[OkeyTableSpectatorQuiet]:[true]
[OkeyTableEveryoneQuiet]:[true]
in the List Tuple...
Any help would be fantastic.
Thanks.
This should work. Note that you need to convert the input to valid json array by adding brackets [] first. You will need to get JSON.NET to make this work.
//using System;
//using System.Collections.Generic;
//using System.Linq;
//using Newtonsoft.Json.Linq;
string validJson = "[" + json + "]";
JArray jsonArray = JArray.Parse(validJson);
List<Tuple<string, string>> tupleJson = jsonArray
.Select(p => new Tuple<string, string>((string)p["name"], (string)p["value"]))
.ToList();
More info in the documentation.
Assuming you get a valid JSON array, a custom converter with JSON.NET would work as well:
public class TupleConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanWrite
{
get { return false; }
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(List<Tuple<string, string>>);
}
public override object ReadJson(
JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
List<Tuple<string, string>> result = null;
if (reader.TokenType == JsonToken.StartArray)
{
JArray deserialized = JArray.Load(reader);
result = new List<Tuple<string, string>>(deserialized.Count);
foreach (var token in deserialized)
{
if (token.Type == JTokenType.Object)
{
result.Add(Tuple.Create(
token["name"].ToObject<string>(),
token["value"].ToObject<string>()));
}
}
}
return result;
}
}
Usage:
List<Tuple<string, string>> result =
JsonConvert.DeserializeObject<List<Tuple<string, string>>>(json, new TupleConverter());
Example: https://dotnetfiddle.net/TEbNsH
If you've created a data contract for Tuple that the JSON can interpret, you can use DataContractJsonSerializer (from the System.Runtime.Serialization.Json library):
var request = WebRequest.Create(requestUrl) as HttpWebRequest;
request.Method = "GET";
var jsonSerializer = new DataContractJsonSerializer(typeof (Tuple));
var objResponse = (Tuple) jsonSerializer.ReadObject(response.GetResponseStream());
The data contract, in your case, would probably be pretty straightforward, something like this:
[DataContract]
public class Tuple
{
[DataMember]
public string OkeyTablePaired {get; set;}
[DataMember]
public string OkeyTableIndicator {get; set;}
.....etc.
}

JSON not binding correctly in WebApi action

I'm creating and api using asp.net webapi
For some reasons this json:
JSON => {"page":"1","count":"10","sorting[name]":"asc"}
JSONString = >"{\"page\":\"1\",\"count\":\"10\",\"sorting[name]\":\"asc\"}"
Does not get bindend correctly to such a model:
public class DataSourceRequestParams
{
public int Page { get; set; }
public int Count { get; set; }
public IDictionary<string, string> Sorting { get; set; }
public IDictionary<string, string> Filter { get; set; }
public IDictionary<string, string> Order { get; set; }
}
The Sorting property does not get binded.
[HttpPost]
public PagingResult<ApplicationUser> Get([FromBody] DataSourceRequestParams #params)
{...}
If I create an action in an MVC application and pass the same JSON it gets binded.
Am I missing something here?
The binding in Web API expects a Dictionary as a JSON object when the Content-Type is application/json so the following JSON will bind correctly with your model:
{"page":"1","count":"10","sorting":{"name":"asc"}}
Edit
Give that you can't change the JSON being passed in you will need to manually bind the JSON to your object. There are a couple of options here. You could write your own IModelBinder or you could override the behaviour of the JSON binder by writing your own JsonConverter.
As a rough example, you could create a JsonConverter like this:
public class DictionaryConverter : JsonConverter
{
public DictionaryConverter()
{
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
//I've not implemented writing the Json
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
//create a new object
var temp = new DataSourceRequestParams();
temp.Sorting = new Dictionary<string, string>();
temp.Filter = new Dictionary<string, string>();
temp.Order = new Dictionary<string, string>();
//load the input into a JObject and grab the "simple" values
JObject jsonObject = JObject.Load(reader);
temp.Page = jsonObject["page"].Value<int>();
temp.Count = jsonObject["count"].Value<int>();
//parse the dictionary values
AddValuesToDictionary(temp, jsonObject, "sorting", temp.Sorting);
AddValuesToDictionary(temp, jsonObject, "filter", temp.Filter);
AddValuesToDictionary(temp, jsonObject, "order", temp.Order);
return temp;
}
private void AddValuesToDictionary(DataSourceRequestParams test, JObject jsonObject, string name, IDictionary<string, string> dictionary)
{
//grab each matching property
var properties = jsonObject.Properties().Where(j => j.Name.StartsWith(name));
if (properties != null)
{
foreach (var property in properties)
{
/*for each matched property grab the value between the brackets
* from the name and the property value
* and the associate json value and add it to the dictionary
*/
dictionary.Add(Regex.Match(property.Name, #"\[([^\]]*)\]").Groups[1].Value, property.Value.Value<string>());
}
}
}
public override bool CanConvert(Type objectType)
{
//we can convert if the type is DataSourceRequestParams
return typeof(DataSourceRequestParams).IsAssignableFrom(objectType);
}
}
Then register the new converter in the GlobalConfiguration:
GlobalConfiguration.Configuration.Formatters.JsonFormatter
.SerializerSettings.Converters.Add(new DictionaryConverter());
Then your original JSON will parse correctly. Obviously that code is devoid of all error handling etc so it's not production ready.

Why does JavaScriptSerializer ignore my converter?

I'm trying to read a JSON object which contains the date/time in a format that cannot be directly parsed by .NET's DateTime structure. In order to avoid having an 'int' field in my structure for the date/time, I wrote a custom DateTimeConverter:
public class DateTimeConverter : JavaScriptConverter {
public override IEnumerable<Type> SupportedTypes {
get { return new Type[] { typeof(DateTime), typeof(DateTime?) }; }
}
public override IDictionary<string, object> Serialize(
object obj, JavaScriptSerializer serializer
) { throw new NotImplementedException(); }
public override object Deserialize(
IDictionary<string, object> dictionary, Type type,
JavaScriptSerializer serializer
) {
return DateTime.Now;
}
}
But when I read a JSON string with the JavaScriptSerializer, it does not use my custom converter:
public struct TextAndDate {
public string Text;
public DateTime Date;
}
static void Main() {
string json =
"{" +
" \"text\": \"hello\", " +
" \"date\": \"1276692024\"" +
"}";
var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new [] { new DateTimeConverter() });
var test = serializer.Deserialize<TextAndDate>(json);
}
The converter is used when I directly deserialize a DateTime value, just not when I deserialize a type containing a DateTime value.
Why?
Any way around this without writing a custom DateTime type or using int?
You should make small changes in your DateTimeConverter class:
public class DateTimeConverter : JavaScriptConverter {
public override IEnumerable<Type> SupportedTypes {
get { return new Type[] { typeof (TextAndDate) }; }
}
public override IDictionary<string, object> Serialize (
object obj, JavaScriptSerializer serializer
) { throw new NotImplementedException (); }
public override object Deserialize (
IDictionary<string, object> dictionary, Type type,
JavaScriptSerializer serializer
) {
if (type == typeof (TextAndDate)) {
TextAndDate td = new TextAndDate ();
if (dictionary.ContainsKey ("text"))
td.Text = serializer.ConvertToType<string> (
dictionary["text"]);
//if (dictionary.ContainsKey ("date"))
td.Date = DateTime.Now;
return td;
}
else
return null;
}
}
UPDATED based on comment: It seems to me that you should use Message Inspectors technique (see http://msdn.microsoft.com/en-us/library/aa717047.aspx). Look at How to ignore timezone of DateTime in .NET WCF client? for an example.

Categories

Resources