Runtime: Das neue Asp.NET Core 3.0 Json serialisiert das Wörterbuch nicht<key/>

Erstellt am 7. Aug. 2019  ·  51Kommentare  ·  Quelle: dotnet/runtime

.NET Core 3.0-Vorschau 7

Asp.NET Web Apis schlägt beim Zurückgeben eines Wörterbuchs mit einer NotSupportedException fehl. Ich habe die Ausnahme unten eingefügt.

Außerdem nimmt die ControllerBase.BadRequest -Methode ein ModelStateDictionary , aber wenn das zurückgegeben wird, explodiert der Serializer ebenfalls mit einer NotSupportedException, aber einer etwas anderen Meldung.

Wann wird diese Unterstützung hinzugefügt? Da dies in Json.net und anderen Serializern seit einiger Zeit unterstützt wird, hoffe ich, dass dies auf dem Radar ist.

Ich schätze die Tatsache, dass ich mich wieder für die Verwendung von Json.net entscheiden kann, also vielen Dank dafür!

Ausnahme bei Rückgabe eines Wörterbuchs
System.NotSupportedException: Der Sammlungstyp 'System.Collections.Generic.Dictionary`2[System.Int32,System.String]' wird nicht unterstützt.
at System.Text.Json.JsonClassInfo.GetElementType(Type propertyType, Type parentType, MemberInfo memberInfo, JsonSerializerOptions-Optionen)
at System.Text.Json.JsonClassInfo.CreateProperty (Typ deklariertPropertyType, Typ runtimePropertyType, PropertyInfo propertyInfo, Typ parentClassType, JsonSerializerOptions-Optionen)
at System.Text.Json.JsonClassInfo.AddProperty(Type propertyType, PropertyInfo propertyInfo, Type classType, JsonSerializerOptions Optionen)
at System.Text.Json.JsonClassInfo.AddPolicyProperty(Type propertyType, JsonSerializerOptions-Optionen)
at System.Text.Json.JsonClassInfo..ctor(Typtyp, JsonSerializerOptions-Optionen)
at System.Text.Json.JsonSerializerOptions.GetOrAddClass(Type classType)
at System.Text.Json.WriteStackFrame.Initialize(Type type, JsonSerializerOptions-Optionen)
at System.Text.Json.JsonSerializer.WriteAsyncCore(Stream utf8Json, Objektwert, Typtyp, JsonSerializerOptions-Optionen, CancellationToken CancellationToken)
bei Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
bei Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
bei Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|21_0(ResourceInvoker-Aufrufer, IActionResult-Ergebnis)
bei Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|29_0 TFilter,TFilterAsync
bei Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed-Kontext)
bei Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext TFilter,TFilterAsync
bei Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
--- Ende des Stack-Trace vom vorherigen Ort, an dem die Ausnahme ausgelöst wurde ---
bei Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|19_0(ResourceInvoker-Aufrufer, Task lastTask, State next, Scope-Bereich, Object state, Boolean isCompleted)
bei Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|17_1(ResourceInvoker-Aufrufer)
bei Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint-Endpunkt, Task requestTask, ILogger-Logger)
bei Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext-Kontext)
bei Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext-Kontext)
bei Microsoft.AspNetCore.Builder.Extensions.MapWhenMiddleware.Invoke(HttpContext-Kontext)
bei Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
bei Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
bei Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext-Kontext)

Ausnahme bei Rückgabe von BadRequest
System.NotSupportedException: Der Sammlungstyp 'Microsoft.AspNetCore.Mvc.SerializableError' wird nicht unterstützt.
at System.Text.Json.JsonClassInfo.GetElementType(Type propertyType, Type parentType, MemberInfo memberInfo, JsonSerializerOptions-Optionen)
at System.Text.Json.JsonClassInfo.CreateProperty (Typ deklariertPropertyType, Typ runtimePropertyType, PropertyInfo propertyInfo, Typ parentClassType, JsonSerializerOptions-Optionen)
at System.Text.Json.JsonClassInfo.AddProperty(Type propertyType, PropertyInfo propertyInfo, Type classType, JsonSerializerOptions Optionen)
at System.Text.Json.JsonClassInfo.AddPolicyProperty(Type propertyType, JsonSerializerOptions-Optionen)
at System.Text.Json.JsonClassInfo..ctor(Typtyp, JsonSerializerOptions-Optionen)
at System.Text.Json.JsonSerializerOptions.GetOrAddClass(Type classType)
at System.Text.Json.WriteStackFrame.Initialize(Type type, JsonSerializerOptions-Optionen)
at System.Text.Json.JsonSerializer.WriteAsyncCore(Stream utf8Json, Objektwert, Typtyp, JsonSerializerOptions-Optionen, CancellationToken CancellationToken)
bei Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
bei Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
bei Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|21_0(ResourceInvoker-Aufrufer, IActionResult-Ergebnis)
bei Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|29_0 TFilter,TFilterAsync
bei Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed-Kontext)
bei Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext TFilter,TFilterAsync
bei Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
--- Ende des Stack-Trace vom vorherigen Ort, an dem die Ausnahme ausgelöst wurde ---
bei Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|19_0(ResourceInvoker-Aufrufer, Task lastTask, State next, Scope-Bereich, Object state, Boolean isCompleted)
bei Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|17_1(ResourceInvoker-Aufrufer)
bei Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint-Endpunkt, Task requestTask, ILogger-Logger)
bei Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext-Kontext)
bei Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext-Kontext)
bei Microsoft.AspNetCore.Builder.Extensions.MapWhenMiddleware.Invoke(HttpContext-Kontext)
bei Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
bei Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
bei Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext-Kontext)

