Asp.net mvc'de JsonResult aracılığıyla döndürülen bir ExpandoObject nasıl düzleştirilir?


95

Çalışma ExpandoObjectzamanında sunucu tarafı dinamik bir nesneyi derlerken gerçekten hoşuma gidiyor , ancak JSON serileştirme sırasında bu şeyi düzleştirmede sorun yaşıyorum. İlk olarak, nesneyi somutlaştırıyorum:

dynamic expando = new ExpandoObject();
var d = expando as IDictionary<string, object>;
expando.Add("SomeProp", SomeValueOrClass);

Çok uzak çok iyi. MVC denetleyicimde, bunu bir JsonResult olarak göndermek istiyorum, bu yüzden şunu yapıyorum:

return new JsonResult(expando);

Bu, JSON'yi tarayıcı tarafından tüketilmek üzere aşağıdaki şekilde serileştirir:

[{"Key":"SomeProp", "Value": SomeValueOrClass}]

AMA, gerçekten istediğim şey şunu görmek:

{SomeProp: SomeValueOrClass}

Bunu dynamicyerine kullanırsam başarabileceğimi biliyorum ExpandoObject- özellikleri ve değerleri tek bir nesnede (Anahtar veya Değer işi olmadan) JsonResultseri hale getirebilirse dynamic, ancak kullanmamın ExpandoObjectnedeni hepsini bilmemem çalışma zamanına kadar nesnede istediğim özellikler ve bildiğim kadarıyla, dynamicbir ExpandoObject.

JavaScript'imdeki "Anahtar", "Değer" işini incelemek zorunda kalabilirim, ancak bunu müşteriye göndermeden önce çözmeyi umuyordum. Yardımınız için teşekkürler!


9
Neden ExpandoObject yerine Dictionary <string, object> kullanmıyorsunuz? Otomatik olarak istediğiniz formata göre serileştirir ve zaten ExpandoObject'inizi yalnızca bir sözlük gibi kullanıyorsunuz. Meşru ExpandoObject'leri serileştirmek istiyorsanız, "yeni JsonResult (d.ToDictionary (x => x.Key, x => x.Value)));" yaklaşım muhtemelen en iyi uzlaşmadır.
BrainSlugs83

Yanıtlar:


37

Ayrıca, yalnızca ExpandoObject için çalışan özel bir JSONConverter yapabilir ve ardından bunu JavaScriptSerializer örneğine kaydedebilirsiniz. Bu şekilde, expando dizilerini, expando nesnelerinin kombinasyonlarını ve ... doğru şekilde serileştirilmemiş başka tür bir nesne bulana kadar ("istediğiniz şekilde"), sonra başka bir Dönüştürücü oluşturabilir veya başka bir tür ekleyebilirsiniz. Bu. Bu yardımcı olur umarım.

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) });
        }
    }
}

Dönüştürücüyü kullanma

var serializer = new JavaScriptSerializer(); 
serializer.RegisterConverters(new JavaScriptConverter[] { new ExpandoJSONConverter()});
var json = serializer.Serialize(obj);

2
Bu, ihtiyaçlarım için harika çalıştı. Birisi NotImplementedExceptiongibi bir şey eklemek için bir kod eklemek serializer.Deserialize<ExpandoObject>(json);isterse, @theburningmonk benim için çalışan bir çözüm sunar .
patridge

2
Harika iş @ pablo. Özel bir serileştirme rutini MVC çerçevesine eklemenin mükemmel bir örneği!
pb.

Bunu yapmanın en kolay yolu şuydu: new JavaScriptSerializer (). <nesneyi> kaldır (Newtonsoft.Json.JsonConvert.SerializeObject (listOfExpandoObject)); ne düşünüyorsun?
kavain

Serileştiricim yinelemeli olarak çağrılıyor. RecursionLimit'i ayarlarsam, ya özyineleme sınırı aşıldı hatası ya da yığın taşması istisna hatası alıyorum. Ne yapmalıyım? :(
Dhanashree

72

JSON.NET kullanarak SerializeObject öğesini expando nesnesini "düzleştirmek" için çağırabilirsiniz:

dynamic expando = new ExpandoObject();
expando.name = "John Smith";
expando.age = 30;

var json = JsonConvert.SerializeObject(expando);

Çıktı:

{"name":"John Smith","age":30}

ASP.NET MVC Denetleyicisi bağlamında, sonuç İçerik yöntemi kullanılarak döndürülebilir: 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");
    }
}

1
Newtonsoft.Json demek istiyorsun?
Ayyash

