JSONをASP.NETの単純なDictionary <string、string>に逆シリアル化するにはどうすればよいですか?


679

JSONの単純なキー/値リストがPOST経由でASP.NETに送り返されます。例:

{ "key1": "value1", "key2": "value2"}

厳密に型指定された.NETオブジェクトに逆シリアル化するつもりはありません

単純な古いDictionary(Of String、String)または同等のものが必要です(ハッシュテーブル、Dictionary(Of String、Object)、古い学校のStringDictionary-地獄、文字列の2次元配列が機能します。

ASP.NET 3.5で利用できるものなら何でも使用でき、人気のあるJson.NET(既にクライアントのシリアル化使用している)も使用できます。

どうやら、これらのJSONライブラリのどちらにも、この額を叩くような明白な機能はありません。それらは、強力なコントラクトを介したリフレクションベースの逆シリアル化に完全に集中しています。

何か案は?

制限:

  1. 自分のJSONパーサーを実装したくない
  2. ASP.NET 4.0はまだ使用できません
  3. JSONの古い非推奨のASP.NETクラスから離れた方がよい

1
re:制限3、JavaScriptSerizlizerASP.NET MVCで使用され、廃止されなくなりました。
bdukes '20年

17
json文字列を、さまざまなスタックオーバーフローを繰り返さずに簡単に使用できるものに変換する簡単な方法を見つけるのがいかに難しいかは、驚くべきことです。他の言語では非常に簡単ですが、JavaとC#は生活を困難にするために彼らの邪魔をするようです。
user299709 2016年

回答:


891

Json.NETはこれを行います...

string json = @"{""key1"":""value1"",""key2"":""value2""}";

var values = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);

その他の例:Json.NETを使用したコレクションのシリアル化


9
これは、値が整数の場合にも機能しますか?それらは自動的に「文字列」にキャストされますか?
ハイマストドン

58
@Highmastdonいいえ、ありません。辞書にデシリアライズするための最良の方法を使用することです、私を発見したdynamic:値の型としてJsonConvert.DeserializeObject<Dictionary<string, dynamic>>(json);
エリックSchierboom

1
このページでいくつかの答えを非常に厄介なキーと値のペアで試してみましたが、私が試したのはJSON.NETだけでした。
bnieland 2014年

15
あなたはJSONのキー値のペアの配列を使用している場合は仕事をしません[{key: "a", value: "1"}, {key: "b", value:"2"}]:あなたはこのような何かしなければならないvar dict = JsonConvert.DeserializeObject<List<KeyValuePair<string, string>>>(json);
エイドリアン

8
値がネストされたオブジェクトである場合も機能しません。json.netがそれらをJObjectsとして作成するためです
Kugel

100

.NET には3.5 アセンブリの型をDictionary<String, Object>介してJSON文字列をにキャストする組み込みの方法があることを発見しました。メソッドを使用します。System.Web.Script.Serialization.JavaScriptSerializerSystem.Web.ExtensionsDeserializeObject(String)

静的な.netページメソッドにコンテンツタイプ 'application / json'のajaxポスト(jqueryを介して)を実行しているときにこれに遭遇し、メソッド(タイプの単一パラメーターを持つObject)が魔法のようにこのディクショナリを受け取ったことを確認しました。


5
しかし、組み込みのjavascriptserializerはjson.netよりもバグが多いので、その方が優れています。たとえば、javascriptseralizerは空白の文字列の代わりにnullを返し、null許容プロパティに対してはまったく機能しません。
pilavdzice 2012

1
@pilavdzice MSの非標準の日付形式を想定しているため、日付を解析しようとするときの楽しみは言うまでもありません。
基本的な

16
簡単なコード例:var jsSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();続きDictionary<string, object> dict = (Dictionary<string, object>)jsSerializer.DeserializeObject(jsonString);
Nate Cook

6
シンプルなケースでのNate Cookの例の利点は、外部DLLの必要性を回避することです。.Netフレームワークにのみ依存できるスタンドアロンコンソールからAPIにアクセスしています。
Nick.T 2016年

@pilavdziceそれについてもっと詳しく説明してもらえますか?「空の文字列の代わりにnullを返す」ことを再現できません。これにより、空の文字列値が返されましたSomeData: ""
jrh

51

インターネットを検索してこの投稿を偶然見つけた人のために、JavaScriptSerializerクラスの使用方法に関するブログ投稿を書きました。

続きを読む... http://procbits.com/2011/04/21/quick-json-serializationdeserialization-in-c/

次に例を示します。

var json = "{\"id\":\"13\", \"value\": true}";
var jss = new JavaScriptSerializer();
var table = jss.Deserialize<dynamic>(json);
Console.WriteLine(table["id"]);
Console.WriteLine(table["value"]);

ええと、私はあなたの解決策を試しました...私はこのようなjsonを持っています{"id": "13"、 "value":true}そして私にとっては、Dictionary <dynamic>解決策のみが機能します
Marko

わかりました。問題が発生している場所を見つけました...正しくデシリアライズするには、辞書宣言の後に[]を追加する必要があります...ブログ投稿にもコメントを追加しています...乾杯;)
Marko

特定のデータセットを反映するように回答を更新しました。ダイナミックでうまく動作します。
JPリチャードソン、

:私はもう少し柔軟で、Silverlightをサポートする別のJSONパーサ書いprocbits.com/2011/08/11/...
JPリチャードソン

41

外部JSON実装を使用しないようにしたので、次のようにデシリアライズしました。

string json = "{\"id\":\"13\", \"value\": true}";