area-System.Text.Json enhancement

Hilfreichster Kommentar

Für die Neuankömmlinge besteht die vorübergehende Lösung darin, zu Newtonsoft.Json .

  1. Paketverweis zu Microsoft.AspNetCore.Mvc.NewtonsoftJson .
  2. Fügen Sie .AddNewtonsoftJson() direkt nach .AddControllers() / .AddMvc() oder einer anderen Kombination hinzu.

Alle 51 Kommentare

Beide Fehler "nicht unterstützte Ausnahme" sind Einschränkungen innerhalb des integrierten Serializers und sind beabsichtigt (zumindest für das, was in 3.0 ausgeliefert wird).

Wann wird diese Unterstützung hinzugefügt? Da dies in Json.net und anderen Serializern seit einiger Zeit unterstützt wird, hoffe ich, dass dies auf dem Radar ist.
Ich schätze die Tatsache, dass ich mich wieder für die Verwendung von Json.net entscheiden kann, also vielen Dank dafür!

Es gibt eine Reihe von Serializer-Funktionen, die wir in vNext (also 5.0) und darüber hinaus unterstützen, wobei die Unterstützung für benutzerdefinierte Wörterbücher eine davon ist.

Asp.NET Web Apis, wenn ein Wörterbuch zurückgegeben wird, schlägt es mit einer NotSupportedException fehl

Beim Serialisieren werden heute nur Dictionary<string, TValue> unterstützt (dh TKeys, die Strings sind). Ihr Wörterbuch enthält <int, string> was nicht unterstützt wird.
https://github.com/dotnet/corefx/blob/93d7aa1c1737b6da29d04b78557215e18eb786d4/src/System.Text.Json/tests/Serialization/DictionaryTests.cs#L385 -L390

@steveharter , @layomia - gibt es hier in der Zwischenzeit einen möglichen Workaround? Was wäre erforderlich, um die Unterstützung für ein Wörterbuch mit Schlüsselwörtern ohne Zeichenfolgen innerhalb des Serializers für 5.0 hinzuzufügen?

System.NotSupportedException: Der Sammlungstyp 'Microsoft.AspNetCore.Mvc.SerializableError' wird nicht unterstützt.

~ Das Hinzufügen von Unterstützung für einen Typ wie SerializableError war nicht auf meinem Radar. @pranavkm , @rynowak - was ist hier der Kontext? Ich bin mit ModelStateDictionary nicht vertraut, könnte dies in mvc selbst mit einem benutzerdefinierten Konverter unterstützt werden?~

Edit: Egal, das ist schon behoben.

System.NotSupportedException: Der Sammlungstyp 'Microsoft.AspNetCore.Mvc.SerializableError' wird nicht unterstützt.

Dies war ein bekanntes Problem https://github.com/aspnet/AspNetCore/issues/11459 , das kürzlich behoben wurde (als Teil der Vorschau 8): https://github.com/dotnet/corefx/pull/39001

Vielen Dank für eure schnellen Antworten @ahsonkhan!

Die "Einschränkung" des Schlüssels, der eine Zeichenfolge ist, macht tatsächlich Sinn, wenn ich darüber nachdenke. Ich sehe jetzt, dass Json.net tatsächlich Json generiert, wobei der Schlüssel eine Zeichenfolge ist. Beim Deserialisieren würde mir nur ein int zurückgegeben. Es wäre auf jeden Fall schön, in Zukunft die Unterstützung für Nicht-String-Tasten zu haben, aber kein Showstopper.

Ok, schön zu hören, dass der nicht unterstützte Mvc.SerializableError behoben wurde. Irgendwelche Ideen, ob es ein erwartetes Erscheinungsdatum von Preview 8 gibt? Ich habe versucht, etwas zu suchen und zu finden, aber nichts darüber gefunden.

Sobald Vorschau 8 erscheint, werden wir versuchen, die Json-Serialisierungsbibliothek von .net Core 3 erneut auszuprobieren, aber im Moment müssen wir bei Json.net bleiben

@steveharter , @layomia - gibt es hier in der Zwischenzeit einen möglichen Workaround? Was wäre erforderlich, um die Unterstützung für ein Wörterbuch mit Schlüsselwörtern ohne Zeichenfolgen innerhalb des Serializers für 5.0 hinzuzufügen?

>

@ahsonkhan @willyt150 die JsonConverter<T> implementiert, wobei T Dictionary<int, string> .
Einige Beispiele finden Sie unter https://github.com/dotnet/corefx/issues/36639#issue -429928740.

