कैसे एक ExpandoObject को समतल करने के लिए asp.net mvc में JsonResult के माध्यम से लौटा?


95

मैं 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, लेकिन मुझे जो उपयोग करने की आवश्यकता है ExpandoObjectवह इसलिए है क्योंकि मैं सभी को नहीं जानता हूं गुण जो मैं रनटाइम तक ऑब्जेक्ट पर चाहता हूं , और जहां तक ​​मुझे पता है, मैं गतिशील रूप से एक dynamicका उपयोग किए बिना एक संपत्ति नहीं जोड़ सकता ExpandoObject

मुझे अपनी जावास्क्रिप्ट में "कुंजी", "मूल्य" व्यवसाय के माध्यम से झारना पड़ सकता है, लेकिन मैं इसे ग्राहक को भेजने से पहले यह पता लगाने की उम्मीद कर रहा था। आपकी सहायताके लिए धन्यवाद!


9
एक्सपेंडेओबजेक्ट के बजाय डिक्शनरी <string> ऑब्जेक्ट> का उपयोग क्यों नहीं किया जाता? यह स्वचालित रूप से आपके इच्छित प्रारूप पर क्रमबद्ध हो जाता है, और आप केवल किसी भी तरह एक शब्दकोश की तरह अपने ExpandoObject का उपयोग कर रहे हैं। यदि आप "नया JsonResult (d.ToDictionary (x => x.Key, x => x.Value)) लौटाएँ" का उपयोग करते हुए, वैध ExpandoObject के क्रमबद्ध करना चाहते हैं; दृष्टिकोण शायद सबसे अच्छा समझौता है।
ब्रेनस्ल्गस83

जवाबों:


37

आप एक विशेष JSONConverter भी बना सकते हैं, जो केवल ExpandoObject के लिए काम करता है और फिर इसे JavaScriptSerializer की आवृत्ति में पंजीकृत करता है। इस तरह से आप एक्सपेंडो के एसेसरीज, एक्सपेंडेओ ऑब्जेक्ट्स के कॉम्बिनेशन को क्रिएट कर सकते हैं और ... जब तक आपको दूसरी तरह की ऑब्जेक्ट नहीं मिल जाती है जो सही तरीके से सीरीज़ नहीं हो रही है ("जिस तरह से आप चाहते हैं"), तो आप एक और कन्वर्टर बनाते हैं, या दूसरे टाइप को एड करते हैं यह वाला। उम्मीद है की यह मदद करेगा।

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

2
यह मेरी जरूरतों के लिए बहुत काम आया। अगर कोई किसी NotImplementedExceptionचीज़ को जोड़ने के लिए किसी कोड में प्लग करना चाहता है serializer.Deserialize<ExpandoObject>(json);, तो @theburningmonk एक समाधान प्रदान करता है जो मेरे लिए काम करता है
Patridge

2
MVC फ्रेमवर्क में एक कस्टम क्रमांकन दिनचर्या प्लग करने का महान कार्य @ pablo.Excellent उदाहरण!
पी.बी.

सबसे आसान तरीका जो मुझे ऐसा करने के लिए मिला वह था: नया JavaScriptSerializer ()। Deserialize <object> (Newtonsoft.Json.JsonConvert.SerializeObject (listOfExpandoOfject)); तुम क्या सोचते हो?
केवैन