var serializer = new JavaScriptSerializer(); //using System.Web.Script.Serialization;

Dictionary<string, string> values = serializer.Deserialize<Dictionary<string, string>>(json);

6
参照System.Web.Extensionsを追加してSystem.Web.Scriptを使用する
Patrick Cullen

1
シンプルで.NETを使用しているので、私はこの回答を最も気に入っていますSystem.Web.Script.Serialization。うまくいきます。のような「無効な」JSONを使用することもできましたstring json = "{'id':13, 'value': true}";
スタイル

好奇心から、OrdinalIgnoreCase辞書に逆シリアル化する同じ方法はありますか?
batbaatar 2014

38

同じ問題があったので、これを自分で書きました。このソリューションは、複数のレベルに逆シリアル化できるため、他の回答とは異なります。

JSON文字列をdeserializeToDictionary関数に送信するだけで、厳密に型指定されていないDictionary<string, object>オブジェクトが返されます。

古いコード

private Dictionary<string, object> deserializeToDictionary(string jo)
{
    var values = JsonConvert.DeserializeObject<Dictionary<string, object>>(jo);
    var values2 = new Dictionary<string, object>();
    foreach (KeyValuePair<string, object> d in values)
    {
        // if (d.Value.GetType().FullName.Contains("Newtonsoft.Json.Linq.JObject"))
        if (d.Value is JObject)
        {
            values2.Add(d.Key, deserializeToDictionary(d.Value.ToString()));
        }
        else
        {
            values2.Add(d.Key, d.Value);
        }
    }
    return values2;
}

例:これはDictionary<string, object>Facebook JSON応答のオブジェクトを返します。

テスト

private void button1_Click(object sender, EventArgs e)
{
    string responsestring = "{\"id\":\"721055828\",\"name\":\"Dasun Sameera Weerasinghe\",\"first_name\":\"Dasun\",\"middle_name\":\"Sameera\",\"last_name\":\"Weerasinghe\",\"username\":\"dasun\",\"gender\":\"male\",\"locale\":\"en_US\",  hometown: {id: \"108388329191258\", name: \"Moratuwa, Sri Lanka\",}}";
    Dictionary<string, object> values = deserializeToDictionary(responsestring);
}

注:故郷はさらにDictionary<string, object> オブジェクトに非滅菌されます。

更新

JSON文字列に配列がない場合、私の古い答えはうまくいきます。これList<object>は、要素が配列の場合、さらに直列化復元されます。

JSON文字列をdeserializeToDictionaryOrList関数に送信するだけで、厳密に型指定されていないDictionary<string, object>オブジェクトまたはが返されますList<object>

private static object deserializeToDictionaryOrList(string jo,bool isArray=false)
{
    if (!isArray)
    {
        isArray = jo.Substring(0, 1) == "[";
    }
    if (!isArray)
    {
        var values = JsonConvert.DeserializeObject<Dictionary<string, object>>(jo);
        var values2 = new Dictionary<string, object>();
        foreach (KeyValuePair<string, object> d in values)
        {
            if (d.Value is JObject)
            {
                values2.Add(d.Key, deserializeToDictionary(d.Value.ToString()));
            }
            else if (d.Value is JArray)
            {
                values2.Add(d.Key, deserializeToDictionary(d.Value.ToString(), true));
            }
            else
            {
                values2.Add(d.Key, d.Value);
            }
        }
        return values2;
    }else
    {
        var values = JsonConvert.DeserializeObject<List<object>>(jo);
        var values2 = new List<object>();
        foreach (var d in values)
        {
            if (d is JObject)
            {
                values2.Add(deserializeToDictionary(d.ToString()));
            }
            else if (d is JArray)
            {
                values2.Add(deserializeToDictionary(d.ToString(), true));
            }
            else
            {
                values2.Add(d);
            }
        }
        return values2;
    }
}

@ジョーダンは指摘してくれてありがとう、私はこのコードにいくつかの変更を加えましたが、今は持っていません。このコードはJArrayオブジェクトを処理しません。取得したらコードを更新します。
Dasun

1
問題ない。isand as演算子について学ぶことは私を大いに助けてくれ、自分のコードを単純化したので、私はそれだけに言及します。
ヨルダン

これは機能しますが、効率的ではありません。ToStringを呼び出してから、再度デシリアライズします。以下のファルコの答えを見てください。ソース文字列は1回だけ逆シリアル化されます。
Sergei Zinovyev 2017年

1
Falkoの答えは、事前にデータ構造を知っている場合にのみ機能します。このソリューションは、任意のJSON文字列に使用できます。
Dasun

16

軽量で参照が追加されていない種類のアプローチを使用している場合は、おそらく、先ほど書いたこのコードが機能します(ただし、堅牢性を100%保証することはできません)。

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