Irgendwelche Ideen, ob es ein erwartetes Erscheinungsdatum von Preview 8 gibt?

Einige Zeit später in diesem Monat.

Denken Sie noch einmal darüber nach und entfernen Sie die Up-for-Grabs für den Moment, da dies möglicherweise etwas ist, das wir standardmäßig nicht unterstützen möchten.

Danke @layomia das werde ich mir mal anschauen.

Danke @ahsonkhan ,

Von @namse (von https://github.com/dotnet/corefx/issues/40404):

Hallo. Wenn ich versuche, Dictionary mit Integer-Schlüssel zu serialisieren, wird System.NotSupportedException ausgelöst.

Ich denke, es ist sinnvoll, die Json-Serialisierung zu unterstützen, deren Dictionary einen ToString -fähigen Schlüssel hat. Wenn wir beispielsweise ToString für int oder boolean ausführen, wird "123" oder "true" zurückgegeben. Ich denke, dieser Schlüssel ist ein ToString -fähiger Schlüssel.

Version

System.Text.Json Nuget-Version: 4.6.0-preview8.19405.3

Code

var dictionary = new Dictionary<int, string>
{
  [5] = "five"
};
JsonSerializer.Serialize(dictionary);

Erwartet

"{"5": "five"}"

Aber was passiert?

Fehler System.NotSupportedException geworfen

Tatsächlich gibt es ein Kompatibilitätsproblem, wenn ich Newtonsoft.Json in System.Text.Json ändere. Sie geben String zurück, wie ich es erwartet hatte. Ich denke, System.Text.Json muss nicht kompatibel sein, aber ... wissen Sie.

Ich habe einen Konverter implementiert, der sowohl Serialisierung als auch Deserialisierung für IDictionary<TKey, TValue> wobei TKey eine statische Methode TKey Parse(string) :

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace JsonDictionaryConverter
{
    sealed class JsonNonStringKeyDictionaryConverter<TKey, TValue> : JsonConverter<IDictionary<TKey, TValue>>
    {
        public override IDictionary<TKey, TValue> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            var convertedType = typeof(Dictionary<,>)
                .MakeGenericType(typeof(string), typeToConvert.GenericTypeArguments[1]);
            var value = JsonSerializer.Deserialize(ref reader, convertedType, options);
            var instance = (Dictionary<TKey, TValue>)Activator.CreateInstance(
                typeToConvert,
                BindingFlags.Instance | BindingFlags.Public,
                null,
                null,
                CultureInfo.CurrentCulture);
            var enumerator = (IEnumerator)convertedType.GetMethod("GetEnumerator")!.Invoke(value, null);
            var parse = typeof(TKey).GetMethod("Parse", 0, BindingFlags.Public | BindingFlags.Static, null, CallingConventions.Any, new[] { typeof(string) }, null);
            if (parse == null) throw new NotSupportedException($"{typeof(TKey)} as TKey in IDictionary<TKey, TValue> is not supported.");
            while (enumerator.MoveNext())
            {
                var element = (KeyValuePair<string?, TValue>)enumerator.Current;
                instance.Add((TKey)parse.Invoke(null, new[] { element.Key }), element.Value);
            }
            return instance;
        }

        public override void Write(Utf8JsonWriter writer, IDictionary<TKey, TValue> value, JsonSerializerOptions options)
        {
            var convertedDictionary = new Dictionary<string?, TValue>(value.Count);
            foreach (var (k, v) in value) convertedDictionary[k?.ToString()] = v;
            JsonSerializer.Serialize(writer, convertedDictionary, options);
            convertedDictionary.Clear();
        }
    }

    sealed class JsonNonStringKeyDictionaryConverterFactory : JsonConverterFactory
    {
        public override bool CanConvert(Type typeToConvert)
        {
            if (!typeToConvert.IsGenericType) return false;
            if (typeToConvert.GenericTypeArguments[0] == typeof(string)) return false;
            return typeToConvert.GetInterface("IDictionary") != null;
        }

        public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
        {
            var converterType = typeof(JsonNonStringKeyDictionaryConverter<,>)
                .MakeGenericType(typeToConvert.GenericTypeArguments[0], typeToConvert.GenericTypeArguments[1]);
            var converter = (JsonConverter)Activator.CreateInstance(
                converterType,
                BindingFlags.Instance | BindingFlags.Public,
                null,
                null,
                CultureInfo.CurrentCulture);
            return converter;
        }
    }
}

Prüfen:

class Entity
{
    public string Value { get; set; }
}
class TestClass
{
    public Dictionary<int, Entity> IntKey { get; set; }
    public Dictionary<float, Entity> FloatKey { get; set; }
    public Dictionary<double, Entity> DoubleKey { get; set; }
    public Dictionary<DateTime, Entity> DateTimeKey { get; set; }
    public Dictionary<string, Entity> StringKey { get; set; }
}
class Program
{
    static void Main(string[] args)
    {
        var options = new JsonSerializerOptions();
        options.Converters.Add(new JsonNonStringKeyDictionaryConverterFactory());
        var x = new TestClass
        {
            IntKey = new Dictionary<int, Entity> { [1] = new Entity { Value = "test" } },
            FloatKey = new Dictionary<float, Entity> { [1.3f] = new Entity { Value = "test" } },
            DoubleKey = new Dictionary<double, Entity> { [1.35] = new Entity { Value = "test" } },
            DateTimeKey = new Dictionary<DateTime, Entity> { [DateTime.Now] = new Entity { Value = "test" } },
            StringKey = new Dictionary<string, Entity> { ["test"] = new Entity { Value = "test" } }
        };

        var value = JsonSerializer.Serialize(x, options);
        Console.WriteLine(value);
        var obj = JsonSerializer.Deserialize<TestClass>(value, options);
        Console.WriteLine(JsonSerializer.Serialize(obj, options));
    }
}

Ergebnis:

{"IntKey":{"1":{"Value":"test"}},"FloatKey":{"1.3":{"Value":"test"}},"DoubleKey":{"1.35":{"Value":"test"}},"DateTimeKey":{"8/25/2019 6:47:48 PM":{"Value":"test"}},"StringKey":{"test":{"Value":"test"}}}
{"IntKey":{"1":{"Value":"test"}},"FloatKey":{"1.3":{"Value":"test"}},"DoubleKey":{"1.35":{"Value":"test"}},"DateTimeKey":{"8/25/2019 6:47:48 PM":{"Value":"test"}},"StringKey":{"test":{"Value":"test"}}}

Es kann jedoch immer noch kein verschachteltes Wörterbuch wie Dictionary<int, Dictionary<int, int>> serialisieren, da System.Text.Json den inneren Typ Dictionary<int, int> nicht akzeptiert. Ich denke, es ist ein Bug.

Es kann jedoch immer noch kein verschachteltes Wörterbuch wie Dictionary . serialisieren> weil System.Text.Json den inneren Typ Dictionary nicht akzeptiert. Ich denke, es ist ein Bug.

Die Unterstützung von nur <string, x> ist beabsichtigt, da der Umfang eingeschränkt wurde, um für v3.0 ausgeliefert zu werden. Die Version 3.0 soll ein minimal praktikables Produkt sein, bei dem die gängigsten Szenarien unterstützt werden.

@steveharter Zumindest sollten Sie keine NotsupportException werfen, wenn es einen verwendbaren Konverter gibt.

Gibt es Pläne, dies in .net Core 3.1 zu unterstützen?

Für die Neuankömmlinge besteht die vorübergehende Lösung darin, zu Newtonsoft.Json .

  1. Paketverweis zu Microsoft.AspNetCore.Mvc.NewtonsoftJson .
  2. Fügen Sie .AddNewtonsoftJson() direkt nach .AddControllers() / .AddMvc() oder einer anderen Kombination hinzu.

@steveharter Zumindest sollten Sie keine NotsupportException werfen, wenn es einen verwendbaren Konverter gibt.

Ja, das ist ein gerechter Punkt. Vielleicht können wir diese Einschränkung für 3.1 aufheben. cc @layomia

Auch nur um klarzustellen, dass Wörterbuchelemente heute wie Eigenschaften serialisiert werden, was möglich ist, da der Schlüssel ein String ist. Die Unterstützung von Nicht-String-Schlüsseln bedeutet, dass Elemente als KeyValuePair serialisiert werden.

Oh Junge, hatte dieses Problem direkt nach dem Upgrade auf 3.0.

Musste das Newton-Paket mit AddNewtonsoftJson installieren.

Von @israellot in https://github.com/dotnet/corefx/issues/41345

var dictionary = new Dictionary<int, int>()
            {
                [0] = 1
            };

 var serialized = System.Text.Json.JsonSerializer.Serialize(dictionary);

Diese einfache Serialisierung wird von der früheren standardmäßigen Newtonsoft json-Bibliothek gut gehandhabt, indem der int-Schlüssel als String serialisiert wird. Bei System.Text.Json wird eine nicht unterstützte Ausnahme ausgelöst.

@israellot , @unruledboy und andere im Thread, können Sie Details dazu Dictionary<string, TValue> ändern? Ich würde gerne einige Verwendungen zum Sammeln von Anforderungen sehen und helfen, den Fix zu motivieren.

Sie würden sowieso als Zeichenfolgen serialisiert, daher verstehe ich nicht, in welchen Szenarien Sie möchten, dass Ihr zugrunde liegendes Wörterbuch stattdessen int32-Schlüssel hat.

@ahsonkhan Ich glaube, die Hauptmotivation ist Kompatibilität.
Der vorherige Standard-Serializer war Newtonsoft, sodass Benutzer möglicherweise ganze Modelle geschrieben haben, die sich auf seine Fähigkeit verlassen haben, beliebige Klassen zu serialisieren und zu deserialisieren. Die Migration von 2.X auf 3.0 führt nun zu einem stillen Breaking Change, da wir die Inkompatibilität nur zur Laufzeit kennen.