3
newtonsoft.json, genişletmeler veya sözlükler ve iç sözlükler içindeki yinelemeli genişletmeler için kutudan çıkar çıkmaz daha iyi bir kullanım sağlar
Jone Polvora

26

Tanımladığınız davranışı gerçekleştirmek için yaptığım şey şu:

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);

Maliyet, verileri serileştirmeden önce bir kopyasını oluşturuyor olmanızdır.


Güzel. Dinamikleri anında yayınlayabilirsiniz: yeni JsonResult (((ExpandoObject) someIncomingDynamicExpando) .ToDictionary (item => item.Key, item => item.Value))
joeriks

"expando.Add" benim için çalışmıyor. Bu durumda bunun "d.Add" olduğuna inanıyorum (benim için çalıştı).
Justin

9
Öyleyse bekleyin ... bir ExpandoObject oluşturuyorsunuz, onu sözlük olarak kullanıyorsunuz, sözlük gibi kullanıyorsunuz ve sonra bu yeterince iyi olmadığında, sözlüğe dönüştürüyorsunuz ... ... neden sadece bir sözlük kullanmıyorsunuz? bu durum? ...
o_o

5
An ExpandoObjectsize basit bir Sözlükten çok daha fazla esneklik sağlar. Yukarıdaki örnek bunu göstermese de, ExpandoObjectJSON'nuzda olmasını istediğiniz özellikleri eklemek için öğesinin dinamik özelliklerini kullanabilirsiniz . Normal bir Dictioanrynesne sorunsuz bir ExpandoObjectşekilde JSON'a dönüşür , bu nedenle dönüştürmeyi yaparak, kullanımı kolay dinamiği JSONifiye edilebilecek bir biçime yerleştirmenin basit bir O (n) yoludur . Yine de haklısınız, yukarıdaki örnek ExpandoObject; basit Dictionaryçok daha iyi olurdu.
ajb

1
Bu yaklaşıma daha çok benziyor - bir kopya oluşturmak herhangi bir ortamda işe yaramıyor, ancak yalnızca küçük nesnelerim var ve Expando (değiştirilemez) bir 3. tarafça sağlanıyor ....
Sebastian J.

12

Bunu ExpandoObject'i bir JSON dizesine dönüştüren bir uzantı yöntemi yazarak çözdüm:

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();
}

Bu, mükemmel Newtonsoft kütüphanesini kullanır .

JsonResult daha sonra şuna benzer:

return JsonResult(expando.Flatten());

Ve bu tarayıcıya geri döndürülür:

"{SomeProp: SomeValueOrClass}"

Bunu yaparak bunu javascript'te kullanabilirim ( burada referans gösterilmektedir ):

var obj = JSON.parse(myJsonString);

Umarım bu yardımcı olur!


7
Değerlendirmeyin! Güvenlik sorunlarını önlemek için bir JSON seriyi kaldırıcı kullanmalısınız. Bkz. Json2.js : json.org/js.html var o = JSON.parse (myJsonString);
Lance Fisher

Yine de bu uzatma yöntemini seviyorum. Güzel!
Lance Fisher

3
-1: Bunu bir dize döndüren bir uzantı yönteminde yapmak, bu davranışı çerçeveyle arabirim haline getirmenin doğru yolu değildir. Bunun yerine yerleşik serileştirme mimarisini genişletmelisiniz.
BrainSlugs83

1
Bu yöntemin en büyük dezavantajı, özyineleme eksikliğidir - üst düzey nesnenin dinamik olduğunu biliyorsanız ve bu işe yarar, ancak dinamik nesneler döndürülen nesne ağacının herhangi bir veya her düzeyinde olabilirse, bu başarısız olur.
Chris Moschini

Yinelemeli hale getirmek için bu yöntemde bazı iyileştirmeler yaptım. İşte kod: gist.github.com/renanvieira/e26dc34e2de156723f79
MaltMaster

5

JsonFx kullanarak aynı sorunu çözebildim .

        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);

çıktı:

{"FirstName": "John", "LastName": "Doe", "Address": "1234 Home St", "City": "Home Town", "State": "CA", "Zip": "12345 "}


1
Bunu JSON .Net (Newtonsoft) kullanarak aşağıdaki adımları tamamlayarak da yapabilirsiniz. var varlık = nesne olarak kişi; var json = JsonConvert.SerializeObject (varlık);
bkorzynski

4

Düzleştirme sürecini bir adım daha ileri götürdüm ve anahtar değeri saçmalıklarını ortadan kaldıran liste nesnelerini kontrol ettim. :)

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();
    }

3