public Dictionary<string, object> ParseJSON(string json)
{
    int end;
    return ParseJSON(json, 0, out end);
}
private Dictionary<string, object> ParseJSON(string json, int start, out int end)
{
    Dictionary<string, object> dict = new Dictionary<string, object>();
    bool escbegin = false;
    bool escend = false;
    bool inquotes = false;
    string key = null;
    int cend;
    StringBuilder sb = new StringBuilder();
    Dictionary<string, object> child = null;
    List<object> arraylist = null;
    Regex regex = new Regex(@"\\u([0-9a-z]{4})", RegexOptions.IgnoreCase);
    int autoKey = 0;
    for (int i = start; i < json.Length; i++)
    {
        char c = json[i];
        if (c == '\\') escbegin = !escbegin;
        if (!escbegin)
        {
            if (c == '"')
            {
                inquotes = !inquotes;
                if (!inquotes && arraylist != null)
                {
                    arraylist.Add(DecodeString(regex, sb.ToString()));
                    sb.Length = 0;
                }
                continue;
            }
            if (!inquotes)
            {
                switch (c)
                {
                    case '{':
                        if (i != start)
                        {
                            child = ParseJSON(json, i, out cend);
                            if (arraylist != null) arraylist.Add(child);
                            else
                            {
                                dict.Add(key, child);
                                key = null;
                            }
                            i = cend;
                        }
                        continue;
                    case '}':
                        end = i;
                        if (key != null)
                        {
                            if (arraylist != null) dict.Add(key, arraylist);
                            else dict.Add(key, DecodeString(regex, sb.ToString()));
                        }
                        return dict;
                    case '[':
                        arraylist = new List<object>();
                        continue;
                    case ']':
                        if (key == null)
                        {
                            key = "array" + autoKey.ToString();
                            autoKey++;
                        }
                        if (arraylist != null && sb.Length > 0)
                        {
                            arraylist.Add(sb.ToString());
                            sb.Length = 0;
                        }
                        dict.Add(key, arraylist);
                        arraylist = null;
                        key = null;
                        continue;
                    case ',':
                        if (arraylist == null && key != null)
                        {
                            dict.Add(key, DecodeString(regex, sb.ToString()));
                            key = null;
                            sb.Length = 0;
                        }
                        if (arraylist != null && sb.Length > 0)
                        {
                            arraylist.Add(sb.ToString());
                            sb.Length = 0;
                        }
                       continue;
                    case ':':
                        key = DecodeString(regex, sb.ToString());
                        sb.Length = 0;
                        continue;
                }
            }
        }
        sb.Append(c);
        if (escend) escbegin = false;
        if (escbegin) escend = true;
        else escend = false;
    }
    end = json.Length - 1;
    return dict; //theoretically shouldn't ever get here
}
private string DecodeString(Regex regex, string str)
{
    return Regex.Unescape(regex.Replace(str, match => char.ConvertFromUtf32(Int32.Parse(match.Groups[1].Value, System.Globalization.NumberStyles.HexNumber))));
}