Ich glaube, viele Szenarien beinhalten die Verwendung von Json nur als Transport über die Leitung, und in diesem Fall könnte das Domänenmodell Dictionary sein. Ihr Vorschlag läuft darauf hinaus, ein separates DTO-Objektverzeichnis zu erstellenund das Konvertieren zwischen den beiden scheint ziemlich ineffizient, da wir jetzt ein anderes Objekt zuweisen müssen, nur um mit dem Serializer kompatibel zu sein.
Wenn man sich strikt an den Serializer hält, ist es logisch, das Dictionary auf Zeichenfolgenschlüssel zu beschränken, da dies die einzig mögliche Json-Darstellung ist. Aber wenn man bedenkt, dass der Serializer in Anwendungen eine Rolle spielt, ist es meiner Meinung nach am besten, so viel Reibung wie möglich zu beseitigen.

Ich bin auf dieses Problem mit einem kleinen Programm gestoßen, das ich schreibe, bei dem Teile eines Versionslabels über eine JSON-Datei bereitgestellt werden.
Die Etikettenteile haben einen Schlüssel, der den Index angibt, wo der Etikettenteil eingefügt werden kann. Dies bedeutet, dass die Tasten numerische Werte sind, z. B.

{
  "parts" : {
    "1" : "alpha",
    "3" : "beta"
  }
}

Mit Newtonsoft kann das json ohne Probleme zu Dictionary<int, string> deserialisiert werden. Nach der Konvertierung in System.Text.Json ist die Serialisierung fehlgeschlagen.

Ich habe dieses Problem gelöst, indem ich meinen eigenen DictionaryConverter und DictionaryConverter erstellt habe .
Ich habe auch einen einfachen Konverter erstellt , mit dem Ganzzahlen aus einem String deserialisiert werden können

Diese werden dann über die Serializer-Optionen registriert: https://github.com/Kieranties/SimpleVersion/blob/master/src/SimpleVersion.Core/Serialization/Serializer.cs#L22

Durch diese Änderungen können die Schlüssel eines Wörterbuchs deserialisiert werden, anstatt direkt als Zeichenfolge gelesen zu werden. Dies eröffnet weiter die Unterstützung für beliebige Typen von Schlüsseln, die ihre eigenen Konverter für die Serialisierung registriert haben könnten (z. B. Aufzählungen/Typen/Typen, die als eindeutige Schlüssel serialisiert werden können usw.).

Ich habe die Dinge nicht offiziell getestet, aber im Rahmen der aktuellen Entwicklung scheint das Problem dadurch gelöst zu sein.

Setzen eines Meilensteins für 3.1, um alle Einschränkungen zu entfernen, die die Erstellung eines benutzerdefinierten Konverters verhindern, der TKey für Dictionary<TKey,TValue> .

Update: Ich habe Beispiele hinzugefügt, die mit 3.0 funktionieren. Ich habe keine Probleme wie oben beschrieben mit verschachtelten Wörterbüchern bemerkt.

In den Beispielen werden zwei JSON-Formate verwendet:

  • JSON-Objekt mit String-Eigenschaften: {"1":"val1","2":"val2"} , obwohl TKey kein String ist.

    • Diese basieren auf einer TryParse Methode für den entsprechenden unterstützten Schlüsseltyp. Für v3.0 ist es nicht möglich, generalisierte TKey da TryParse Methoden für alle TKey und weil Kultureinstellungen bereitgestellt werden müssen (typischerweise Invariant). Die folgenden Beispiele gelten also für einen einzelnen TKey Typ.

    • Sample Dictionary<int, string> . Dies ist ein einfaches Beispiel für nur TValue == string .

    • Sample Dictionary<Guid, TValue> . Dies kann mit jedem TValue umgehen.

    • Sample Dictionary<TKey, TValue> where TKey is Enum . Für Aufzählungen; dies kann mit jedem TValue umgehen.

  • JSON-Array mit KeyValuePair-Einträgen: [{"Key":1,"Value":"val1"},{"Key":2,"Value":"val2"}]

Wenn diese Beispiele zufriedenstellend sind, werde ich dieses Problem in 5.0 ändern, um zu besprechen, ob wir integrierte Unterstützung bieten, die keine benutzerdefinierten Konverter erfordert.

Setzen des Meilensteins auf 5.0 zur Überlegung (was, wenn eines der obigen Beispiele standardmäßig funktionieren sollte).

Deserialisierung scheint auch generische Objekttypen auf JsonDocument zuzuordnen und nicht auf ihre normalen (primitiven?) Typen.

Beispiel:

string test = "[{\"id\":86,\"name\":\"test\"}]";
var SystemTextJson = System.Text.Json.JsonSerializer.Deserialize<List<Dictionary<string, object>>>(test);
var NewtonSoftJson = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(test);

SystemTextJson[0]["id"] wird angezeigt als: ValueKind = Number : "86"
NewtonSoftJson[0]["id"] wird angezeigt als: 86

@steveharter Ein enum-keyed-Wörterbuch mit diesem Konverter wird wie
{ "Stuff": [ { "Key": 1, "Value": "String" }, { "Key": 3, "Value": "String" }, { "Key": 2, "Value": "String" } ] }