मेरे धारावाहिक को पुनरावर्ती कहा जाता है। यदि मैं RecursionLimit सेट करता हूं, तो मुझे या तो पुनरावर्तन सीमा त्रुटि से अधिक हो जाती है या ओवरफ़्लो अपवाद त्रुटि हो जाती है। मुझे क्या करना चाहिए? :(
धनश्री

71

JSON.NET का उपयोग करके आप विस्तारक ऑब्जेक्ट को "समतल" करने के लिए SerializeObject को कॉल कर सकते हैं:

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

var json = JsonConvert.SerializeObject(expando);

उत्पादन होगा:

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

ASP.NET MVC नियंत्रक के संदर्भ में, सामग्री-विधि का उपयोग करके परिणाम लौटाया जा सकता है:

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 तुम्हारा मतलब है?
अय्याश

3
newtonsoft.json के पास विस्तारक या शब्दकोश और इनर डिक्शनरी के अंदर पुनरावर्ती विस्तार के लिए बेहतर हैंडलिंग है, बॉक्स से बाहर
जौन पोलोवा

26

आपके द्वारा बताए गए व्यवहार को प्राप्त करने के लिए मैंने यहां क्या किया है:

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

लागत यह है कि आप इसे क्रमबद्ध करने से पहले डेटा की एक प्रति बना रहे हैं।


अच्छा लगा। आप फ़्लाई पर डायनामिक भी डाल सकते हैं: नया JsonResult ((ExpandoObject) someIncomingDynamicExpando) वापस लौटाएं। टूलरोड (आइटम => item.Key, item => item.Value)
joeriks

"Expando.Add" मेरे लिए काम नहीं करता है। मेरा मानना ​​है कि इस मामले में यह "d.Add" है (जो मेरे लिए काम किया है)।
जस्टिन

9
इसलिए प्रतीक्षा करें ... आप एक ExpandoObject बना रहे हैं, इसे एक शब्दकोश के रूप में कास्टिंग कर रहे हैं, इसे एक शब्दकोश की तरह उपयोग कर रहे हैं, और फिर जब यह पर्याप्त नहीं होता है, तो इसे एक शब्दकोश में परिवर्तित करना ... ... सिर्फ एक शब्दकोश का उपयोग क्यों न करें ये मामला? ... o_o
BrainSlugs83

5
एक ExpandoObjectसरल शब्दकोश की तुलना में आपको बहुत अधिक लचीलापन देता है। यद्यपि ऊपर दिया गया उदाहरण इसे प्रदर्शित नहीं करता है, आप ExpandoObjectअपने जेएसएन में उन गुणों को जोड़ने के लिए गतिशील सुविधाओं का उपयोग कर सकते हैं जो आप चाहते हैं। एक सामान्य Dictioanryवस्तु बिना किसी समस्या के JSON में परिवर्तित हो जाएगी, इसलिए रूपांतरण करके, यह एक सरल O- (n) तरीका है ExpandoObject, जो एक प्रारूप के लिए आसान-से-गतिशील का उपयोग करने के लिए है, जो JSONified हो सकता है। हालांकि आप सही हैं, ऊपर दिया गया उदाहरण एक रेडिकुलस उपयोग होगा ExpandoObject; एक सरल Dictionaryबहुत बेहतर होगा।
अंजब

1
उस दृष्टिकोण की तरह - अधिक प्रतिलिपि बनाना किसी भी वातावरण में काम नहीं करता है, लेकिन मेरे पास केवल छोटी वस्तुएं हैं और एक्सपेंडो को एक (अपरिवर्तनीय) तीसरी पार्टी द्वारा प्रदान किया जाता है ....
सेबस्टियन जे।

12

मैंने इसे एक एक्सटेंशन विधि लिखकर हल किया, जो 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}"

और मैं इसे ( यहाँ संदर्भित ) करके जावास्क्रिप्ट में इसका उपयोग कर सकता हूं :

var obj = JSON.parse(myJsonString);

आशा है कि ये आपकी मदद करेगा!


7
इसे विकसित न करें! सुरक्षा के मुद्दों से बचने के लिए आपको JSON डेज़राइज़र का उपयोग करना चाहिए। Json2.js देखें: json.org/js.html var o = JSON.parse (myJsonString);
लांस फिशर

मुझे वह विस्तार विधि पसंद है। अच्छा!
लांस फिशर

3
-1: एक विस्तार पद्धति में ऐसा करना जो एक स्ट्रिंग लौटाता है, फ्रेमवर्क के साथ इस व्यवहार को इंटरफ़ेस करने का सही तरीका नहीं है। आपको इसके बजाय निर्मित क्रमांकन वास्तुकला में विस्तार करना चाहिए।
BrainSlugs83

1
इस पद्धति का प्रमुख दोष पुनरावृत्ति की कमी है - यदि आप शीर्ष-स्तरीय वस्तु को गतिशील होने के लिए जानते हैं और यही है, तो यह काम करता है, लेकिन अगर गतिशील वस्तुएं किसी भी या हर स्तर पर वस्तु पेड़ की वापसी हो सकती हैं, तो यह विफल हो जाता है।
क्रिस मोसचिनी

मैंने इसे पुनरावर्ती बनाने के लिए इस पद्धति पर कुछ सुधार किए हैं। यहाँ कोड है: gist.github.com/renanvieira/e26dc34e2de156723f79
माल्टमास्टर

5

मैं 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);

उत्पादन:

{"फर्स्टनाम": "जॉन", "लास्टनेम": "डो", "एड्रेस": "1234 होम सेंट", "सिटी": "होम टाउन", "स्टेट": "सीए", "जिप": "12345 "}


1
आप निम्न चरणों को पूरा करके JSON .Net (न्यूटनसॉफ्ट) का उपयोग भी कर सकते हैं। var एंटिटी = व्यक्ति वस्तु के रूप में; var json = JsonConvert.SerializeObject (एंटिटी);
bkorzynski

4

