99

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の代わりに使用すればこれを達成できることはわかっていますが、プロパティと値を単一のオブジェクトにシリアル化できます (キーまたは値のビジネスはありません)。ランタイムまでオブジェクトに必要なプロパティであり、私が知る限り、を使用せずにプロパティを動的に追加することはできません。ExpandoObjectJsonResultdynamicExpandoObjectdynamicExpandoObject

JavaScriptで「キー」、「値」のビジネスをふるいにかける必要があるかもしれませんが、クライアントに送信する前にこれを理解したいと思っていました。ご協力いただきありがとうございます!

4

12 に答える 12

72

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 コントローラーのコンテキストでは、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");
    }
}
于 2012-12-06T14:28:54.060 に答える
37

また、ExpandoObjectに対してのみ機能する特別なJSONConverterを作成し、それを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);
于 2011-05-06T14:48:17.260 に答える
27

あなたが説明している動作を達成するために私がしたことは次のとおりです。

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

コストは、シリアル化する前にデータのコピーを作成することです。

于 2011-11-28T18:48:23.310 に答える
11

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

これが役立つことを願っています!

于 2011-03-01T17:12:41.777 に答える
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();
    }
于 2011-04-09T18:35:26.040 に答える
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;
    }
}

ExpandoJsonResult クラスで、シリアル化に使用する方法を確認できます。逆シリアル化するには、同じ方法でシリアライザーを作成し、コンバーターを登録しますが、

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

ここで私を助けてくれたすべての参加者に感謝します。

于 2012-07-20T12:39:25.053 に答える
3

JsonResultJavaScriptSerializer必要に応じて実際に(コンクリートを)逆シリアル化するものを使用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 };

すべての組み込み型を使用した1行のコード:)

ネストされた ExpandoObject

もちろん、s をネストしている場合は、それらすべてを再帰的にsExpandoObjectに変換する必要があります。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);
于 2014-10-30T13:55:14.827 に答える
3

これは役に立たないかもしれませんが、同様の要件がありましたが、SerializableDynamicObject を使用しました

辞書の名前を「Fields」に変更し、これを Json.Net でシリアル化して、次のような json を生成します。

{"Fields":{"Property1":"Value1"、"Property2":"Value2" など。ここで、Property1 と Property2 は動的に追加されるプロパティです。つまり、ディクショナリ キーです。

残りをカプセル化する余分な「フィールド」プロパティを取り除くことができれば完璧ですが、その制限を回避しました。

リクエストに応じて、この質問から回答を移動しました

于 2012-07-19T07:55:22.670 に答える
1

ASP.Net 4 で WebApi から動的 ExpandoObject を返すことを使用すると、デフォルトの JSON フォーマッタは、ExpandoObjects を単純な JSON オブジェクトにフラット化するように見えます。

于 2013-09-12T15:18:01.090 に答える
-2

私はちょうど同じ問題を抱えていて、かなり奇妙なことを考え出しました。私が行った場合:

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

動作しますが、私のメソッドが HttpPost 属性を使用している場合のみです。HttpGet を使用すると、エラーが発生します。したがって、私の答えは HttpPost でのみ機能します。私の場合、それは Ajax 呼び出しだったので、HttpPost で HttpGet を変更できました。

于 2012-04-02T20:58:43.423 に答える
-2

シリアライザーが Expando をディクショナリにキャストしてからシリアライズしているようです (つまり、キー/バリュー ビジネス)。ディクショナリとしてデシリアライズしてから、それを Expando にキャストしてみましたか?

于 2011-03-01T15:47:54.873 に答える