[私はこれがOP制限#1に違反していることを理解していますが、技術的には、あなたはそれを書かなかったと思います]


3
これがSilverlightで機能し、依存関係がない唯一の答えです。Silverlightには、JavascriptSerializerまたはSerializableがありません。依存関係がないということは、Json.NET、RestSharp、MiniJSONがないことを意味します。@DanCsharpsterだけが別の可能な解決策を試しましたが、残念ながら、この解決策のように機能しませんでした。
・クール

1
JSON.NETのような単純なものへの参照を追加することの何が問題になっていますか?何も参照できないほど軽量である必要がありますか?あなたのコードが機能しないと言っているわけではありませんが、あなたが自分でコードをロールバックするときはいつでも、エッジケースのようなものに対してコードが堅牢でなかったり、JSON.NETのようなテスト済みのライブラリのように高速ではないリスクを明らかに抱えています。
Dan Csharpster、2014

1
あなたが良い代替手段を持っているとき、あなた自身のローリングは悪い考えです。私がしなければならいない状況を知ることで軽量。そして、私はむしろ、読みやすく、変更しやすい、あまり最適ではないコードが欲しいです。
ヨルダン

3
私が最初にそのコードを書いたのは、代替手段がなかったからです。Silverlightや、Office製品のさまざまな種類のプロバイダーなどを検討してください。プロジェクトへの外部参照の追加は、非常に問題があるか不可能です。
dexy

その数年後のことは知っていますが、これは依然として非常に有効な質問です。SQL CLR C#を使用している場合、なぜ軽量にしたいのかと疑問に思う方は、使用できる「安全な」ライブラリは非常に多くSystem.RunTime.Serialization、そのうちの1つではありませんが、残念ながらJSON.NETはそのため、使用することもできません。卓越した仕事をしてくれたdexyに感謝します。あえて配列の配列を逆シリアル化できるように少し改善しました。私の回答の更新されたコードをここで参照してください
アルベルトレシー

15

私は入れ子になった辞書を解析する必要がありまし

{
    "x": {
        "a": 1,
        "b": 2,
        "c": 3
    }
}

JsonConvert.DeserializeObject助けにならないところ。私は次のアプローチを見つけました:

var dict = JObject.Parse(json).SelectToken("x").ToObject<Dictionary<string, int>>();

SelectTokenあなたが目的のフィールドに掘り下げることができます。"x.y.z"JSONオブジェクトにさらにステップダウンするようなパスを指定することもできます。


JObject.Parse(json).ToObject <Dictionary <Guid、List <int >>>()が私のシナリオで機能しました感謝
geedubb

11

System.Text.Json

これSystem.Text.Jsonは、に組み込まれているを使用して行うことができます.net core 3.0。サードパーティのライブラリ使用せずに JSONをデシリアライズできるようになりました。

var json = @"{""key1"":""value1"",""key2"":""value2""}";
var values = JsonSerializer.Deserialize<Dictionary<string, string>>(json);

.Net Standardまたは.Net Frameworkを使用している場合は、nu-getパッケージSystem.Text.Jsonでも利用できます。


1
はい!System.Text.Json最近行く方法です。
mfluehr

2
はい、それは有望に見えます!ただし、.NET Core 3.1のデフォルトバージョンのSystem.Text.Jsonは、非文字列キーを使用した辞書の逆シリアル化をサポートしていないことに注意してください。私のOPは文字列に関するものでしたが、実際には現在、Guidキーはたくさんあります。そのため、切り替えを行おうとすると、「ビット」します。また、一部の属性(必須など)に相当するものもありません。
richardtallent

6

Mark Rendleがこれをコメントとして投稿しました。これは、成功を返し、Google reCaptcha応答からのエラーコードjson結果を返すためにこれまでに機能した唯一のソリューションであるため、回答として投稿したかったのです。

string jsonReponseString= wClient.DownloadString(requestUrl);    
IDictionary<string, object> dict = new JavaScriptSerializer().DeserializeObject(jsonReponseString) as IDictionary<string, object>;

再びありがとう、マーク!


1
JavaScriptSerializerはほとんど使用されていません。ドキュメントには、JSON.NET(docs.microsoft.com/en-us/dotnet/api/…)を使用する必要があると記載されています
Mario Lopez

追加の依存関係を含めたくないレガシーWebフォームアプリにも適しています。
Andrew Grothe

5

編集:これは機能しますが、Json.NETを使用して受け入れられた答えははるかに簡単です。誰かがBCLのみのコードを必要とする場合に備えて、これを残します。

そのままでは.NETフレームワークではサポートされていません。明白な見落とし–誰もが名前付きプロパティを持つオブジェクトに逆シリアル化する必要があるわけではありません。だから私は自分自身を転がしてしまった:

<Serializable()> Public Class StringStringDictionary
    Implements ISerializable
    Public dict As System.Collections.Generic.Dictionary(Of String, String)
    Public Sub New()
        dict = New System.Collections.Generic.Dictionary(Of String, String)
    End Sub
    Protected Sub New(info As SerializationInfo, _
          context As StreamingContext)
        dict = New System.Collections.Generic.Dictionary(Of String, String)
        For Each entry As SerializationEntry In info
            dict.Add(entry.Name, DirectCast(entry.Value, String))
        Next
    End Sub
    Public Sub GetObjectData(info As SerializationInfo, context As StreamingContext) Implements ISerializable.GetObjectData
        For Each key As String in dict.Keys
            info.AddValue(key, dict.Item(key))
        Next
    End Sub
End Class

と呼ばれる:

string MyJsonString = "{ \"key1\": \"value1\", \"key2\": \"value2\"}";
System.Runtime.Serialization.Json.DataContractJsonSerializer dcjs = new
  System.Runtime.Serialization.Json.DataContractJsonSerializer(
    typeof(StringStringDictionary));
System.IO.MemoryStream ms = new
  System.IO.MemoryStream(Encoding.UTF8.GetBytes(MyJsonString));
StringStringDictionary myfields = (StringStringDictionary)dcjs.ReadObject(ms);
Response.Write("Value of key2: " + myfields.dict["key2"]);

C#とVB.NETの組み合わせでごめんなさい…


2
[TestMethod] public void TestSimpleObject(){const string json = @ "{" "Name" ":" "Bob" "、" "Age" ":42}"; var dict = new JavaScriptSerializer()。DeserializeObject(json)as IDictionary <string、object>; Assert.IsNotNull(dict); Assert.IsTrue(dict.ContainsKey( "Name")); Assert.AreEqual( "Bob"、dict ["Name"]); Assert.IsTrue(dict.ContainsKey( "Age")); Assert.AreEqual(42、dict ["Age"]); }
Mark Rendle

1
これは素晴らしいです。ブラウザベースのクライアントでJSONを使用してインターフェイスするWCFサービスの実装を支援します。
アントン

@Mark Rendle:あなたの実装は非常に単純で、これまでに成功とエラーコードのjson結果の両方を取得するのに私のために働いた唯一のものです。私は多くの解決策を試しましたので、コメントとして投稿していただきありがとうございます。それが答えになるはずです。
ブライアン

5

ここにjSnake04とDasunによって提出されたコードを追加しました。JArrayインスタンスからオブジェクトのリストを作成するコードを追加しました。双方向の再帰がありますが、固定された有限ツリーモデルで機能するため、データが大量でない限り、スタックオーバーフローのリスクはありません。

/// <summary>
/// Deserialize the given JSON string data (<paramref name="data"/>) into a
///   dictionary.
/// </summary>
/// <param name="data">JSON string.</param>
/// <returns>Deserialized dictionary.</returns>
private IDictionary<string, object> DeserializeData(string data)
{
    var values = JsonConvert.DeserializeObject<Dictionary<string, object>>(data);

    return DeserializeData(values);
}

/// <summary>
/// Deserialize the given JSON object (<paramref name="data"/>) into a dictionary.
/// </summary>
/// <param name="data">JSON object.</param>
/// <returns>Deserialized dictionary.</returns>
private IDictionary<string, object> DeserializeData(JObject data)
{
    var dict = data.ToObject<Dictionary<String, Object>>();

    return DeserializeData(dict);
}

/// <summary>
/// Deserialize any elements of the given data dictionary (<paramref name="data"/>) 
///   that are JSON object or JSON arrays into dictionaries or lists respectively.
/// </summary>
/// <param name="data">Data dictionary.</param>
/// <returns>Deserialized dictionary.</returns>
private IDictionary<string, object> DeserializeData(IDictionary<string, object> data)
{
    foreach (var key in data.Keys.ToArray()) 
    {
        var value = data[key];

        if (value is JObject)
            data[key] = DeserializeData(value as JObject);

        if (value is JArray)
            data[key] = DeserializeData(value as JArray);
    }

    return data;
}

/// <summary>
/// Deserialize the given JSON array (<paramref name="data"/>) into a list.
/// </summary>
/// <param name="data">Data dictionary.</param>
/// <returns>Deserialized list.</returns>
private IList<Object> DeserializeData(JArray data)
{
    var list = data.ToObject<List<Object>>();

    for (int i = 0; i < list.Count; i++)
    {
        var value = list[i];

        if (value is JObject)
            list[i] = DeserializeData(value as JObject);

        if (value is JArray)
            list[i] = DeserializeData(value as JArray);
    }

    return list;
}

4

JSONのnull値のチェックを他の回答に追加しました

同じ問題があったので、自分で書きました。このソリューションは、複数のレベルに逆シリアル化できるため、他の回答とは異なります。

json文字列をdeserializeToDictionary関数に送信するだけで、厳密に型指定されていないDictionary<string, object>オブジェクトが返されます。

private Dictionary<string, object> deserializeToDictionary(string jo)
{
    var values = JsonConvert.DeserializeObject<Dictionary<string, object>>(jo);
    var values2 = new Dictionary<string, object>();
    foreach (KeyValuePair<string, object> d in values)
    {
        if (d.Value != null && d.Value.GetType().FullName.Contains("Newtonsoft.Json.Linq.JObject"))
        {
            values2.Add(d.Key, deserializeToDictionary(d.Value.ToString()));
        }
        else
        {
            values2.Add(d.Key, d.Value);
        }
    }
    return values2;
}

例:これはDictionary<string, object>Facebook JSON応答のオブジェクトを返します。

private void button1_Click(object sender, EventArgs e)
{
    string responsestring = "{\"id\":\"721055828\",\"name\":\"Dasun Sameera
        Weerasinghe\",\"first_name\":\"Dasun\",\"middle_name\":\"Sameera\",\"last_name\":\"Weerasinghe\",\"username\":\"dasun\",\"gender\":\"male\",\"locale\":\"en_US\",
        hometown: {id: \"108388329191258\", name: \"Moratuwa, Sri Lanka\",}}";
    Dictionary<string, object> values = deserializeToDictionary(responsestring);
}

注:出身地はさらにDictionary<string, object>オブジェクトに逆シリアル化します。


1
+1上記のダスンで述べたように。あなただけかどうかを確認することができd.Value is JObjectます。タイプをチェックするためにリフレクションを実行する必要はありません。そして、とisオペレータあなたはnullをチェックする必要はありません。オブジェクトがnullの場合はfalseを返します。
ジョーダン

3

これらすべての答えは、大きなオブジェクトからその小さな文字列を取得できると想定しているようです...マッピング内のどこかにそのようなディクショナリで単純に大きなオブジェクトを非現実化することを望んでいる人、およびSystem.Runtime.Serialization.JsonDataContractシステムを使用している人のために、ここにあります解決策:

gis.stackexchange.comへの回答には、この興味深いリンクがありました。私はarchive.orgでそれを回復する必要がありましたが、それはかなり完璧な解決策を提供します:IDataContractSurrogateあなたが正確にあなた自身の型を実装するカスタムクラスです。簡単に拡張できました。

でも、たくさんの変更を加えました。元のソースが利用できなくなったため、クラス全体をここに投稿します。

using System;
using System.CodeDom;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;

namespace JsonTools
{
    /// <summary>
    /// Allows using Dictionary&lt;String,String&gt; and Dictionary&lt;String,Boolean&gt; types, and any others you'd like to add.
    /// Source: https://web.archive.org/web/20100317222656/my6solutions.com/post/2009/06/30/DataContractSerializer-DataContractJsonSerializer-JavaScriptSerializer-XmlSerializer-for-serialization.aspx
    /// </summary>
    public class JsonSurrogate : IDataContractSurrogate
    {
        /// <summary>
        /// Deserialize an object with added support for the types defined in this class.
        /// </summary>
        /// <typeparam name="T">Contract class</typeparam>
        /// <param name="json">JSON String</param>
        /// <param name="encoding">Text encoding</param>
        /// <returns>The deserialized object of type T</returns>
        public static T Deserialize<T>(String json, Encoding encoding)
        {
            if (encoding == null)
                encoding = new UTF8Encoding(false);
            DataContractJsonSerializer deserializer = new DataContractJsonSerializer(
                typeof(T), new Type[0], int.MaxValue, true, new JsonSurrogate(), false);
            using (MemoryStream stream = new MemoryStream(encoding.GetBytes(json)))
            {
                T result = (T)deserializer.ReadObject(stream);
                return result;
            }
        }

        // make sure all values in this are classes implementing JsonSurrogateObject.
        private static Dictionary<Type, Type> KnownTypes = 
            new Dictionary<Type, Type>()
            {
                {typeof(Dictionary<String, String>), typeof(SSDictionary)},
                {typeof(Dictionary<String, Boolean>), typeof(SBDictionary)}
            };

        #region Implemented surrogate dictionary classes

        [Serializable]
        public class SSDictionary : SurrogateDictionary<String>
        {
            public SSDictionary() : base() {}
            protected SSDictionary (SerializationInfo info, StreamingContext context) : base(info, context) {}
        }
        [Serializable]
        public class SBDictionary : SurrogateDictionary<Boolean>
        {
            public SBDictionary() : base() {}
            protected SBDictionary (SerializationInfo info, StreamingContext context) : base(info, context) {}
        }

        #endregion

        /// <summary>Small interface to easily extract the final value from the object.</summary>
        public interface JsonSurrogateObject
        {
            Object DeserializedObject { get; }
        }

        /// <summary>
        /// Class for deserializing any simple dictionary types with a string as key.
        /// </summary>
        /// <typeparam name="T">Any simple type that will be deserialized correctly.</typeparam>
            [Serializable]
        public abstract class SurrogateDictionary<T> : ISerializable, JsonSurrogateObject
        {
            public Object DeserializedObject { get { return dict; } }
            private Dictionary<String, T> dict;

            public SurrogateDictionary()
            {
                dict = new Dictionary<String, T>();
            }

            // deserialize
            protected SurrogateDictionary(SerializationInfo info, StreamingContext context)
            {
                dict = new Dictionary<String, T>();
                foreach (SerializationEntry entry in info)
                {
                    // This cast will only work for base types, of course.
                    dict.Add(entry.Name, (T)entry.Value);
                }
            }
            // serialize
            public void GetObjectData(SerializationInfo info, StreamingContext context)
            {
                foreach (String key in dict.Keys)
                {
                    info.AddValue(key, dict[key]);
                }
            }

        }

        /// <summary>
            /// Uses the KnownTypes dictionary to get the surrogate classes.
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public Type GetDataContractType(Type type)
        {
            Type returnType;
            if (KnownTypes.TryGetValue(type, out returnType))
            {
                return returnType;
            }
            return type;
        }

        public object GetObjectToSerialize(object obj, Type targetType)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        ///     Gets the object out of the surrogate datacontract object. This function is the reason all surrogate objects need to implement the JsonSurrogateObject class.
        /// </summary>
        /// <param name="obj">Result of the deserialization</param>
        /// <param name="targetType">Expected target type of the deserialization</param>
        /// <returns></returns>
        public object GetDeserializedObject(object obj, Type targetType)
        {
            if (obj is JsonSurrogateObject)
            {
                return ((JsonSurrogateObject)obj).DeserializedObject;
            }
            return obj;
        }

        public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
        {
            return null;
        }

        #region not implemented

        public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)
        {
            throw new NotImplementedException();
        }

        public object GetCustomDataToExport(Type clrType, Type dataContractType)
        {
            throw new NotImplementedException();
        }

        public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
        {
            throw new NotImplementedException();
        }

        public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit)
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}

新しいサポートされている型をクラスに追加するには、クラスを追加し、それに適切なコンストラクターと関数を与え(SurrogateDictionary例を参照)、クラスがを継承していることを確認しJsonSurrogateObject、型マッピングをKnownTypesディクショナリに追加するだけです。含まれているSurrogateDictionaryはDictionary<String,T>、Tが正しく逆シリアル化される任意の型である任意の型の基礎として機能します。

それを呼び出すことは本当に簡単です:

MyObjtype newObj = JsonSurrogate.Deserialize<MyObjtype>(jsonStr, encoding);

なんらかの理由で、これにはスペースを含むキー文字列の使用に問題があることに注意してください。それらは単に最終的なリストに含まれていませんでした。単にjsonの仕様に反していて、私が呼んでいたapiの実装が不十分だったかもしれません。私は知らないよ。とにかく、生のjsonデータでアンダースコアをregexに置き換え、逆シリアル化後に辞書を修正することで、これを解決しました。


ちなみに、Monoはいくつかの奇妙な理由により、これを実行するのに問題があるようです...
Nyerguds

共有していただきありがとうございます。残念ながら、このソリューションは非プリミティブ型をサポートしておらず、生の値を取得する方法がないため、自分で構築できます。カスタムタイプをKnownTypesに登録し、それをディクショナリで使用すると、最初にディクショナリが呼び出され、最もリモートのタイプからより複雑なタイプへと、下から上へと解析が開始されると予想されます。
Ivan Leonenko

さて、質問はについてのみ尋ねたDictionary<String,String>。私は正直に言って、このシステムで複雑な型を逆シリアル化しようとしたことはありません。
Nyerguds

3

上記のコメントに基づいてみてくださいJsonConvert.DeserializeObject<Dictionary<string,dynamic>>(json)

var json = @"{""key1"":1,""key2"":""value2"", ""object1"":{""property1"":""value1"",""property2"":[2,3,4,5,6,7]}}";
var parsedObject = JsonConvert.DeserializeObject<Dictionary<string,dynamic>>(json);

複雑なオブジェクトやリストでも機能するようです。


1

これをRestSharpに実装しましたこの投稿は役に立ちました。

リンクのコードの他に、ここに私のコードがあります。私は今、Dictionaryこのようなことをすると結果を得ます:

var jsonClient = new RestClient(url.Host);
jsonClient.AddHandler("application/json", new DynamicJsonDeserializer());
var jsonRequest = new RestRequest(url.Query, Method.GET);
Dictionary<string, dynamic> response = jsonClient.Execute<JObject>(jsonRequest).Data.ToObject<Dictionary<string, dynamic>>();

あなたが期待しているJSONの種類に注意してください-私の場合、私はいくつかのプロパティを持つ単一のオブジェクトを取得していました。添付のリンクで、作成者はリストを取得していました。


1

私のアプローチは、JObjectやExpandObjectを介さずにIDictionaryに直接逆シリアル化します。コードはコンバーターを使用します。コンバーターは基本的にJSON.NETソースコードにあるExpandoObjectConverterクラスからコピーされますが、ExpandoObjectの代わりにIDictionaryを使用します。

使用法:

var settings = new JsonSerializerSettings()
{
    Converters = { new DictionaryConverter() },
};
var result = JsonConvert.DeserializeObject<IDictionary<string, object>>(json, settings);

コード:

// based on ExpandoObjectConverter, but using arrays instead of IList, to behave similar to System.Web.Script.Serialization.JavaScriptSerializer
public class DictionaryConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        return ReadValue(reader);
    }

    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(IDictionary<string, object>));
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    private object ReadValue(JsonReader reader)
    {
        while (reader.TokenType == JsonToken.Comment)
        {
            if (!reader.Read())
                throw JsonSerializationExceptionCreate(reader, "Unexpected end when reading IDictionary<string, object>.");
        }

        switch (reader.TokenType)
        {
            case JsonToken.StartObject:
                return ReadObject(reader);
            case JsonToken.StartArray:
                return ReadList(reader);
            default:
                if (IsPrimitiveToken(reader.TokenType))
                    return reader.Value;

                throw JsonSerializationExceptionCreate(reader, string.Format(CultureInfo.InvariantCulture, "Unexpected token when converting IDictionary<string, object>: {0}", reader.TokenType));
        }
    }

    private object ReadList(JsonReader reader)
    {
        List<object> list = new List<object>();

        while (reader.Read())
        {
            switch (reader.TokenType)
            {
                case JsonToken.Comment:
                    break;
                default:
                    object v = ReadValue(reader);

                    list.Add(v);
                    break;
                case JsonToken.EndArray:
                    return list;
            }
        }

        throw JsonSerializationExceptionCreate(reader, "Unexpected end when reading IDictionary<string, object>.");
    }

    private object ReadObject(JsonReader reader)
    {
        IDictionary<string, object> dictionary = new Dictionary<string, object>();
        while (reader.Read())
        {
            switch (reader.TokenType)
            {
                case JsonToken.PropertyName:
                    string propertyName = reader.Value.ToString();

                    if (!reader.Read())
                        throw JsonSerializationExceptionCreate(reader, "Unexpected end when reading IDictionary<string, object>.");

                    object v = ReadValue(reader);

                    dictionary[propertyName] = v;
                    break;
                case JsonToken.Comment:
                    break;
                case JsonToken.EndObject:
                    return dictionary;
            }
        }

        throw JsonSerializationExceptionCreate(reader, "Unexpected end when reading IDictionary<string, object>.");
    }

    //based on internal Newtonsoft.Json.JsonReader.IsPrimitiveToken
    internal static bool IsPrimitiveToken(JsonToken token)
    {
        switch (token)
        {
            case JsonToken.Integer:
            case JsonToken.Float:
            case JsonToken.String:
            case JsonToken.Boolean:
            case JsonToken.Undefined:
            case JsonToken.Null:
            case JsonToken.Date:
            case JsonToken.Bytes:
                return true;
            default:
                return false;
        }
    }

    // based on internal Newtonsoft.Json.JsonSerializationException.Create
    private static JsonSerializationException JsonSerializationExceptionCreate(JsonReader reader, string message, Exception ex = null)
    {
        return JsonSerializationExceptionCreate(reader as IJsonLineInfo, reader.Path, message, ex);
    }

    // based on internal Newtonsoft.Json.JsonSerializationException.Create
    private static JsonSerializationException JsonSerializationExceptionCreate(IJsonLineInfo lineInfo, string path, string message, Exception ex)
    {
        message = JsonPositionFormatMessage(lineInfo, path, message);

        return new JsonSerializationException(message, ex);
    }

    // based on internal Newtonsoft.Json.JsonPosition.FormatMessage
    internal static string JsonPositionFormatMessage(IJsonLineInfo lineInfo, string path, string message)
    {
        if (!message.EndsWith(Environment.NewLine))
        {
            message = message.Trim();

            if (!message.EndsWith(".", StringComparison.Ordinal))
                message += ".";

            message += " ";
        }

        message += string.Format(CultureInfo.InvariantCulture, "Path '{0}'", path);

        if (lineInfo != null && lineInfo.HasLineInfo())
            message += string.Format(CultureInfo.InvariantCulture, ", line {0}, position {1}", lineInfo.LineNumber, lineInfo.LinePosition);

        message += ".";

        return message;
    }
}

