كيف يمكنني إلغاء تسلسل JSON إلى قاموس بسيط<string ، string> في ASP.NET؟




asp.net-ajax asp.net-3.5 (14)

لدي قائمة مفتاح / قيمة بسيطة في JSON يتم إعادتها إلى ASP.NET عبر POST. مثال:

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

أنا لا تحاول أن تعترض على أشياء NET-TYPED قوية

أنا ببساطة بحاجة إلى قاموس بسيط عادي (من سلسلة ، سلسلة) ، أو ما يعادل (جدول التجزئة ، قاموس (سلسلة ، كائن) ، StringDictionary مدرسة قديمة - الجحيم ، وسوف تعمل لي صفيف 2-D من السلاسل.

يمكنني استخدام أي شيء متاح في ASP.NET 3.5 ، وكذلك Json.NET الشائعة (التي أستخدمها بالفعل للتسلسل للعميل).

يبدو أن أياً من مكتبات JSON لا تملك هذه القدرة الواضحة للجبهة من العلبة - فهي تركز تماماً على نزع التسلسل القائم على التفكير عبر عقود قوية.

أيه أفكار؟

محددات:

  1. لا أريد تنفيذ محلل JSON الخاص بي
  2. لا يمكن استخدام ASP.NET 4.0 حتى الآن
  3. يفضل الابتعاد عن فئة ASP.NET القديمة ، التي تم إيقافها لـ JSON

Json.NET يفعل هذا ...

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

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

المزيد من الأمثلة: Serializing Collections with Json.NET


أنا فقط بحاجة إلى تحليل قاموس متداخل ، مثل

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

حيث لا يساعد JsonConvert.DeserializeObject . لقد وجدت الطريقة التالية:

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

يتيح لك SelectToken إلى الحقل المطلوب. يمكنك حتى تحديد مسار مثل "xyz" لأسفل في كائن JSON.


إذا كنت تتبع نوعًا من المقاربة خفيفة الوزن ، بدون إضافة مراجعات ، فربما تعمل هذه الشفرة التي كتبتها للتو (لا أضمن 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 ، ولكن من الناحية الفنية ، لم تكتبه ، لقد فعلت ذلك]


النهج الخاص بي deserializes مباشرة إلى IDictionary دون JObject أو ExpandObject بينهما. تستخدم الشفرة المحول ، والذي تم نسخه بشكل أساسي من فئة ExpandoObjectConverter الموجودة في مصدر JSON.NET ، ولكن باستخدام IDictionary بدلاً من ExpandoObject.

الاستعمال:

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

حاولت عدم استخدام أي تطبيق 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);

لقد أضفت شيكًا بالقيم الخالية في JSON إلى الإجابة الأخرى

واجهت نفس المشكلة حتى كتبت هذا ذاتي. يتم تمييز هذا الحل عن الإجابات الأخرى لأنه يمكن أن يندثر في مستويات متعددة.

ما عليك سوى إرسال سلسلة json إلى الدالة deserializeToDictionary ستؤدي إلى إرجاع Dictionary<string, object> يحتوي على الكلمات القوية 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;
}

Ex: سيؤدي هذا إلى إرجاع Dictionary<string, object> في استجابة JSON على Facebook.

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> object.


لقد اكتشفت .NET قد بنيت في طريقة لإدخال سلسلة JSON إلى Dictionary<String, Object> عبر نوع System.Web.Script.Serialization.JavaScriptSerializer في تجميع System.Web.Extensions 3.5. استخدم الطريقة DeserializeObject(String) .

تعثرت على هذا عند القيام بوظيفة ajax post (عبر jquery) من نوع المحتوى 'application / json' إلى static .net Page Method ورأيت أن الطريقة (التي كانت لها معلمة مفردة من نوع Object ) استقبلت هذا القاموس بطريقة سحرية.


نشر مارك ريندل هذا كتعليق ، أردت نشره كإجابة لأنه الحل الوحيد الذي نجح حتى الآن في إرجاع النجاح ونتائج أخطاء رموز الخطأ من رد Google reCaptcha.

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

شكرا مرة أخرى ، مارك!


يبدو أن كل هذه الإجابات هنا تفترض فقط أنه يمكنك الحصول على هذه السلسلة الصغيرة من كائن أكبر ... بالنسبة للأشخاص الذين يتطلعون ببساطة إلى إلغاء تسلسل كائن كبير باستخدام قاموس من هذا القبيل في مكان ما داخل الخريطة ، والذين يستخدمون System.Runtime.Serialization.Json DataContract النظام ، وهنا الحل:

كان الجواب على 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
    }
}

لإضافة أنواع جديدة مدعومة إلى الفصل ، تحتاج فقط إلى إضافة فصل JsonSurrogateObject ، JsonSurrogateObject والوظائف المناسبة (انظر إلى SurrogateDictionary على سبيل المثال) ، وتأكد من أنه يرث JsonSurrogateObject ، وإضافة تعيين نوعه إلى قاموس KnownTypes . يمكن استخدام SurrogateDictionary المضمنة كأساس لأي Dictionary<String,T> حيث T هو أي نوع لا يندثر بشكل صحيح.

الاتصال به بسيط للغاية:

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

لاحظ أنه لسبب ما ، يواجه هذا الشيء مشكلة في استخدام السلاسل الرئيسية التي تحتوي على مسافات ؛ كانوا ببساطة غير موجود في القائمة النهائية. قد يكون الأمر ببساطة ضد مواصفات json و api التي كنت أدعوها تم تنفيذها بشكل سيء ، فكر فيك ؛ لا أدري. على أي حال ، قمت بحل هذه المشكلة من خلال regex- استبدالها بشرطة سفلية في بيانات json الخام وتثبيت القاموس بعد إلغاء التسلسل.


تحرير: يعمل هذا ، ولكن الإجابة المقبولة باستخدام Json.NET هي أكثر وضوحًا. ترك هذا واحد في حال احتاج شخص ما إلى شفرة BCL فقط.

إنه غير مدعوم من خلال .NET framework من الصندوق. إشراف صارخ - لا يحتاج الجميع إلى التسلل إلى كائنات ذات خصائص محددة. لذا انتهى بي الأمر

<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 ...


Annoyingly enough, if you want to use the default model binders, it looks like you will have to use numerical index values like a form POST.

See the following excerpt from this article http://msdn.microsoft.com/en-us/magazine/hh781022.aspx :

Though it's somewhat counterintuitive, JSON requests have the same requirements—they, too, must adhere to the form post naming syntax. Take, for example, the JSON payload for the previous UnitPrice collection. The pure JSON array syntax for this data would be represented as:

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

However, the default value providers and model binders require the data to be represented as a JSON form post:

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

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

The complex object collection scenario is perhaps one of the most widely problematic scenarios that developers run into because the syntax isn't necessarily evident to all developers. However, once you learn the relatively simple syntax for posting complex collections, these scenarios become much easier to deal with.


Based on comments above try 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);

seems to work even for complex objects and lists.


If you don't want or can't add a large third-party library like Json.NET (for example, because you're using Unity3D), I really recommend taking a look at MiniJSON . Short and sweet code, easy to extend if there's something more you need.


You could use Tiny-JSON

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






json.net