Bu sizin için yararlı olmayabilir, ancak benzer bir gereksinimim vardı, ancak SerializableDynamicObject kullandım

Sözlüğün adını "Alanlar" olarak değiştirdim ve ardından bu, Json.Net ile serileştirerek json üretecek şekilde şöyle görünüyor:

{"Alanlar": {"Özellik1": "Değer1", "Özellik2": "Değer2" vb. Burada Özellik1 ve Özellik2 Dinamik olarak eklenen özelliklerdir - yani Sözlük Anahtarları

Gerisini özetleyen fazladan "Alanlar" özelliğinden kurtulabilirsem mükemmel olurdu, ama bu sınırlama üzerinde çalıştım.

Cevap istek üzerine bu sorudan taşındı


3

Bu geç bir cevap, ama aynı sorunu yaşadım ve bu soru onları çözmeme yardımcı oldu. Özet olarak, başkaları için uygulamayı hızlandırması umuduyla sonuçlarımı yayınlamam gerektiğini düşündüm.

İlk olarak, eyleminizde bir örneğini döndürebileceğiniz ExpandoJsonResult. Veya denetleyicinizdeki Json yöntemini geçersiz kılabilir ve oraya geri döndürebilirsiniz.

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));
        }
    }
}

Ardından dönüştürücü (hem serileştirmeyi hem de serileştirmeyi kaldırmayı destekler. Serileştirmenin nasıl kaldırılacağına ilişkin bir örnek için aşağıya bakın).

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 sınıfında serileştirme için nasıl kullanılacağını görebilirsiniz. Serileştirmeyi kaldırmak için, serileştiriciyi oluşturun ve dönüştürücüyü aynı şekilde kaydedin, ancak

dynamic _data = serializer.Deserialize<ExpandoObject>("Your JSON string");

Bana yardım eden tüm katılımcılara çok teşekkür ederim.


2

JsonResultJavaScriptSerializergerçekte Dictionary<string, object>istediğiniz gibi (somut) seriyi kaldıran kullanır .

Bir aşırı yük var Dictionary<string, object>alır yapıcısı IDictionary<string, object>.

ExpandoObjectuygular IDictionary<string, object> (burada nereye gittiğimi görebileceğinizi düşünüyorum ...)

Tek seviyeli 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 };

Tüm yerleşik türleri kullanan bir satır kod :)

İç içe geçmiş ExpandoObjects

Elbette, öğeleri iç içe yerleştiriyorsanız ExpandoObject, hepsini yinelemeli olarak Dictionary<string, object>s'ye dönüştürmeniz gerekecektir :

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;
}

son kodun olma

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);

1

ASP.Net 4'te WebApi'den dinamik ExpandoObject döndürerek, varsayılan JSON biçimlendiricisi, ExpandoObjects'i basit JSON nesnesine düzleştiriyor gibi görünüyor.


-2

Görünüşe göre serileştirici Expando'yu bir Sözlüğe aktarıyor ve sonra onu serileştiriyor (dolayısıyla Anahtar / Değer işi). Bir Sözlük olarak Seriyi Kaldırmayı ve ardından bunu bir Expando'ya geri göndermeyi denediniz mi?


1
Expando nesnesi IDictionary <string, object> uygular, bu yüzden JsonResult'un onu bir anahtar / değer çiftleri dizisi halinde serileştirmesinin nedeni budur. Bunu bir kimlik sözlüğü olarak ve tekrar geri çevirmek, korkarım onu ​​düzleştirmeye gerçekten yardımcı olmaz.
TimDog

-2

Ben de aynı problemi yaşadım ve oldukça garip bir şey buldum. Eğer yaparsam:

dynamic x = new ExpandoObject();
x.Prop1 = "xxx";
x.Prop2 = "yyy";
return Json
(
    new
    {
        x.Prop1,
        x.Prop2
    }
);

Çalışır, ancak yalnızca yöntemim HttpPost özniteliğini kullanıyorsa. HttpGet kullanırsam hata alıyorum. Bu yüzden cevabım sadece HttpPost'ta çalışıyor. Benim durumumda bu bir Ajax Çağrısıydı, bu yüzden HttpGet'i HttpPost tarafından değiştirebilirim.


2
-1 Bu, stackoverflow.com/a/7042631/11635'e indirgendiğinden gerçekten kullanışlı değildir ve yaptığınız gibi dönüp adlara statik olarak güvenecekseniz, bunu dinamik olarak yapmanın bir anlamı yoktur. AllowGet sorunu tamamen ortogonaldir.
Ruben Bartelink
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.