1

あなたはTiny-JSONを使うことができます

string json = "{\"key1\":\"value1\", \"key2\":\"value2\"}";
IDictionary<string, string> dict = Tiny.Json.Decode<Dictionary<string, string>>(json);

1

ゲームには少し遅れましたが、上記のいずれのソリューションも、json.netソリューションではなく、純粋でシンプルな.NETの方向を示しました。ですから、これは非常にシンプルなものになりました。標準の.NET Jsonシリアル化でどのように実行されるかを示す完全な実行例の下で、この例には、ルートオブジェクトと子オブジェクトの両方に辞書があります。

黄金の弾丸はこの猫で、シリアライザへの2番目のパラメータとして設定を解析します。

DataContractJsonSerializerSettings settings =
                       new DataContractJsonSerializerSettings();
                    settings.UseSimpleDictionaryFormat = true;

以下の完全なコード:

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;

    namespace Kipon.dk
    {
        public class JsonTest
        {
            public const string EXAMPLE = @"{
                ""id"": ""some id"",
                ""children"": {
                ""f1"": {
                    ""name"": ""name 1"",
                    ""subs"": {
                    ""1"": { ""name"": ""first sub"" },
                    ""2"": { ""name"": ""second sub"" }
                    }
                },
                ""f2"": {
                    ""name"": ""name 2"",
                    ""subs"": {
                    ""37"": { ""name"":  ""is 37 in key""}
                    }
                }
                }
            }
            ";

            [DataContract]
            public class Root
            {
                [DataMember(Name ="id")]
                public string Id { get; set; }

                [DataMember(Name = "children")]
                public Dictionary<string,Child> Children { get; set; }
            }

            [DataContract]
            public class Child
            {
                [DataMember(Name = "name")]
                public string Name { get; set; }

                [DataMember(Name = "subs")]
                public Dictionary<int, Sub> Subs { get; set; }
            }

            [DataContract]
            public class Sub
            {
                [DataMember(Name = "name")]
                public string Name { get; set; }
            }

            public static void Test()
            {
                var array = System.Text.Encoding.UTF8.GetBytes(EXAMPLE);
                using (var mem = new System.IO.MemoryStream(array))
                {
                    mem.Seek(0, System.IO.SeekOrigin.Begin);
                    DataContractJsonSerializerSettings settings =
                       new DataContractJsonSerializerSettings();
                    settings.UseSimpleDictionaryFormat = true;

                    var ser = new DataContractJsonSerializer(typeof(Root), settings);
                    var data = (Root)ser.ReadObject(mem);
                    Console.WriteLine(data.Id);
                    foreach (var childKey in data.Children.Keys)
                    {
                        var child = data.Children[childKey];
                        Console.WriteLine(" Child: " + childKey + " " + child.Name);
                        foreach (var subKey in child.Subs.Keys)
                        {
                            var sub = child.Subs[subKey];
                            Console.WriteLine("   Sub: " + subKey + " " + sub.Name);
                        }
                    }
                }
            }
        }
    }