मैंने चपटे करने की प्रक्रिया को एक कदम आगे बढ़ाया और सूची वस्तुओं के लिए जाँच की, जो मुख्य मूल्य बकवास को हटा देता है। :)

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

यह आपके लिए उपयोगी नहीं हो सकता है, लेकिन मुझे एक समान आवश्यकता थी, लेकिन एक SerializableDynamicObject का उपयोग किया

मैंने डिक्शनरी का नाम बदलकर "फील्ड्स" कर दिया और फिर यह Json.Net के साथ मिलकर जस का निर्माण करता है जो इस प्रकार है:

{"फ़ील्ड्स": {"प्रॉपर्टी 1": "वैल्यू 1", "प्रॉपर्टी 2": "वैल्यू 2" आदि जहां प्रॉपर्टी 1 और प्रॉपर्टी 2 को डायनामिकली जोड़ा गया गुण है - यानी डिक्शनरी कीज़

यह सही होगा यदि मैं अतिरिक्त "फील्ड्स" संपत्ति से छुटकारा पा सकता हूं जो बाकी को घेर लेता है, लेकिन मैंने उस सीमा के आसपास काम किया है।

अनुरोध पर इस प्रश्न का उत्तर दिया गया


3

यह एक देर से जवाब है, लेकिन मुझे भी यही समस्या थी और इस सवाल ने मुझे उन्हें हल करने में मदद की। एक सारांश के रूप में, मुझे लगा कि मुझे अपना परिणाम पोस्ट करना चाहिए, इस उम्मीद में कि यह दूसरों के लिए कार्यान्वयन को गति देगा।

सबसे पहले 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;
    }
}

आप ExpandationJsonResult वर्ग में देख सकते हैं कि इसे क्रमांकन के लिए कैसे उपयोग किया जाए। डी-सीरीज़ करने के लिए, सीरलाइज़र बनाएं और कनवर्टर को उसी तरह पंजीकृत करें, लेकिन उपयोग करें

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

एक बड़ा धन्यवाद सभी प्रतिभागियों के लिए, जिन्होंने मेरी मदद की।


1

ASP.Net 4 में वेबएपीआई से डायनामिक एक्सपेंडेओबजेक्ट का उपयोग करते हुए, डिफ़ॉल्ट JSON फॉर्मैटर को विस्तार से साधारण JSON ऑब्जेक्ट में समतल करना प्रतीत होता है।


1

JsonResultका उपयोग करता है JavaScriptSerializerजो वास्तव में deserializes (कंक्रीट) 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 };

सभी प्रकारों का उपयोग करके कोड की एक पंक्ति :)

नेस्टेड एक्सपेंडिऑबजेक्ट्स

बेशक, यदि आप नेस्ट कर रहे हैं ExpandoObjectतो आपको उन सभी को पुन: रूपांतरित करने की आवश्यकता होगी Dictionary<string, object>:

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

-2

ऐसा लगता है कि सीरियलाइज़र एक्सपेंडो को डिक्शनरी में डाल रहा है और फिर इसे सीरीज़ कर रहा है (इस प्रकार की / वैल्यू बिज़नेस)। क्या आपने डिस्क्रिअलाइज़िंग की कोशिश एक डिक्शनरी के रूप में की है और फिर उसे एक एक्सपेंडो में वापस लाना है?


1
एक्सपेंडेओ ऑब्जेक्ट IDEDIA <string, object> को लागू करता है, इसलिए मुझे लगता है कि यही कारण है कि JsonResult इसे कुंजी / मान जोड़े के एक सरणी में क्रमबद्ध करता है। इसे एक IDEDIA के रूप में कास्टिंग और वापस फिर से वास्तव में इसे समतल करने में मदद नहीं करेगा, मुझे डर है।
टिमडॉग

-2

मैं सिर्फ एक ही समस्या थी और कुछ बहुत अजीब लगा। यदि मैं करता हूँ:

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

यह काम करता है, लेकिन केवल अगर मेरी विधि HttpPost विशेषता का उपयोग करें। अगर मैं HttpGet का उपयोग करता हूं तो मुझे त्रुटि मिलती है। तो मेरा जवाब HttpPost पर ही काम करता है। मेरे मामले में यह एक अजाक्स कॉल था ताकि मैं HttpPost द्वारा HttpGet को बदल सकूं।


2
-1 यह वास्तव में उपयोगी नहीं है क्योंकि यह stackoverflow.com/a/7042631/11635 पर फोड़ा जाता है और इस सामान को गतिशील रूप से करने का कोई मतलब नहीं है यदि आप घूमने जा रहे हैं और नामों पर निर्भर करते हैं जैसा कि आप करते हैं। AllowGet समस्या पूरी तरह से रूढ़िवादी है।
रूबेन बार्टलिंक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.