I have the following code:
///<summary>
///In this case you can set any other valid attribute for the editable element.
///For example, if the element is edittype:'text', we can set size, maxlength,
///etc. attributes. Refer to the valid attributes for the element
///</summary>
public object OtherOptions { get; set; }
public override string ToString()
{
return this.ToJSON();
}
I need to get the anonymous object from the OtherOptions property and serialize each property of the anonymous object as it were from the main object.
E.g.:
OtherOptions = new { A = "1", B = "2" }
If I serialize it, it will be (or something like this):
OtherOptions: {
A: "1",
B: "2"
}
Is it possible to have A and B at the same level of OtherOptions without explicitly removing it.
Ok this is just ugly and I don't recommend doing it but it does what you want...
Essentially, it creates a Dictionary of just the properties you want and then serializes that dictionary.
static void Main(string[] args)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
var obj = new {Prop1 = "val1", OtherOptions = new {A = "1", B = "2"}};
IDictionary<string, object> result = new Dictionary<string, object>();
foreach (var kv in GetProps(obj))
{
if (!kv.Key.Equals("OtherOptions"))
result.Add(kv);
}
foreach (var kv in GetProps(obj.OtherOptions))
{
result.Add(kv);
}
var serialized = serializer.Serialize(result);
}
static IEnumerable<KeyValuePair<string, object>> GetProps(object obj)
{
var props = TypeDescriptor.GetProperties(obj);
return
props.Cast<PropertyDescriptor>()
.Select(prop => new KeyValuePair<string, object>(prop.Name, prop.GetValue(obj)));
}
serialized becomes
{"Prop1":"val1","A":"1","B":"2"}
You could use an attribute on the field you want to ignore and then check for that attribute in the GetProps method and not return if exists.
Again, I do not recommend doing this.
Related
For the project at hand I have to use DataContractJsonSerializer for serialization and have to generate a specific output based on the member's values.
My class looks similar to this:
public class MyClass
{
public string foo;
public string bar;
public MyClass(string f, string b = "")
{
this.foo = f;
this.bar = b;
}
}
Now serialization of a list like
var list = new List<MyClass>
{
new MyClass("foo", "bar"),
new MyClass("foo1"),
new MyClass("foo2", "bar2")
};
should look like this
[{"foo": "bar"}, "foo1", {"foo2": "bar2"}]
or - better yet - escaped and as a string:
"[{\"foo\": \"bar\"}, \"foo1\", {\"foo2\": \"bar2\"}]"
A mixture of strings and objects. How could I achieve this?
I tried to override the ToString() method and serializing the corresponding strings resulting in unnecessarily escaped symbols, e.g. bar2 could be m/s and was escaped as m\\/s which could not be deserialized correctly on the web server.
Finally, I just need to serialize to this format. There is no need to deserialize this format with DataContractJsonSerializer.
What you would like to do is to conditionally replace instances of MyClass with a serialization surrogate that is a string or a dictionary, however using a primitive as a surrogate is not supported by data contract serialization, as explained here by Microsoft.
However, since you only need to serialize and not deserialize, you can get the output you need by manually replacing your List<MyClass> with a surrogate List<object> in which instances of MyClass are replaced with a string when bar is empty, and a Dictionary<string, string> otherwise. Then manually construct a DataContractJsonSerializer with the following values in DataContractJsonSerializerSettings:
Set KnownTypes to be a list of the actual types in the surrogate object list.
Set EmitTypeInformation to EmitTypeInformation.Never. This suppresses inclusion of type hints in the output JSON.
Set UseSimpleDictionaryFormat to true.
(Note that DataContractJsonSerializerSettings, EmitTypeInformation and UseSimpleDictionaryFormat are all new to .NET 4.5.)
Thus you could define your MyType as follows:
public interface IHasSerializationSurrogate
{
object ToSerializationSurrogate();
}
public class MyClass : IHasSerializationSurrogate
{
public string foo;
public string bar;
// If you're not going to mark MyClass with data contract attributes, DataContractJsonSerializer
// requires a default constructor. It can be private.
MyClass() : this("", "") { }
public MyClass(string f, string b = "")
{
this.foo = f;
this.bar = b;
}
public object ToSerializationSurrogate()
{
if (string.IsNullOrEmpty(bar))
return foo;
return new Dictionary<string, string> { { foo, bar } };
}
}
Then introduce the following extension methods:
public static partial class DataContractJsonSerializerHelper
{
public static string SerializeJsonSurrogateCollection<T>(this IEnumerable<T> collection) where T : IHasSerializationSurrogate
{
if (collection == null)
throw new ArgumentNullException();
var surrogate = collection.Select(i => i == null ? null : i.ToSerializationSurrogate()).ToList();
var settings = new DataContractJsonSerializerSettings
{
EmitTypeInformation = EmitTypeInformation.Never,
KnownTypes = surrogate.Where(s => s != null).Select(s => s.GetType()).Distinct().ToList(),
UseSimpleDictionaryFormat = true,
};
return DataContractJsonSerializerHelper.SerializeJson(surrogate, settings);
}
public static string SerializeJson<T>(this T obj, DataContractJsonSerializerSettings settings)
{
var type = obj == null ? typeof(T) : obj.GetType();
var serializer = new DataContractJsonSerializer(type, settings);
return SerializeJson<T>(obj, serializer);
}
public static string SerializeJson<T>(this T obj, DataContractJsonSerializer serializer = null)
{
serializer = serializer ?? new DataContractJsonSerializer(obj == null ? typeof(T) : obj.GetType());
using (var memory = new MemoryStream())
{
serializer.WriteObject(memory, obj);
memory.Seek(0, SeekOrigin.Begin);
using (var reader = new StreamReader(memory))
{
return reader.ReadToEnd();
}
}
}
}
And serialize your list to JSON manually as follows:
var json = list.SerializeJsonSurrogateCollection();
With the following result:
[{"foo":"bar"},"foo1",null,{"foo2":"bar2"}]
If you really need the string to be escaped (why?) you can always serialize the resulting string to JSON a second time with DataContractJsonSerializer producing a double-serialized result:
var jsonOfJson = json.SerializeJson();
Resulting in
"[{\"foo\":\"bar\"},\"foo1\",{\"foo2\":\"bar2\"}]"
Deserialize will happen based on name and value combination so your text should be like this
[{"foo": "foo","bar": "bar"},{"foo": "foo1"},{"foo": "foo2","bar": "bar2"}]
I have a piece of JSON that looks like this:
[
{
"$id": "1",
"Name": "James",
"BirthDate": "1983-03-08T00:00Z",
"LastModified": "2012-03-21T05:40Z"
},
{
"$ref": "1"
}
]
As you can tell by the $ref, this JSON array contains the same Person (James), twice. The second time is a reference to the first.
I am wondering if there is a way to deserialize this JSON into an object that contains two copies of the James person.
Currently, I'm using this:
var jsonSerializerSettings = new JsonSerializerSettings()
{
PreserveReferencesHandling = PreserveReferencesHandling.None,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
var deserializedPersons = JsonConvert.DeserializeObject<List<Person>>(json, jsonSerializerSettings);
But this just gives me an array with the same instance of the Person, twice:
object.ReferenceEquals(deserializedPersons[0], deserializedPersons[1]) // Evaluates to true
I've found a workaround I am unhappy with which is simply deserializing the JSON string, then serializing it using the jsonSerializerSettings above, which will duplicate the person in the JSON, then deserializing it again. This is causing major slowdowns for the large objects we are using.
Note: I know I could change the API that I retrieve this JSON from to duplicate the data, but preserving the references saves substantial space when sending the response JSON over the wire.
You could use a custom reference resolver. For example, assuming Name is the "primary key" for your objects, this should work. Of course, you may want to make it more generic.
public class PersonReferenceResolver : IReferenceResolver
{
private readonly IDictionary<string, Person> _objects =
new Dictionary<string, Person>();
public object ResolveReference(object context, string reference)
{
Person p;
if (_objects.TryGetValue(reference, out p))
{
//This is the "clever" bit. Instead of returning the found object
//we just return a copy of it.
//May be better to clone your class here...
return new Person
{
Name = p.Name,
BirthDate = p.BirthDate,
LastModified = p.LastModified
};
}
return null;
}
public string GetReference(object context, object value)
{
Person p = (Person)value;
_objects[p.Name] = p;
return p.Name;
}
public bool IsReferenced(object context, object value)
{
Person p = (Person)value;
return _objects.ContainsKey(p.Name);
}
public void AddReference(object context, string reference, object value)
{
_objects[reference] = (Person)value;
}
}
Now you deserialise like this:
var jsonSerializerSettings = new JsonSerializerSettings()
{
ReferenceResolver = new PersonReferenceResolver()
};
var deserializedPersons = JsonConvert.DeserializeObject<List<Person>>(
json, jsonSerializerSettings);
Edit: I was bored so I made a generic version:
public class GenericResolver<TEntity> : IReferenceResolver
where TEntity : ICloneable, new()
{
private readonly IDictionary<string, TEntity> _objects = new Dictionary<string, TEntity>();
private readonly Func<TEntity, string> _keyReader;
public GenericResolver(Func<TEntity, string> keyReader)
{
_keyReader = keyReader;
}
public object ResolveReference(object context, string reference)
{
TEntity o;
if (_objects.TryGetValue(reference, out o))
{
return o.Clone();
}
return null;
}
public string GetReference(object context, object value)
{
var o = (TEntity)value;
var key = _keyReader(o);
_objects[key] = o;
return key;
}
public bool IsReferenced(object context, object value)
{
var o = (TEntity)value;
return _objects.ContainsKey(_keyReader(o));
}
public void AddReference(object context, string reference, object value)
{
if(value is TEntity)
_objects[reference] = (TEntity)value;
}
}
With slightly new usage:
var jsonSerializerSettings = new JsonSerializerSettings()
{
//Now we need to specify the type and how to get the object's key
ReferenceResolver = new GenericResolver<Person>(p => p.Name)
};
var deserializedPersons = JsonConvert.DeserializeObject<List<Person>>(
json, jsonSerializerSettings);
I had problem with $ref deserialization.
PreserveReferencesHandling helps only if you have metadata located at the beginning of the request/response.
Otherwise I recommend using those:
var settings = new JsonSerializerSettings();
settings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
settings.MetadataPropertyHandling = MetadataPropertyHandling.Ignore; //ign
var deserializedObjects = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(requestResult.Content, settings);
Regarding to https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_MetadataPropertyHandling.htm
Default 0 Read metadata properties located at the start of a JSON object.
ReadAhead 1 Read metadata properties located anywhere in a JSON object. Note that this setting will impact performance.
Ignore 2 Do not try to read metadata properties.
Having a class defined:
[DataContract]
public class Command
{
[DataMember(Name = "cmdName")]
public string CommandName { get; set; }
[DataMember(Name = "cmdTransactionId")]
public string CommandTransactionId { get; set; }
}
I would like to create an instance of that class from a dictionary:
Dictionary<string, object> propertyBag = new Dictionary<string, object>();
propertyBag["cmdName"] = "cmd1";
propertyBag["cmdTransactionId"] = "1";
Command command = deserializer.Create<Command>(propertyBag);
DataContractSerializer is not working for me nor is the JavaScriptSerializer.ConvertToType as each of them is missing a piece that stop s me from creating objects in one go.
JavaScriptSerializer will work here with some changes:
var propertyBag = new Dictionary<string, object>();
propertyBag["CommandName"] = "cmd1";
propertyBag["CommandTransactionId"] = "1";
var serializer = new JavaScriptSerializer();
var res = serializer.Serialize(propertyBag);
var command = serializer.Deserialize<Command>(res);
I used Deserialize method instead of ConvertToType one. The second difference is more significant. A dictionary used by me contains keys which are the same as names of properties in Command class. If you don't like it you should try to write a custom JavaScriptConverter. See this question.
Maybe you can try this code for deserializer.Create<Command>(propertyBag):
public T Create<T>(IDictionary<string, object> propertyBag)
{
var result = (T)FormatterServices.GetUninitializedObject(typeof(T));
foreach (var item in from member in typeof(T).GetMembers()
let dataMemberAttr = member.GetCustomAttributes(typeof(DataMemberAttribute), true).Cast<DataMemberAttribute>().SingleOrDefault()
where dataMemberAttr != null && propertyBag.ContainsKey(dataMemberAttr.Name)
select new { Member = member, Value = propertyBag[dataMemberAttr.Name] })
{
var property = item.Member as PropertyInfo;
if (property != null)
{
property.SetValue(result, item.Value, null);
continue;
}
var field = item.Member as FieldInfo;
if (field != null)
{
field.SetValue(result, item.Value);
continue;
}
}
return result;
}
This code is not tested - just typed here.
I am getting an error with a Date in a JSON string: /Date(1370963229000)/ is not a valid value for DateTime., and I can fix this by doing a ToString("g") on the date, but I don't want to have to explicitly put every column in my select statement.
Currently, I am doing:
var people = _context.People.ToList();
I don't want to have to do var people = _context.People.Select({x=>x.Id.....});
Method 1: Use "Proxy" Properties
Put [ScriptIgnore] attributes on your DateTime properties and implement proxy properties that get the date value as a string. The properties with [ScriptIgnore] will be skipped by JavaScriptSerializer and the proxy properties will be emitted. For example:
[ScriptIgnore]
public DateTime DateValue { get; set; }
public string DateValueJS
{
get { return DateValue.ToString("g"); }
}
Method 2: Use CustomConverters with JavaScriptSerializer
Use the CustomConverters support that's built into JavaScriptSerializer to register your own class for handling the serialization of particular types. For example:
public class DateJsonConverter : JavaScriptConverter
{
public override IEnumerable<Type> SupportedTypes
{
get { return new Type[] { typeof(DateTime) }; }
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
return new Dictionary<string, object>()
{
{ "Value", ((DateTime)obj).ToString("g") }
};
}
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
throw new NotSupportedException();
}
}
And you use this custom converter like this:
var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new JavaScriptConverter[] { new DateJsonConverter() });
Date values will be serialized by this class into: {"Dt":{"Value":"6/11/2013 5:36 PM"}}
Method 3: Use Reflection to transparently format DateTime
You can use reflection to transparently convert DateTime values into string values when the value is being serialized. For example:
private static object FormatDateTime(object x)
{
if (x == null || x is IEnumerable)
return x;
var t = x.GetType();
if (t == typeof(DateTime))
return ((DateTime)x).ToString("g");
if (t.IsPrimitive)
return x;
var result = new Dictionary<string, object>();
foreach (var prop in t.GetProperties())
{
// Skip properties with ScriptIgnoreAttribute
if (prop.GetCustomAttributes(typeof(ScriptIgnoreAttribute), true).Any())
continue;
result[prop.Name] = FormatDateTime(prop.GetValue(x, null));
}
return result;
}
This method can be used in your Select statement to convert the object values into a Dictionary that JavaScriptSerializer can use to emit the JSON. For example:
var value = new[] { new { Dt = DateTime.Now, Childs = new[] { 1, 2, 3 } } };
serializer.Serialize(value.Select(x => FormatDateTime(x)))
Will emit [{"Dt":"6/12/2013 3:27 PM","Childs":[1,2,3]}]
I never used JavaScriptSerializer, but if you have any influence on how the data is being deserialized, I'd recommend deserializing this data field into a string and then have a property on the Person class that will return the value converted into a DateTime.
I really like the ExpandoObject while compiling a server-side dynamic object at runtime, but I am having trouble flattening this thing out during JSON serialization. First, I instantiate the object:
dynamic expando = new ExpandoObject();
var d = expando as IDictionary<string, object>;
expando.Add("SomeProp", SomeValueOrClass);
So far so good. In my MVC controller, I want to then send this down as a JsonResult, so I do this:
return new JsonResult(expando);
This serializes the JSON into the below, to be consumed by the browser:
[{"Key":"SomeProp", "Value": SomeValueOrClass}]
BUT, what I'd really like is to see this:
{SomeProp: SomeValueOrClass}
I know I can achieve this if I use dynamic instead of ExpandoObject -- JsonResult is able to serialize the dynamic properties and values into a single object (with no Key or Value business), but the reason I need to use ExpandoObject is because I don't know all of the properties I want on the object until runtime, and as far as I know, I cannot dynamically add a property to a dynamic without using an ExpandoObject.
I may have to sift through the "Key", "Value" business in my javascript, but I was hoping to figure this out prior to sending it to the client. Thanks for your help!
Using JSON.NET you can call SerializeObject to "flatten" the expando object:
dynamic expando = new ExpandoObject();
expando.name = "John Smith";
expando.age = 30;
var json = JsonConvert.SerializeObject(expando);
Will output:
{"name":"John Smith","age":30}
In the context of an ASP.NET MVC Controller, the result can be returned using the Content-method:
public class JsonController : Controller
{
public ActionResult Data()
{
dynamic expando = new ExpandoObject();
expando.name = "John Smith";
expando.age = 30;
var json = JsonConvert.SerializeObject(expando);
return Content(json, "application/json");
}
}
You could also, make a special JSONConverter that works only for ExpandoObject and then register it in an instance of JavaScriptSerializer. This way you could serialize arrays of expando,combinations of expando objects and ... until you find another kind of object that is not getting serialized correctly("the way u want"), then you make another Converter, or add another type to this one. Hope this helps.
using System.Web.Script.Serialization;
public class ExpandoJSONConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
var result = new Dictionary<string, object>();
var dictionary = obj as IDictionary<string, object>;
foreach (var item in dictionary)
result.Add(item.Key, item.Value);
return result;
}
public override IEnumerable<Type> SupportedTypes
{
get
{
return new ReadOnlyCollection<Type>(new Type[] { typeof(System.Dynamic.ExpandoObject) });
}
}
}
Using converter
var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new JavaScriptConverter[] { new ExpandoJSONConverter()});
var json = serializer.Serialize(obj);
Here's what I did to achieve the behavior you're describing:
dynamic expando = new ExpandoObject();
expando.Blah = 42;
expando.Foo = "test";
...
var d = expando as IDictionary<string, object>;
d.Add("SomeProp", SomeValueOrClass);
// After you've added the properties you would like.
d = d.ToDictionary(x => x.Key, x => x.Value);
return new JsonResult(d);
The cost is that you're making a copy of the data before serializing it.
I solved this by writing an extension method that converts the ExpandoObject into a JSON string:
public static string Flatten(this ExpandoObject expando)
{
StringBuilder sb = new StringBuilder();
List<string> contents = new List<string>();
var d = expando as IDictionary<string, object>;
sb.Append("{");
foreach (KeyValuePair<string, object> kvp in d) {
contents.Add(String.Format("{0}: {1}", kvp.Key,
JsonConvert.SerializeObject(kvp.Value)));
}
sb.Append(String.Join(",", contents.ToArray()));
sb.Append("}");
return sb.ToString();
}
This uses the excellent Newtonsoft library.
JsonResult then looks like this:
return JsonResult(expando.Flatten());
And this is returned to the browser:
"{SomeProp: SomeValueOrClass}"
And I can use it in javascript by doing this (referenced here):
var obj = JSON.parse(myJsonString);
I hope this helps!
I was able to solve this same problem using JsonFx.
dynamic person = new System.Dynamic.ExpandoObject();
person.FirstName = "John";
person.LastName = "Doe";
person.Address = "1234 Home St";
person.City = "Home Town";
person.State = "CA";
person.Zip = "12345";
var writer = new JsonFx.Json.JsonWriter();
return writer.Write(person);
output:
{ "FirstName": "John", "LastName": "Doe", "Address": "1234 Home St",
"City": "Home Town", "State": "CA", "Zip": "12345" }
I took the flattening process one step further and checked for list objects, which removes the key value nonsense. :)
public string Flatten(ExpandoObject expando)
{
StringBuilder sb = new StringBuilder();
List<string> contents = new List<string>();
var d = expando as IDictionary<string, object>;
sb.Append("{ ");
foreach (KeyValuePair<string, object> kvp in d)
{
if (kvp.Value is ExpandoObject)
{
ExpandoObject expandoValue = (ExpandoObject)kvp.Value;
StringBuilder expandoBuilder = new StringBuilder();
expandoBuilder.Append(String.Format("\"{0}\":[", kvp.Key));
String flat = Flatten(expandoValue);
expandoBuilder.Append(flat);
string expandoResult = expandoBuilder.ToString();
// expandoResult = expandoResult.Remove(expandoResult.Length - 1);
expandoResult += "]";
contents.Add(expandoResult);
}
else if (kvp.Value is List<Object>)
{
List<Object> valueList = (List<Object>)kvp.Value;
StringBuilder listBuilder = new StringBuilder();
listBuilder.Append(String.Format("\"{0}\":[", kvp.Key));
foreach (Object item in valueList)
{
if (item is ExpandoObject)
{
String flat = Flatten(item as ExpandoObject);
listBuilder.Append(flat + ",");
}
}
string listResult = listBuilder.ToString();
listResult = listResult.Remove(listResult.Length - 1);
listResult += "]";
contents.Add(listResult);
}
else
{
contents.Add(String.Format("\"{0}\": {1}", kvp.Key,
JsonSerializer.Serialize(kvp.Value)));
}
//contents.Add("type: " + valueType);
}
sb.Append(String.Join(",", contents.ToArray()));
sb.Append("}");
return sb.ToString();
}
JsonResult uses JavaScriptSerializer which actually deserializes (the concrete) Dictionary<string, object> as you want.
There's an overload of the Dictionary<string, object> constructor which takes IDictionary<string, object>.
ExpandoObject implements IDictionary<string, object> (I think you can see where I am going here...)
Single level ExpandoObject
dynamic expando = new ExpandoObject();
expando.hello = "hi";
expando.goodbye = "cya";
var dictionary = new Dictionary<string, object>(expando);
return this.Json(dictionary); // or new JsonResult { Data = dictionary };
One line of code, using all built-in types :)
Nested ExpandoObjects
Of course if you are nesting ExpandoObjects then you will need to recursively convert them all into Dictionary<string, object>s:
public static Dictionary<string, object> RecursivelyDictionary(
IDictionary<string, object> dictionary)
{
var concrete = new Dictionary<string, object>();
foreach (var element in dictionary)
{
var cast = element.Value as IDictionary<string, object>;
var value = cast == null ? element.Value : RecursivelyDictionary(cast);
concrete.Add(element.Key, value);
}
return concrete;
}
your final code becoming
dynamic expando = new ExpandoObject();
expando.hello = "hi";
expando.goodbye = "cya";
expando.world = new ExpandoObject();
expando.world.hello = "hello world";
var dictionary = RecursivelyDictionary(expando);
return this.Json(dictionary);
This may not be useful to you, but I had a similar requirement, but used a SerializableDynamicObject
I changed the name of the dictionary to "Fields" and then this serializes with Json.Net to produce json which looks like:
{"Fields":{"Property1":"Value1", "Property2":"Value2" etc.
where Property1 and Property2 are Dynamically added properties - i.e. Dictionary Keys
It would be perfect if I could get rid of the extra "Fields" property which encapsulates the rest, but I've worked around that limitation.
Answer moved from this question on request
This is a late answer, but I had the same problem, and this question helped me solve them.
As a summary, I thought I should post my results, in hopes that it speeds up the implementation for others.
First the ExpandoJsonResult, which you can return an instance of in your action. Or you can override the Json method in your controller and return it there.
public class ExpandoJsonResult : JsonResult
{
public override void ExecuteResult(ControllerContext context)
{
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = !string.IsNullOrEmpty(ContentType) ? ContentType : "application/json";
response.ContentEncoding = ContentEncoding ?? response.ContentEncoding;
if (Data != null)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new JavaScriptConverter[] { new ExpandoConverter() });
response.Write(serializer.Serialize(Data));
}
}
}
Then the converter (which supports both serialization and de-serialization. See below for an example of how to de-serialize).
public class ExpandoConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{ return DictionaryToExpando(dictionary); }
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{ return ((ExpandoObject)obj).ToDictionary(x => x.Key, x => x.Value); }
public override IEnumerable<Type> SupportedTypes
{ get { return new ReadOnlyCollection<Type>(new Type[] { typeof(System.Dynamic.ExpandoObject) }); } }
private ExpandoObject DictionaryToExpando(IDictionary<string, object> source)
{
var expandoObject = new ExpandoObject();
var expandoDictionary = (IDictionary<string, object>)expandoObject;
foreach (var kvp in source)
{
if (kvp.Value is IDictionary<string, object>) expandoDictionary.Add(kvp.Key, DictionaryToExpando((IDictionary<string, object>)kvp.Value));
else if (kvp.Value is ICollection)
{
var valueList = new List<object>();
foreach (var value in (ICollection)kvp.Value)
{
if (value is IDictionary<string, object>) valueList.Add(DictionaryToExpando((IDictionary<string, object>)value));
else valueList.Add(value);
}
expandoDictionary.Add(kvp.Key, valueList);
}
else expandoDictionary.Add(kvp.Key, kvp.Value);
}
return expandoObject;
}
}
You can see in the ExpandoJsonResult class how to use it for serialization. To de-serialize, create the serializer and register the converter in the same way, but use
dynamic _data = serializer.Deserialize<ExpandoObject>("Your JSON string");
A big thank you, to all participants here that helped me.
Using returning dynamic ExpandoObject from WebApi in ASP.Net 4, the default JSON formatter seems to flatten ExpandoObjects into simple JSON object.
It seems like the serializer is casting the Expando to a Dictionary and then serializing it (thus the Key/Value business). Have you tried Deserializing as a Dictionary and then casting that back to an Expando?
I just had the same problem and figured out something pretty weird.
If I do:
dynamic x = new ExpandoObject();
x.Prop1 = "xxx";
x.Prop2 = "yyy";
return Json
(
new
{
x.Prop1,
x.Prop2
}
);
It works, but only if my method use HttpPost attribute. If I use HttpGet i get error.
So my answer works only on HttpPost. In my case it was an Ajax Call so i could change HttpGet by HttpPost.