0

厄介なことに、デフォルトのモデルバインダーを使用する場合、フォームPOSTのような数値のインデックス値を使用する必要があるように見えます。

この記事http://msdn.microsoft.com/en-us/magazine/hh781022.aspxからの次の抜粋を参照してください

多少直観に反しますが、JSONリクエストにも同じ要件があります。JSONリクエストもフォームのポスト命名構文に準拠する必要があります。たとえば、前のUnitPriceコレクションのJSONペイロードを見てみましょう。このデータの純粋なJSON配列構文は次のように表されます。

[ 
  { "Code": "USD", "Amount": 100.00 },
  { "Code": "EUR", "Amount": 73.64 }
]

ただし、デフォルト値プロバイダーとモデルバインダーでは、データをJSONフォームポストとして表す必要があります。

{
  "UnitPrice[0].Code": "USD",
  "UnitPrice[0].Amount": 100.00,

  "UnitPrice[1].Code": "EUR",
  "UnitPrice[1].Amount": 73.64
}

構文が必ずしもすべての開発者に明らかであるとは限らないため、複雑なオブジェクトコレクションシナリオは、おそらく開発者が遭遇する最も広く問題のあるシナリオの1つです。ただし、複雑なコレクションを投稿するための比較的単純な構文を学習すると、これらのシナリオははるかに扱いやすくなります。


