Мне очень нравится ExpandoObject
компиляция динамического объекта на стороне сервера во время выполнения, но у меня возникают проблемы с выравниванием этого объекта во время сериализации JSON. Сначала я создаю экземпляр объекта:
dynamic expando = new ExpandoObject();
var d = expando as IDictionary<string, object>;
expando.Add("SomeProp", SomeValueOrClass);
Все идет нормально. В моем контроллере MVC я хочу отправить это как JsonResult, поэтому я делаю следующее:
return new JsonResult(expando);
Это сериализует JSON в приведенный ниже код, который будет использоваться браузером:
[{"Key":"SomeProp", "Value": SomeValueOrClass}]
НО, мне бы очень хотелось это увидеть:
{SomeProp: SomeValueOrClass}
Я знаю, что могу добиться этого, если использую dynamic
вместо ExpandoObject
- JsonResult
может сериализовать dynamic
свойства и значения в один объект (без бизнеса Key или Value), но причина, по которой мне нужно использовать, ExpandoObject
заключается в том, что я не знаю всех свойства, которые мне нужны для объекта до времени выполнения , и, насколько мне известно, я не могу динамически добавлять свойство в объект dynamic
без использования ExpandoObject
.
Возможно, мне придется просеять бизнес «Ключ» и «Ценность» в моем javascript, но я надеялся выяснить это перед отправкой клиенту. Спасибо за вашу помощь!
источник
Ответы:
Вы также можете создать специальный JSONConverter, который работает только для ExpandoObject, а затем зарегистрировать его в экземпляре JavaScriptSerializer. Таким образом, вы можете сериализовать массивы expando, комбинации объектов expando и ... до тех пор, пока вы не найдете другой тип объекта, который не сериализуется правильно («так, как вы хотите»), затем вы создаете другой конвертер или добавляете другой тип в вот этот. Надеюсь это поможет.
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) }); } } }
Использование конвертера
var serializer = new JavaScriptSerializer(); serializer.RegisterConverters(new JavaScriptConverter[] { new ExpandoJSONConverter()}); var json = serializer.Serialize(obj);
источник
NotImplementedException
добавить какой-то код, чтобы добавить что-то вродеserializer.Deserialize<ExpandoObject>(json);
, @theburningmonk предлагает решение, которое сработало для меня.Используя JSON.NET, вы можете вызвать SerializeObject, чтобы «сгладить» объект expando:
dynamic expando = new ExpandoObject(); expando.name = "John Smith"; expando.age = 30; var json = JsonConvert.SerializeObject(expando);
Выведет:
{"name":"John Smith","age":30}
В контексте ASP.NET MVC Controller результат может быть возвращен с помощью Content-метода:
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"); } }
источник
Вот что я сделал для достижения описываемого вами поведения:
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);
Стоимость состоит в том, что вы делаете копию данных перед их сериализацией.
источник
ExpandoObject
дает вам гораздо больше гибкости, чем простой словарь. Хотя приведенный выше пример не демонстрирует этого, вы можете использовать динамические функции,ExpandoObject
чтобы добавить свойства, которые вы хотите иметь в своем JSON. ОбычныйDictioanry
объект конвертируется в JSON без каких-либо проблем, поэтому, выполнив преобразование, это простой способ O (n) преобразовать простую в использовании динамикуExpandoObject
в формат, который может быть JSONified. Однако вы правы, приведенный выше пример - это повторное использованиеExpandoObject
; простойDictionary
будет намного лучше.Я решил это, написав метод расширения, который преобразует ExpandoObject в строку JSON:
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(); }
Здесь используется отличная библиотека Newtonsoft .
Тогда JsonResult выглядит так:
return JsonResult(expando.Flatten());
И это возвращается в браузер:
"{SomeProp: SomeValueOrClass}"
И я могу использовать его в javascript, выполнив это (ссылка здесь ):
var obj = JSON.parse(myJsonString);
Надеюсь, это поможет!
источник
Я смог решить эту же проблему с помощью 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);
выход:
источник
Я продвинулся на шаг вперед в процессе сглаживания и проверил объекты списка, что устраняет бессмыслицу значения ключа. :)
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(); }
источник
Это может быть вам не полезно, но у меня было аналогичное требование, но я использовал SerializableDynamicObject
Я изменил имя словаря на «Поля», а затем он сериализуется с помощью Json.Net для создания json, который выглядит так:
{"Поля": {"Свойство1": "Значение1", "Свойство2": "Значение2" и т. Д., Где Свойство1 и Свойство2 - это динамически добавляемые свойства, то есть ключи словаря
Было бы идеально, если бы я мог избавиться от лишнего свойства «Fields», которое инкапсулирует все остальное, но я обошел это ограничение.
Ответ перенесен с этого вопроса по запросу
источник
Это поздний ответ, но у меня была такая же проблема, и этот вопрос помог мне их решить. Подводя итог, я подумал, что должен опубликовать свои результаты в надежде, что это ускорит реализацию для других.
Сначала ExpandoJsonResult, экземпляр которого вы можете вернуть в своем действии. Или вы можете переопределить метод Json в своем контроллере и вернуть его туда.
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)); } } }
Затем конвертер (который поддерживает как сериализацию, так и десериализацию. См. Ниже пример того, как десериализовать).
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; } }
Вы можете увидеть в классе ExpandoJsonResult, как использовать его для сериализации. Для десериализации создайте сериализатор и зарегистрируйте конвертер таким же образом, но используйте
dynamic _data = serializer.Deserialize<ExpandoObject>("Your JSON string");
Большое спасибо всем участникам, которые мне помогли.
источник
JsonResult
использует,JavaScriptSerializer
который фактически десериализует (бетон),Dictionary<string, object>
как вы хотите.Имеется перегрузка
Dictionary<string, object>
конструктора, который принимаетIDictionary<string, object>
.ExpandoObject
орудияIDictionary<string, object>
(я думаю, вы видите, куда я иду ...)Одноуровневый 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 };
Одна строка кода, использующая все встроенные типы :)
Вложенные объекты ExpandoObjects
Конечно, если вы вкладываете
ExpandoObject
s, вам нужно будет рекурсивно преобразовать их все в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; }
ваш окончательный код становится
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);
источник
Используя возврат динамического ExpandoObject из WebApi в ASP.Net 4, модуль форматирования JSON по умолчанию, кажется, сглаживает ExpandoObjects в простой объект JSON.
источник
Похоже, что сериализатор переводит Expando в словарь, а затем сериализует его (таким образом, бизнес-ключ / значение). Вы пробовали десериализацию как словарь, а затем преобразовать ее обратно в Expando?
источник
У меня была такая же проблема, и я выяснил кое-что довольно странное. Если я сделаю:
dynamic x = new ExpandoObject(); x.Prop1 = "xxx"; x.Prop2 = "yyy"; return Json ( new { x.Prop1, x.Prop2 } );
Он работает, но только если мой метод использует атрибут HttpPost. Если я использую HttpGet, я получаю ошибку. Итак, мой ответ работает только на HttpPost. В моем случае это был вызов Ajax, поэтому я мог изменить HttpGet на HttpPost.
источник