92 votes

Comment aplatir un ExpandoObject renvoyé via JsonResult dans asp.net mvc?

J'aime vraiment l' ExpandoObject lors de la compilation d'un serveur-côté dynamique de l'objet au moment de l'exécution, mais j'ai du mal à l'aplatissement de cette chose lors de la sérialisation JSON. Tout d'abord, j'instancie l'objet:

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

So far So good. Dans mon contrôleur MVC, je veux, puis envoyer cette baisse comme un JsonResult, donc ce que je fais:

return new JsonResult(expando);

Cette sérialise le JSON dans le ci-dessous, pour être consommées par le navigateur:

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

MAIS, ce que j'aimerais vraiment c'est de voir ceci:

{SomeProp: SomeValueOrClass}

Je sais que je peux y arriver si j'utilise dynamic au lieu de ExpandoObject -- JsonResult est en mesure de sérialiser l' dynamic des propriétés et des valeurs dans un objet unique (pas de Clé ou de la Valeur d'entreprise), mais la raison pour laquelle j'ai besoin d'utiliser ExpandoObject c'est parce que je ne connais pas toutes les propriétés sur l'objet, jusqu'à exécution, et autant que je sache, je ne peux pas dynamiquement ajouter une propriété à un dynamic sans l'aide d'un ExpandoObject.

J'ai peut-être de passer au crible la "Clé", la "Valeur" des affaires dans mon javascript, mais je m'attendais à comprendre cela avant de l'envoyer au client. Merci pour votre aide!

66voto

Mikael Koskinen Points 2594

En utilisant JSON.NET, vous pouvez appeler SerializeObject pour "aplatir" l'objet expando:

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

var json = JsonConvert.SerializeObject(expando);
 

Est-ce que la sortie:

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

Dans le contexte d'un contrôleur ASP.NET MVC, le résultat peut être renvoyé à l'aide de la méthode 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");
    }
}
 

35voto

Pablo Rodda Donate Points 266

Vous pouvez également créer un convertisseur JSON spécial qui fonctionne uniquement pour ExpandoObject, puis l'enregistrer dans une instance de JavaScriptSerializer. De cette façon, vous pouvez sérialiser des tableaux de expando, des combinaisons d’objets expando et ... jusqu’à ce que vous trouviez un autre type d’objet qui ne soit pas sérialisé correctement ("comme vous le souhaitez"), puis vous créez un autre convertisseur ou vous ajoutez un autre type à. celui-là. J'espère que cela t'aides.

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

25voto

ajb Points 178

Voici ce que j'ai fait pour obtenir le comportement que vous décrivez:

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

Le coût est que vous effectuez une copie des données avant de les sérialiser.

10voto

TimDog Points 3875

J'ai résolu ce problème en écrivant une méthode d'extension qui convertit le ExpandoObject dans une chaîne 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();
}

Il utilise l'excellent Newtonsoft de la bibliothèque.

JsonResult ressemble alors à ceci:

return JsonResult(expando.Flatten());

Et c'est renvoyée au navigateur:

"{SomeProp: SomeValueOrClass}"

Et je peux l'utiliser en javascript en faisant cela (référencé ici):

var obj = JSON.parse(myJsonString);

J'espère que cela aide!

4voto

JustEngland Points 685

J'ai poussé le processus d'aplatissement un peu plus loin et vérifié les objets de la liste, ce qui supprime la valeur clé non-sens. :)

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X