860 Stimmen

Wie kann ich JSON in ein einfaches Dictionary<string,string> in ASP.NET deserialisieren?

Ich habe eine einfache Schlüssel/Wert-Liste in JSON, die über POST an ASP.NET zurückgesendet wird. Beispiel:

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

ICH VERSUCHE NICHT, IN STARK GETYPTE .NET-OBJEKTE ZU DESERIALISIEREN

Ich brauche einfach einen einfachen alten Dictionary(Of String, String) oder ein Äquivalent (Hash-Tabelle, Dictionary(Of String, Object), StringDictionary der alten Schule - ein 2-D-Array von Strings würde für mich funktionieren.

Ich kann alles verwenden, was in ASP.NET 3.5 verfügbar ist, ebenso wie das beliebte Json.NET (das ich bereits für die Serialisierung verwende) zu den Kunden).

Offensichtlich keine dieser JSON-Bibliotheken haben diese Stirn schlagen offensichtliche Fähigkeit out of the box--sie sind völlig auf Reflexion-basierte Deserialisierung über starke Verträge konzentriert.

Irgendwelche Ideen?

Beschränkungen:

  1. Ich möchte nicht meinen eigenen JSON-Parser implementieren
  2. ASP.NET 4.0 kann noch nicht verwendet werden
  3. Ich würde es vorziehen, von der älteren, veralteten ASP.NET-Klasse für JSON wegzubleiben.

1 Stimmen

Zu: Einschränkung 3, JavaScriptSerizlizer wird in ASP.NET MVC verwendet und ist nicht mehr veraltet.

37 Stimmen

Es ist unglaublich, wie schwierig es war, einen einfachen Weg zu finden, um eine json-Zeichenfolge in etwas zu konvertieren, das ich leicht verwenden konnte, ohne durch viele verschiedene Stackoverflow zu blättern. Es ist so einfach in anderen Sprachen noch Java und C# scheint zu gehen aus dem Weg zu machen das Leben schwer.

3voto

Nyerguds Punkte 4916

Es scheint, dass all diese Antworten hier davon ausgehen, dass Sie diese kleine Zeichenkette aus einem größeren Objekt herausholen können... für Leute, die einfach ein großes Objekt mit einem solchen Wörterbuch irgendwo innerhalb der Abbildung deserealisieren wollen und die die System.Runtime.Serialization.Json DataContract-System, hier ist eine Lösung:

Eine Antwort auf gis.stackexchange.com hatte dieser interessante Link . Ich musste es mit archive.org wiederherstellen, aber es bietet eine ziemlich perfekte Lösung: eine benutzerdefinierte IDataContractSurrogate Klasse, in der Sie genau Ihre eigenen Typen implementieren. Ich konnte sie leicht erweitern.

Ich habe allerdings eine Reihe von Änderungen daran vorgenommen. Da die Originalquelle nicht mehr verfügbar ist, werde ich die gesamte Klasse hier veröffentlichen:

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

Um der Klasse neue unterstützte Typen hinzuzufügen, müssen Sie nur Ihre Klasse hinzufügen und ihr die richtigen Konstruktoren und Funktionen geben (siehe SurrogateDictionary für ein Beispiel), vergewissern Sie sich, dass es erbt JsonSurrogateObject und fügen Sie dessen Typzuordnung zur KnownTypes Wörterbuch. Das mitgelieferte SurrogateDictionary kann als Basis für jedes Dictionary<String,T> Typen, wobei T ein beliebiger Typ ist, der sich korrekt deserialisieren lässt.

Der Aufruf ist wirklich einfach:

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

Beachten Sie, dass aus irgendeinem Grund dieses Ding hat Probleme mit Schlüssel Zeichenfolgen, die Leerzeichen enthalten; sie waren einfach nicht in der endgültigen Liste vorhanden. Könnte nur sein, es ist einfach gegen json Spezifikationen und die api ich aufgerufen wurde schlecht implementiert, wohlgemerkt; Ich weiß nicht. Wie auch immer, ich löste dies durch Regex-Ersetzung sie mit Unterstrichen in der rohen Json-Daten und Festsetzung des Wörterbuchs nach der Deserialisierung.