Während JSON.NET bietet, was die meisten Leute erwarten würden:
{ "Stuff": { "Item1": "String", "Item2": "String", "Item3": "String" }

Deserialisierung scheint auch generische Objekttypen auf JsonDocument zuzuordnen und nicht auf ihre normalen (primitiven?) Typen.

Ja, das ist beabsichtigt. Siehe https://github.com/dotnet/corefx/issues/38713

@steveharter Ein enum-keyed-Wörterbuch mit diesem Konverter wird wie
{ "Stuff": [ { "Key": 1, "Value": "String" }, { "Key": 3, "Value": "String" }, { "Key": 2, "Value": " Zeichenfolge" } ] }
Während JSON.NET bietet, was die meisten Leute erwarten würden:
{ "Stuff": { "Item1": "String", "Item2": "String", "Item3": "String" }

Ich gehe davon aus, dass Sie das "Beispielwörterbuch" verwenden"? Wenn ja, ja, das KeyValuePair verwendet hat, das die Eigenschaften "Key" und "Value" hat. Ich habe kein Beispiel für TKey-basierte Enum-Wörterbücher bereitgestellt, die als Eigenschaftsnamen serialisiert werden, aber ich werde daran arbeiten, das hinzuzufügen.

Ja, der. Und ah ok, ich dachte, du hättest das als generischen Wörterbuch-Serializer gemeint.
Ich bin daran interessiert, Ihr neues Beispiel zu sehen, wenn es verfügbar ist, da das, das wir derzeit verwenden, nicht so schnell scheint, wie ich es mir wünschen würde.

@roguecode Hier ist ein Enum- Beispiel für Dictionary<TKey, TValue> wobei TKey ein Enum ist und die JSON-Syntax "Eigenschaft" anstelle von KeyValuePair verwendet. Ich habe auch die Liste der obigen Beispiele aktualisiert, um dieses neue Beispiel aufzunehmen.

Hallo, ich habe etwas ähnliches, aber anders und ich frage mich, ob Sie mir zeigen können, wo ich noch suchen kann.

Newtonsoft.Json: 12.0.2
Microsoft.AspNetCore.Mvc.NewtonsoftJson: 3.0.0

Start läuft als 3.0:

                .AddMvc(mvcOptions => mvcOptions.EnableEndpointRouting = false)
                .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
                .AddNewtonsoftJson();

Wenn ich dieses problemDetails-Objekt habe:

// problemDetails has 2 extension items
{Microsoft.AspNetCore.Mvc.ValidationProblemDetails}
    Detail: "Please refer to the 'errors' property for additional details."
    Errors: Count = 1
    Extensions: Count = 2
    Instance: "/api/test/complex-binding-from-query"
    Status: 400
    Title: "One or more validation errors occurred."
    Type: "https://tools.ietf.org/html/rfc7231#section-6.5.1"

In 2.2 gibt die Ausführung von Newtonsoft.Json.JsonConvert.SerializeObject(problemDetails) zurück

{"errors":{"Int":["The value 'a' is not valid for Int."]},"type":"https://tools.ietf.org/html/rfc7231#section-6.5.1","title":"One or more validation errors occurred.","status":400,"detail":"Please refer to the 'errors' property for additional details.","instance":"/api/test/complex-binding-from-query","traceId":"0HLQQ40AFBJNG","correlationId":"0HLQQ40AFBJNG"}

In 3.0 gibt es zurück:

{"Errors":{"param":["The value 'a' is not valid."]},"Type":"https://tools.ietf.org/html/rfc7231#section-6.5.1","Title":"One or more validation errors occurred.","Status":400,"Detail":"Please refer to the 'errors' property for additional details.","Instance":"/bad-request","Extensions":{"traceId":"|d0d40f80-48b6c9184401b0e1.","correlationId":"0HLQR10NSMRGD:00000009"}}

Die serialisierte Zeichenfolge der Version 3.0 enthält das IDictionaryEigenschaftsname, Extensions , und wir können diesen String in 3.0 korrekt deserialisieren. Sie sehen, dass dieser Eigenschaftsname in der 2.x-Version fehlt.

Das Problem besteht darin, dass die 3.0-Serialisierung auftritt, wenn die Antwort von einem Filter mit einem BadRequestObjectResult aus dem folgenden Code zurückgegeben wird:

public sealed class ProblemDetailsResultFilterAttribute : Attribute, IAlwaysRunResultFilter
{
        public void OnResultExecuting(ResultExecutingContext context)
        {
             context.Result = new BadRequestObjectResult(problemDetails);
        }
}

... hat der zurückgegebene Antwortinhalt dieselbe Form wie die Version 2.2 (der Extensions Eigenschaftsname ist ausgeschlossen), wodurch die Extensions Eigenschaft als leere Sammlung deserialisiert wird (mit Newtonsoft.Json.JsonConvert.DeserializeObject<ValidationProblemDetails>() )

Irgendwie verwendet diese Serialisierung nicht dieselbe Serialisierung wie die Newtonsoft-Bibliothek, mit der wir versuchen, zu deserialisieren.

Danke für die Überlegung!

Ich habe etwas ähnliches, aber anders

@ts46235 könnten Sie bitte eine neue Ausgabe dafür eröffnen, da sich diese von der aktuellen Ausgabe unterscheidet?

@ts46235 Ich habe auf Ihre Frage in der anderen Ausgabe geantwortet, die Sie hier geöffnet haben - https://github.com/aspnet/AspNetCore/issues/16618. Das Gespräch hier als Off-Topic markieren.

auf Core 3.1 aktualisiert und immer noch nicht behoben

Ich habe gerade auf 3.1 aktualisiert und bin davon betroffen. Zurück zu JSON.NET Ich gehe... (ich verwende GUID-Schlüssel)

Punktnetzkern 3.1
Wörterbuchfunktioniert auch nicht, sogar das Objekt im Schlüssel ist tatsächlich ein String

Auch ich habe gerade darauf gestoßen, und was für eine beängstigende stille Einschränkung es ist, da Sie, wie andere bereits darauf hingewiesen haben, dies zur Kompilierzeit nicht sehen werden. In meinem Fall möchte ich Dictionary<int, List<string>> serialisieren, was mir nicht besonders exotisch vorkommt.

Sie sollten es beheben, aber ich sehe dies immer wieder auch bei den alten Formatierern, Binärformatierern frühen Newtsoft, Wörterbüchern in Wörterbüchern, Wörterbüchern mit Schnittstellen. Sie sollten es beheben, aber wenn Sie keinen Ärger wollen, sollten die Leute wirklich keine komplexen Objekte wie Wörterbücher in Serialisierungsverträge packen, wenn Sie nach Ärger fragen - newtsoft hat die Leute verwöhnt. Sehen Sie sich alle öffentlichen Eigenschaften für die Anzahl der Wörterbücher usw. an. Sie verlassen sich auf etwas Benutzerdefiniertes im Serializer, um dies zuzuordnen.

Leider gibt es dafür in C# keinen einfachen Typ für Eigenschaftsnamen, so dass Dictionary erzwungen wird. Also ich bin einfach traurig..

Hier ist ein Workaround, aber keine vollständige Lösung:

   [JsonConverter(typeof(DictionaryConverter))]
   public Dictionary<string, object> ExtraProperties { get; set; } = new Dictionary<string, object>();
public class DictionaryConverter : JsonConverter<Dictionary<string, object>>
{
    public override Dictionary<string, object> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        var dictionary = JsonSerializer.Deserialize<Dictionary<string, object>>(ref reader, options);

        foreach (string key in dictionary.Keys.ToList())
        {
            if (dictionary[key] is JsonElement je)
            {
                dictionary[key] = Unwrap(je);
            }
        }

        return dictionary;
    }

    public override void Write(Utf8JsonWriter writer, Dictionary<string, object> value, JsonSerializerOptions options)
        => JsonSerializer.Serialize(writer, value, options);

    private static object Unwrap(JsonElement je)
    {
        return je.ValueKind switch
        {
            JsonValueKind.String => je.ToString(),
            JsonValueKind.Number => je.TryGetInt64(out var l) ? l : je.GetDouble(),
            JsonValueKind.True => true,
            JsonValueKind.False => false,
            JsonValueKind.Array => je.EnumerateArray().Select(Unwrap).ToList(),
            JsonValueKind.Object => je.EnumerateObject().ToDictionary(x => x.Name, x => Unwrap(x.Value)),
            _ => null
        };
    }

Vielleicht könnte Unterstützung für (einige der) gängigen Typen von Serializern OOTB hinzugefügt werden, zB diese Liste , und auch wenn einer dieser Typen .IsAssignableFrom(systemDotObjectInstance.GetType()) , um Dictionary<<ins i="7">object</ins>, V> .

Hier ist der Vorschlag zum Hinzufügen von Unterstützung für Nicht-String-Typen TKey in Wörterbüchern.
https://github.com/dotnet/runtime/pull/32676

Bitte teilen Sie uns Ihre Gedanken oder Bedenken mit.

Geben Sie insbesondere Feedback zu den Typen, die
https://github.com/dotnet/runtime/blob/a5d96c0e280b56412d4614848f5ee3b1e0d7f216/src/libraries/System.Text.Json/docs/KeyConverter_spec.md#keyconverter

Wörterbuchfunktioniert auch nicht, sogar das Objekt im Schlüssel ist tatsächlich ein String

@andrew-vdb, die Unterstützung der Serialisierung beliebiger Objektschlüssel wird wahrscheinlich nicht unterstützt. Wenn der Laufzeittyp der Objektschlüssel jedoch einer der "neu unterstützten" Typen ist, funktioniert die Serialisierung dafür, sobald die Funktion abgeschlossen ist. Die Deserialisierung bleibt jedoch als Boxed JsonElement (bis dieses damit verbundene Problem für die Aktivierung des anderen Verhaltens behoben wird): https://github.com/dotnet/runtime/issues/29960

@Jozkee Welche Typen sind für TValue aktiviert? Vermutlich alles, was Sie derzeit als eigenständiges Objekt serialisieren können?

Vermutlich alles, was Sie derzeit als eigenständiges Objekt serialisieren können?

Jawohl.

Deserialisierung scheint auch generische Objekttypen auf JsonDocument zuzuordnen und nicht auf ihre normalen (primitiven?) Typen.

Beispiel:

string test = "[{\"id\":86,\"name\":\"test\"}]";
var SystemTextJson = System.Text.Json.JsonSerializer.Deserialize<List<Dictionary<string, object>>>(test);
var NewtonSoftJson = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(test);

SystemTextJson[0]["id"] wird angezeigt als: ValueKind = Number : "86"
NewtonSoftJson[0]["id"] wird angezeigt als: 86

Von allen genannten Problemen stört mich das am meisten. Aufführenoder T[] oder Wörterbuchsollte für jeden Typ, der direkt von den wenigen Typen, die Json hat, auf CLR-Typen abgebildet werden können, ordnungsgemäß deserialisiert werden.

Die Version 3.0 soll ein minimal praktikables Produkt sein, bei dem die gängigsten Szenarien unterstützt werden

Ich frage mich, wie List> ist nicht eines der häufigsten Szenarien:

[// N objects
{"a":4},
{"b","Bla"},
]

Da dies auch in System.Text.JsonElement deserialisiert wird, würde ich double (Number) und string (String) erwarten.

Ich bin auf dieses Problem mit einem kleinen Programm gestoßen, das ich schreibe, bei dem Teile eines Versionslabels über eine JSON-Datei bereitgestellt werden.
Die Etikettenteile haben einen Schlüssel, der den Index angibt, wo der Etikettenteil eingefügt werden kann. Dies bedeutet, dass die Tasten numerische Werte sind, z. B.

{
  "parts" : {
    "1" : "alpha",
    "3" : "beta"
  }
}

Mit Newtonsoft kann das json ohne Probleme zu Dictionary<int, string> deserialisiert werden. Nach der Konvertierung in System.Text.Json ist die Serialisierung fehlgeschlagen.

Ich habe dieses Problem gelöst, indem ich meinen eigenen DictionaryConverter und DictionaryConverter erstellt habe .
Ich habe auch einen einfachen Konverter erstellt , mit dem Ganzzahlen aus einem String deserialisiert werden können

Diese werden dann über die Serializer-Optionen registriert: https://github.com/Kieranties/SimpleVersion/blob/feature/netcore3/src/SimpleVersion.Core/Serialization/Serializer.cs#L20

Durch diese Änderungen können die Schlüssel eines Wörterbuchs deserialisiert werden, anstatt direkt als Zeichenfolge gelesen zu werden. Dies eröffnet weiter die Unterstützung für beliebige Typen von Schlüsseln, die ihre eigenen Konverter für die Serialisierung registriert haben könnten (z. B. Aufzählungen/Typen/Typen, die als eindeutige Schlüssel serialisiert werden können usw.).

Ich habe die Dinge nicht offiziell getestet, aber im Rahmen der aktuellen Entwicklung scheint das Problem dadurch gelöst zu sein.

hey @Kieranties , der Github verlinkt 404 für mich

hey @Kieranties , der Github verlinkt 404 für mich

@AirEssY Ich habe die Links in meinem ursprünglichen Kommentar korrigiert, denke, sind jetzt im Master unter: https://github.com/Kieranties/SimpleVersion/tree/master/src/SimpleVersion.Core/Serialization

Wenn das Dictionary einer JavaScript-Map entspricht, sollte jeder (JS-Typ, dargestellt in C#) akzeptabel sein,

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map

A Map's keys can be any value (including functions, objects, or any primitive).

Ein Beispiel für den Standardansatz zum Deserialisieren einer Karte in JS ist:

const countries = new Map(JSON.parse('[[1,"Bahamas (the)"],[2,"Bolivia (Plurinational State of)"]]'))

console.log(countries)

Was produziert:

Map(2) {
  1 => 'Bahamas (the)',
  2 => 'Bolivia (Plurinational State of)'
}

TL;DR: Die Beschränkung der Tasten auf Saiten funktioniert nicht gut mit JS

@ Jozkee kommt das also nur in .NET 5 oder wird es in 3.* gehen?

@onionhammer .NET 5.0, Sie können das Feature auch in der nächsten Vorschau (5.0 Vorschau8) ausprobieren.
Es gibt keine Pläne, dies nach 3.x zu portieren.

Lösung für asp net core 3.x:

var dic1 = new Dictionary<TKey, TValue>(); 
return Json(new { dic1 }); // does not work

var dic2 = from i in new Dictionary<TKey, TValue>() select new { i.Key, i.Value }
return Json(new { dic2 });  //works prety well

@verloka das ist nicht die gewünschte Ausgabe

@ Jozkee kommt das also nur in .NET 5 oder wird es in 3.* gehen?

Dies wird nicht auf 3.x zurückportiert, aber Sie können das

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen