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);
Related
I have an issue while deserializing json data which can have both float or array type of data. The same issue from here
Dealing with JSON field that holds different types in C#
But everywhere the solution is to use json.net with a JsonConverter. I need to achieve the deserialization using only System.Web.Script.Serialization.JavaScriptSerializer in c#. Can anyone help, pls?
You can use a JavaScriptConverter for this purpose. However, unlike Json.NET's JsonConverter a JavaScriptConverter can only be used for types that map from and to a JSON object -- not an array or primitive type. Thus you will need to create a custom converter for any object that may contain a polymorphic property that could be an array or singleton item.
Let's imagine you have JSON that looks like the following:
{
"name": "my name",
"data": {
"foo": "Foo",
"bar": "Bar"
},
"values": [
3.14,
2.718
]
}
Where "values" might sometimes be a primitive value like so:
"values": 3.14
And, you want to map this to the following POCO:
public class RootObject
{
public string name { get; set; }
public NestedData data { get; set; }
public float[] Values { get; set; }
}
public class NestedData
{
public string foo { get; set; }
public string bar { get; set; }
}
As JavaScriptConverter.Deserialize() is passed an IDictionary<string, object> of parsed values, the steps to take are:
Detach any properties that need custom processing (keeping in mind that JavaScriptSerializer is case-insensitive but that the dictionary is not).
Generate a default deserialization for any remaining properties using JavaScriptSerializer.ConvertToType<T>() using a fresh serializer that does not contain the converter.
Manually deserialize and populate the custom properties into the partially deserialized object, and return it.
For the type shown above, the following converter, based somewhat on this answer, does the job:
class RootObjectConverter : CustomPropertiesConverter<RootObject>
{
const string ValuesName = "values";
protected override IEnumerable<string> CustomProperties
{
get { return new[] { ValuesName }; }
}
protected override void DeserializeCustomProperties(Dictionary<string, object> customDictionary, RootObject obj, JavaScriptSerializer serializer)
{
object itemCost;
if (customDictionary.TryGetValue(ValuesName, out itemCost) && itemCost != null)
obj.Values = serializer.FromSingleOrArray<float>(itemCost).ToArray();
}
protected override void SerializeCustomProperties(RootObject obj, Dictionary<string, object> dict, JavaScriptSerializer serializer)
{
obj.Values.ToSingleOrArray(dict, ValuesName);
}
}
public abstract class CustomPropertiesConverter<T> : JavaScriptConverter
{
protected abstract IEnumerable<string> CustomProperties { get; }
protected abstract void DeserializeCustomProperties(Dictionary<string, object> customDictionary, T obj, JavaScriptSerializer serializer);
protected abstract void SerializeCustomProperties(T obj, Dictionary<string, object> dict, JavaScriptSerializer serializer);
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
// Detach custom properties
var customDictionary = new Dictionary<string, object>();
foreach (var key in CustomProperties)
{
object value;
if (dictionary.TryRemoveInvariant(key, out value))
customDictionary.Add(key, value);
}
// Deserialize and populate all members other than "values"
var obj = new JavaScriptSerializer().ConvertToType<T>(dictionary);
// Populate custom properties
DeserializeCustomProperties(customDictionary, obj, serializer);
return obj;
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
// Generate a default serialization. Is there an easier way to do this?
var defaultSerializer = new JavaScriptSerializer();
var dict = defaultSerializer.Deserialize<Dictionary<string, object>>(defaultSerializer.Serialize(obj));
// Remove default serializations of custom properties, if present
foreach (var key in CustomProperties)
{
dict.RemoveInvariant(key);
}
// Add custom properties
SerializeCustomProperties((T)obj, dict, serializer);
return dict;
}
public override IEnumerable<Type> SupportedTypes
{
get { return new[] { typeof(T) }; }
}
}
public static class JavaScriptSerializerObjectExtensions
{
public static void ReplaceInvariant<T>(this IDictionary<string, T> dictionary, string key, T value)
{
RemoveInvariant(dictionary, key);
dictionary.Add(key, value);
}
public static bool TryRemoveInvariant<T>(this IDictionary<string, T> dictionary, string key, out T value)
{
if (dictionary == null)
throw new ArgumentNullException();
var keys = dictionary.Keys.Where(k => string.Equals(k, key, StringComparison.OrdinalIgnoreCase)).ToArray();
if (keys.Length == 0)
{
value = default(T);
return false;
}
else if (keys.Length == 1)
{
value = dictionary[keys[0]];
dictionary.Remove(keys[0]);
return true;
}
else
{
throw new ArgumentException(string.Format("Duplicate keys found: {0}", String.Join(",", keys)));
}
}
public static void RemoveInvariant<T>(this IDictionary<string, T> dictionary, string key)
{
if (dictionary == null)
throw new ArgumentNullException();
foreach (var actualKey in dictionary.Keys.Where(k => string.Equals(k, key, StringComparison.OrdinalIgnoreCase)).ToArray())
dictionary.Remove(actualKey);
}
public static void ToSingleOrArray<T>(this ICollection<T> list, IDictionary<string, object> dictionary, string key)
{
if (dictionary == null)
throw new ArgumentNullException();
if (list == null || list.Count == 0)
dictionary.RemoveInvariant(key);
else if (list.Count == 1)
dictionary.ReplaceInvariant(key, list.First());
else
dictionary.ReplaceInvariant(key, list.ToArray());
}
public static List<T> FromSingleOrArray<T>(this JavaScriptSerializer serializer, object value)
{
if (value == null)
return null;
if (value.IsJsonArray())
{
return value.AsJsonArray().Select(i => serializer.ConvertToType<T>(i)).ToList();
}
else
{
return new List<T> { serializer.ConvertToType<T>(value) };
}
}
public static bool IsJsonArray(this object obj)
{
if (obj is string || obj is IDictionary)
return false;
return obj is IEnumerable;
}
public static IEnumerable<object> AsJsonArray(this object obj)
{
return (obj as IEnumerable).Cast<object>();
}
}
Then use it like:
var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new[] { new RootObjectConverter() });
var root = serializer.Deserialize<RootObject>(json);
I'm attempting to implement IDeserializationCallback using JSON.NET. I'm deserializing an object, and I would like to generate a list of all the objects which were deserialized which implement IDeserializationCallback, what would be the best way to do this? Does JSON.NET have any appropriate extension point to facilitate this? I have a (seemingly) working solution below, however it is quite ugly, so I'm convinced there must be a better way to do this. Any help is appreciated, thanks!
private static JsonSerializer serializer = new JsonSerializer();
static cctor()
{
serializer.Converters.Add(new DeserializationCallbackConverter());
}
public static T Deserialize<T>(byte[] data)
{
using (var reader = new JsonTextReader(new StreamReader(new MemoryStream(data))))
using (DeserializationCallbackConverter.NewDeserializationCallbackBlock(reader))
return serializer.Deserialize<T>(reader);
}
private class DeserializationCallbackConverter : JsonConverter
{
[ThreadStatic]
private static ScopedConverter currentConverter;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return currentConverter.ReadJson(reader, objectType, serializer);
}
public override bool CanConvert(Type objectType)
{
return currentConverter == null ? false : currentConverter.CanConvert();
}
public override bool CanWrite
{
get { return false; }
}
public static IDisposable NewDeserializationCallbackBlock(JsonReader reader)
{
return new ScopedConverter(reader);
}
private class ScopedConverter : IDisposable
{
private JsonReader jsonReader;
private string currentPath;
private List<IDeserializationCallback> callbackObjects;
public ScopedConverter(JsonReader reader)
{
jsonReader = reader;
callbackObjects = new List<IDeserializationCallback>();
currentConverter = this;
}
public object ReadJson(JsonReader reader, Type objectType, JsonSerializer serializer)
{
var lastPath = currentPath;
currentPath = reader.Path;
var obj = serializer.Deserialize(reader, objectType);
currentPath = lastPath;
var dc = obj as IDeserializationCallback;
if (dc != null && callbackObjects != null)
callbackObjects.Add(dc);
return obj;
}
public bool CanConvert()
{
return jsonReader.Path != currentPath;
}
public void Dispose()
{
currentConverter = null;
foreach (var obj in callbackObjects)
obj.OnDeserialization(null);
}
}
}
You can create a custom contract resolver that adds an extra, artificial OnDeserialized callback that tracks creation of reference type objects. Here's one example:
public interface IObjectCreationTracker
{
void Add(object obj);
ICollection<object> CreatedObjects { get; }
}
public class ReferenceObjectCreationTracker : IObjectCreationTracker
{
public ReferenceObjectCreationTracker()
{
this.CreatedObjects = new HashSet<object>();
}
public void Add(object obj)
{
if (obj == null)
return;
var type = obj.GetType();
if (type.IsValueType || type == typeof(string))
return;
CreatedObjects.Add(obj);
}
public ICollection<object> CreatedObjects { get; private set; }
}
public class ObjectCreationTrackerContractResolver : DefaultContractResolver
{
readonly SerializationCallback callback = (o, context) =>
{
var tracker = context.Context as IObjectCreationTracker;
if (tracker != null)
tracker.Add(o);
};
protected override JsonContract CreateContract(Type objectType)
{
var contract = base.CreateContract(objectType);
contract.OnDeserializedCallbacks.Add(callback);
return contract;
}
}
And then use it as follows:
public static class JsonExtensions
{
public static T DeserializeWithTracking<T>(string json, out ICollection<object> objects)
{
var tracker = new ReferenceObjectCreationTracker();
var settings = new JsonSerializerSettings
{
ContractResolver = new ObjectCreationTrackerContractResolver(),
Context = new StreamingContext(StreamingContextStates.All, tracker),
// Add other settings as required.
TypeNameHandling = TypeNameHandling.Auto,
};
var obj = (T)JsonConvert.DeserializeObject<T>(json, settings);
objects = tracker.CreatedObjects;
return obj;
}
}
Note that this only returns instances of non-string reference types. Returning instances of value types is more problematic as there is no obvious way to distinguish between a value type that eventually gets embedded into a larger object via a property setter and one that is retained in the object graph as a boxed reference, e.g. as shown in this question. If the boxed value type eventually gets embedded in some larger object there is no way to retain a direct reference to it.
Also note the use of StreamingContext.Context to pass the tracker down into the callback.
You may want to cache the contract resolver for best performance.
Update
In answer to the updated question of how to implement IDeserializationCallback with Json.NET, the above should work for reference types. For value types that implement this interface, you could:
Call the method immediately in the OnDeserialized callback rather than deferring it until serialization is complete, or
Throw an exception indicating that IDeserializationCallback is not supported for structs.
I have a class that is used to represent an identity, it contains a value of string.
I then have another class that is used to represent an event, this includes an identity object, and a string name.
I can serialize from object to JSON string fine, I get the string:
{"Id":{"Value":"2e4146c2-66c9-4637-8936-29ccfc5df638"},"Name":"Jon Doe"}
However when I go from the above JSON to object, in the MyIdentity constructor the string identityValue is passed in as "00000000-0000-0000-0000-000000000000".
I have also tried providing two constructors for MyIdentity, one for string, and one for Guid, but same result. The idea behind this is to store the data as a string, but to remember we can be cast to a Guid if required (since identities will either be formed by a string or by a Guid).
public class MyEntityId : MyIdentity
{
public MyEntityId(Guid identityValue)
: base(identityValue)
{
}
}
public abstract class MyIdentity
{
protected readonly bool convertableAsGuid;
protected readonly string value;
public string Value { get { return this.value; } }
public MyIdentity(string identityValue)
{
this.value = identityValue;
Guid guid;
if(Guid.TryParse(identityValue, out guid)==false)
this.convertableAsGuid = false;
}
public MyIdentity(Guid identityValue)
{
this.value = identityValue.ToString();
this.convertableAsGuid = true;
}
}
static void Main(string[] args)
{
var evnt = new MyEvent(new MyEntityId(Guid.NewGuid()), "Jon Doe");
var eventHeaders = new Dictionary<string, object>
{
{"EventClrTypeName", evnt.GetType().AssemblyQualifiedName}
};
var serializerSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.None };
var metadata = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(eventHeaders, serializerSettings));
var data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(evnt, serializerSettings));
string dataAsString = Encoding.UTF8.GetString(data);
var eventClrTypeName = JObject.Parse(Encoding.UTF8.GetString(metadata)).Property("EventClrTypeName").Value;
var obj = JsonConvert.DeserializeObject(dataAsString, Type.GetType((string)eventClrTypeName));
// stepping through the above, a zero initialised GUID string is passed in to MyIdentity constructor
}
As #hvd has kindly commented, it seems to be the Value only has a get property, if i add a set property also then it works (so I believe the constructor string value is not actually being used). The reason I don't want to put a setter on the identity is for programming design, it is an identity and should not be changed once created.
I can live with a public get and protected set on the property, I have tried the keyword [JsonProperty] and it works... however I don't want to decorate my domain objects with those attributes - is there another way?
If I understood correctly you have a problem initializing the Guid on deserialization and you don't want to create a setter or use attributes for successful deserialization. Please note that I changed your MyIdentity class by removing the constructor that accepted Guid parameter since it's not necessary, changed the parsing of Guid logic since it would have never initialize the convertableAsGuid property correctly and created MyEvent class since you didn't post it on your question. Also I created the MyCustomConverter class which is used during the deserialization. Here are the classes:
public class MyCustomConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof (MyEvent);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jObject = JObject.Load(reader);
existingValue = new MyEvent(new MyEntityId(jObject["Id"]["Value"].ToString()), jObject["Name"].ToString());
return existingValue;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
public class MyEvent
{
public MyEntityId Id { get; set; }
public string Name { get; set; }
public MyEvent(MyEntityId id, string name)
{
Id = id;
Name = name;
}
}
public class MyEntityId : MyIdentity
{
public MyEntityId(string identityValue)
: base(identityValue)
{
}
}
public abstract class MyIdentity
{
protected readonly bool convertableAsGuid;
protected readonly string value;
public string Value { get { return this.value; } }
public MyIdentity(string identityValue)
{
this.value = identityValue;
Guid guid;
if (Guid.TryParse(identityValue, out guid))
this.convertableAsGuid = true;
}
}
And here is the serialization and deserialization logic:
static void Main(string[] args)
{
var evnt = new MyEvent(new MyEntityId(Guid.NewGuid().ToString()), "Jon Doe");
var eventHeaders = new Dictionary<string, object>
{
{"EventClrTypeName", evnt.GetType().AssemblyQualifiedName}
};
var serializerSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.None };
var metadata = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(eventHeaders, serializerSettings));
var data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(evnt, serializerSettings));
string dataAsString = Encoding.UTF8.GetString(data);
var eventClrTypeName = JObject.Parse(Encoding.UTF8.GetString(metadata)).Property("EventClrTypeName").Value;
var obj = JsonConvert.DeserializeObject<MyEvent>(dataAsString, new JsonConverter[] {new MyCustomConverter()});
}
Demo: https://dotnetfiddle.net/asRtEI
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.
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.