0

System.Runtime.Serialization.Json.NET 4.5の一部として使用することをお勧めします。

[DataContract]
public class Foo
{
   [DataMember(Name = "data")]
   public Dictionary<string,string> Data { get; set; }
}

次に、次のように使用します。

var serializer = new DataContractJsonSerializer(typeof(List<Foo>));
var jsonParams = @"{""data"": [{""Key"":""foo"",""Value"":""bar""}] }";
var stream = new MemoryStream(Encoding.UTF8.GetBytes(jsonParams));

var obj = serializer.ReadObject(stream);
Console.WriteLine(obj);

シリアライザはどこで定義されていますか?
bnieland 2014年

..そしてCategory3MeasureModelとは何ですか?Googleにヒットはありません。
bnieland 2014年

1
これは、プロジェクトでシリアル化しているモデルクラスです。それはそのFooクラスであるはずですが、私はセクション全体を量産コードから再コピーしました。私のFooクラスのように、自分で作成する必要があります。簡単にするために、名前をFooに変更しました。これは、jsonにシリアル化して戻したいプロパティまたはフィールドのクラスです。
Dan Csharpster 2014年

1
@DanCsharpster Windows Phone 8.1 Silverlightで、コードの正確なコピーを使用すると、「System.ServiceModel.Web.ni.dllで 'System.Security.SecurityException'タイプの例外が発生しましたが、ユーザーで処理されませんでしたコード追加情報:メンバー 'Data'がパブリックではないため、データコントラクトタイプ 'MyApp.Foo'を逆シリアル化できません。メンバーを公開すると、このエラーが修正されます。また、あなたはそれが内部行い、内部メンバーのシリアライズを有効にするために、あなたのアセンブリにInternalsVisibleToAttribute属性を使用することができます
・クール

1
@DanCsharpsterそして、プロパティDataをメンバーになるように変更すると(get; set;なしで)、次のようになります。「System.ArgumentException」タイプの最初のチャンス例外がSystem.ServiceModel.Web.ni.dllで発生しましたタイプ 'System.Object'はタイプ 'System.Collections.Generic.Dictionary`2 [System.String、System.String]'に変換できません。
・クール

0

JSONから値を取得するためだけにJSONを辞書に変換しようとしている人のために。使用する簡単な方法がありますNewtongsoft.JSON

using Newtonsoft.Json.Linq
...

JObject o = JObject.Parse(@"{
  'CPU': 'Intel',
  'Drives': [
    'DVD read/writer',
    '500 gigabyte hard drive'
  ]
}");

string cpu = (string)o["CPU"];
// Intel

string firstDrive = (string)o["Drives"][0];
// DVD read/writer

IList<string> allDrives = o["Drives"].Select(t => (string)t).ToList();
// DVD read/writer
// 500 gigabyte hard drive
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.