0 Stimmen

Übrigens, aus irgendeinem seltsamen Grund scheint Mono Probleme zu haben, dieses Zeug auszuführen...

0 Stimmen

Vielen Dank für den Austausch, leider diese Lösung nicht unterstützt nicht-primitive Typen, und es gibt keine Möglichkeit, den Rohwert zu erhalten, so dass Sie es selbst konstruieren können. Wenn ich meinen benutzerdefinierten Typ in KnownTypes registriere und ihn im Wörterbuch verwende, ruft er zuerst das Wörterbuch auf. Ich würde erwarten, dass er mit dem Parsen von unten nach oben beginnt, von den entferntesten Typen bis hin zu den komplexeren.

0 Stimmen

Nun, die Frage bezog sich nur auf Dictionary<String,String> . Ich habe ehrlich gesagt nie versucht, komplexe Typen mit diesem System zu deserialisieren.

3voto

Rafał Kłys Punkte 500

Mein Ansatz deserialisiert direkt zu IDictionary, ohne JObject oder ExpandObject dazwischen. Der Code verwendet Konverter, die im Grunde von ExpandoObjectConverter Klasse in JSON.NET Quellcode gefunden kopiert wird, aber mit IDictionary anstelle von ExpandoObject.

Verwendung:

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

Code:

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

2voto

Kjeld Poulsen Punkte 183

Ein bisschen spät zum Spiel, aber keine der oben genannten Lösungen wies mich in Richtung einer reinen und einfachen .NET, keine json.net-Lösung. Also hier ist es, endete als sehr einfach. Nachfolgend ein vollständiges Beispiel, wie es mit Standard-.NET-Json-Serialisierung getan wird, hat das Beispiel Wörterbuch sowohl in der Root-Objekt und in den Kind-Objekte.

Die goldene Kugel ist diese Katze, parsen Sie die Einstellungen als zweiten Parameter an den Serialisierer:

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

Vollständiger Code unten:

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

1voto

northben Punkte 5010

Ich habe dies gerade in RestSharp . Diese Stelle war für mich hilfreich.

Neben dem Code in dem Link, hier ist mein Code. Ich bekomme jetzt eine Dictionary der Ergebnisse, wenn ich so etwas mache:

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

Achten Sie auf die Art von JSON, die Sie erwarten - in meinem Fall rief ich ein einzelnes Objekt mit mehreren Eigenschaften ab. In dem beigefügten Link hat der Autor eine Liste abgerufen.

1voto

jeremysawesome Punkte 6675

Ärgerlicherweise sieht es so aus, als ob Sie numerische Indexwerte wie bei einem POST-Formular verwenden müssen, wenn Sie die Standardmodell-Bindemittel verwenden wollen.

Siehe den folgenden Auszug aus diesem Artikel http://msdn.microsoft.com/en-us/magazine/hh781022.aspx :

Auch wenn es etwas kontraintuitiv ist, haben JSON-Anfragen die gleichen Anforderungen - auch sie müssen sich an die Form-Post-Namenssyntax halten. Nehmen wir zum Beispiel die JSON-Nutzdaten für die vorherige UnitPrice Sammlung. Die reine JSON-Array-Syntax für diese Daten würde lauten dargestellt als:

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

Die Standardwertgeber und Modellbinder benötigen jedoch die Daten als JSON-Formularpost dargestellt werden:

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

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

Das Szenario der komplexen Objektsammlung ist vielleicht eines der problematischsten Szenarien, auf die Entwickler stoßen, weil die Syntax nicht unbedingt für alle Entwickler offensichtlich ist. Sobald Sie jedoch die relativ einfache Syntax für die Buchung komplexer Sammlungen gelernt hat, werden diese Szenarien viel leichter zu bewältigen sein.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X