Runtime: Asp.NET Core 3.0 Json baru tidak membuat kamus bersambung<key/>

Dibuat pada 7 Agu 2019  ·  51Komentar  ·  Sumber: dotnet/runtime

.NET Core 3.0 Pratinjau 7

Asp.NET Web Apis, ketika mengembalikan Kamus gagal dengan NotSupportedException. Saya telah menyertakan pengecualian di bawah ini.

Juga, metode ControllerBase.BadRequest mengambil ModelStateDictionary , tetapi ketika itu dikembalikan, serializer juga meledak dengan NotSupportedException, tetapi pesan yang sedikit berbeda.

Kapan dukungan ini akan ditambahkan? Karena ini telah didukung di Json.net dan serializer lainnya untuk sementara waktu, saya harap ini ada di radar.

Saya sangat menghargai kenyataan bahwa saya dapat memilih kembali untuk menggunakan Json.net, jadi terima kasih banyak untuk itu!

Pengecualian saat mengembalikan Kamus
System.NotSupportedException: Jenis koleksi 'System.Collections.Generic.Dictionary`2[System.Int32,System.String]' tidak didukung.
di System.Text.Json.JsonClassInfo.GetElementType(Jenis propertyType, Type parentType, MemberInfo memberInfo, opsi JsonSerializerOptions)
di System.Text.Json.JsonClassInfo.CreateProperty(Ketik dinyatakanPropertyType, Ketik runtimePropertyType, PropertyInfo propertyInfo, Ketik parentClassType, opsi JsonSerializerOptions)
di System.Text.Json.JsonClassInfo.AddProperty(Jenis propertyType, PropertyInfo propertyInfo, Type classType, opsi JsonSerializerOptions)
di System.Text.Json.JsonClassInfo.AddPolicyProperty(Ketik propertyType, opsi JsonSerializerOptions)
di System.Text.Json.JsonClassInfo..ctor (Jenis tipe, opsi JsonSerializerOptions)
di System.Text.Json.JsonSerializerOptions.GetOrAddClass(Tipe classType)
di System.Text.Json.WriteStackFrame.Initialize (Jenis tipe, opsi JsonSerializerOptions)
di System.Text.Json.JsonSerializer.WriteAsyncCore(Stream utf8Json, Object value, Type type, JsonSerializerOptions options, CancellationToken CancellationToken)
di Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(Konteks OutputFormatterWriteContext, Encoding selectedEncoding)
di Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(Konteks OutputFormatterWriteContext, Encoding selectedEncoding)
di Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|21_0(Invoker ResourceInvoker, hasil IActionResult)
di Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Ditunggu|29_0 TFilter,TFilterAsinkron
di Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed konteks)
di Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext TFilter,TFilterAsync
di Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
--- Akhir jejak tumpukan dari lokasi sebelumnya di mana pengecualian dilemparkan ---
di Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|19_0(Invoker ResourceInvoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
di Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|17_1(pemanggil ResourceInvoker)
di Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
di Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke (konteks HttpContext)
di Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke (konteks HttpContext)
di Microsoft.AspNetCore.Builder.Extensions.MapWhenMiddleware.Invoke(konteks HttpContext)
di Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
di Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
di Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke (konteks HttpContext)

Pengecualian saat mengembalikan BadRequest
System.NotSupportedException: Jenis koleksi 'Microsoft.AspNetCore.Mvc.SerializableError' tidak didukung.
di System.Text.Json.JsonClassInfo.GetElementType(Jenis propertyType, Type parentType, MemberInfo memberInfo, opsi JsonSerializerOptions)
di System.Text.Json.JsonClassInfo.CreateProperty(Ketik dinyatakanPropertyType, Ketik runtimePropertyType, PropertyInfo propertyInfo, Ketik parentClassType, opsi JsonSerializerOptions)
di System.Text.Json.JsonClassInfo.AddProperty(Jenis propertyType, PropertyInfo propertyInfo, Type classType, opsi JsonSerializerOptions)
di System.Text.Json.JsonClassInfo.AddPolicyProperty(Ketik propertyType, opsi JsonSerializerOptions)
di System.Text.Json.JsonClassInfo..ctor (Jenis tipe, opsi JsonSerializerOptions)
di System.Text.Json.JsonSerializerOptions.GetOrAddClass(Tipe classType)
di System.Text.Json.WriteStackFrame.Initialize (Jenis tipe, opsi JsonSerializerOptions)
di System.Text.Json.JsonSerializer.WriteAsyncCore(Stream utf8Json, Object value, Type type, JsonSerializerOptions options, CancellationToken CancellationToken)
di Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(Konteks OutputFormatterWriteContext, Encoding selectedEncoding)
di Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(Konteks OutputFormatterWriteContext, Encoding selectedEncoding)
di Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|21_0(Invoker ResourceInvoker, hasil IActionResult)
di Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Ditunggu|29_0 TFilter,TFilterAsinkron
di Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed konteks)
di Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext TFilter,TFilterAsync
di Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
--- Akhir jejak tumpukan dari lokasi sebelumnya di mana pengecualian dilemparkan ---
di Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|19_0(Invoker ResourceInvoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
di Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|17_1(pemanggil ResourceInvoker)
di Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
di Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke (konteks HttpContext)
di Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke (konteks HttpContext)
di Microsoft.AspNetCore.Builder.Extensions.MapWhenMiddleware.Invoke(konteks HttpContext)
di Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
di Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
di Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke (konteks HttpContext)

area-System.Text.Json enhancement

Komentar yang paling membantu

Untuk pendatang baru, solusi sementara adalah kembali ke Newtonsoft.Json .

  1. Tambahkan referensi paket ke Microsoft.AspNetCore.Mvc.NewtonsoftJson .
  2. Tambahkan .AddNewtonsoftJson() tepat setelah .AddControllers() / .AddMvc() atau kombinasi lainnya.

Semua 51 komentar

Kedua kesalahan "tidak didukung pengecualian" adalah batasan dalam serializer bawaan dan dirancang (setidaknya untuk apa yang dikirimkan dalam 3.0).

Kapan dukungan ini akan ditambahkan? Karena ini telah didukung di Json.net dan serializer lainnya untuk sementara waktu, saya harap ini ada di radar.
Saya sangat menghargai kenyataan bahwa saya dapat memilih kembali untuk menggunakan Json.net, jadi terima kasih banyak untuk itu!

Ada banyak kemampuan serializer yang ada di radar kami untuk mendukung di vNext (jadi 5.0) dan seterusnya, dengan dukungan kamus khusus menjadi salah satunya.

Asp.NET Web Apis, ketika mengembalikan Kamus gagal dengan NotSupportedException

Saat membuat serial, hanya Dictionary<string, TValue> yang didukung hari ini (yaitu TKey yang berupa string). Kamus Anda adalah <int, string> yang tidak didukung.
https://github.com/dotnet/corefx/blob/93d7aa1c1737b6da29d04b78557215e18eb786d4/src/System.Text.Json/tests/Serialization/DictionaryTests.cs#L385 -L390

@steveharter , @layomia - apakah ada solusi potensial di sini untuk sementara? Apa yang diperlukan untuk menambahkan dukungan untuk kamus non-string key'd dalam serializer untuk 5.0?

System.NotSupportedException: Jenis koleksi 'Microsoft.AspNetCore.Mvc.SerializableError' tidak didukung.

~Menambahkan dukungan untuk tipe seperti SerializableError tidak ada dalam radar saya. @pranavkm , @rynowak - apa konteksnya di sini? Saya tidak akrab dengan ModelStateDictionary , dapatkah ini didukung di dalam mvc itu sendiri dengan konverter khusus?~

Sunting: Tidak apa-apa , itu sudah diperbaiki.

System.NotSupportedException: Jenis koleksi 'Microsoft.AspNetCore.Mvc.SerializableError' tidak didukung.

Ini adalah masalah yang diketahui https://github.com/aspnet/AspNetCore/issues/11459 yang baru-baru ini diperbaiki (sebagai bagian dari pratinjau 8): https://github.com/dotnet/corefx/pull/39001

Terima kasih banyak atas balasan cepat Anda @ahsonkhan!

"Batasan" kunci menjadi string sebenarnya masuk akal ketika saya memikirkannya. Saya melihat sekarang bahwa Json.net benar-benar menghasilkan json dengan kuncinya adalah string, ketika deserializing itu hanya akan memberi saya int kembali. Pasti akan menyenangkan memiliki dukungan untuk kunci non-string di masa depan, tetapi bukan penghenti acara.

Oke, senang mendengar bahwa Mvc.SerializableError yang tidak didukung telah diperbaiki. Adakah ide tentang apakah ada harapan untuk tanggal rilis Pratinjau 8? Mencoba mencari dan menemukan sesuatu, tetapi tidak melihat apa pun tentang itu.

Setelah pratinjau 8 keluar, kami akan mencoba mencoba lagi .net core 3 json serialization library, tetapi untuk saat ini kami harus tetap menggunakan Json.net

@steveharter , @layomia - apakah ada solusi potensial di sini untuk sementara? Apa yang diperlukan untuk menambahkan dukungan untuk kamus non-string key'd dalam serializer untuk 5.0?

>

@ahsonkhan @willyt150 solusi untuk ini adalah dengan menggunakan konverter khusus yang mengimplementasikan JsonConverter<T> mana T adalah Dictionary<int, string> .
Lihat https://github.com/dotnet/corefx/issues/36639#issue -429928740 untuk beberapa contoh.

Adakah ide tentang apakah ada harapan untuk tanggal rilis Pratinjau 8?

Beberapa waktu kemudian bulan ini.

Memikirkan hal ini lagi, menghapus up-for-grabs untuk saat ini karena itu mungkin sesuatu yang tidak ingin kami dukung secara default.

Terima kasih @layomia, saya akan memeriksanya.

Terima kasih @ahsonkhan , nantikan perbaikannya!

Dari @namse (dari https://github.com/dotnet/corefx/issues/40404):

Halo. Ketika saya mencoba membuat kamus bersambung dengan kunci integer, itu membuang System.NotSupportedException.

Saya pikir masuk akal untuk mendukung serialisasi Json yang Kamus memiliki kunci ToString -able. misalnya, ketika kita menjalankan ToString untuk int atau boolean, ia mengembalikan "123" atau "true". Saya pikir kunci itu adalah kunci ToString -able.

versi

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

Kode

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

Mengharapkan

"{"5": "five"}"

Tapi apa yang terjadi?

Kesalahan System.NotSupportedException Dilempar

Sebenarnya, ada masalah kompatibilitas ketika saya mengubah Newtonsoft.Json ke System.Text.Json. Mereka mengembalikan string seperti yang saya harapkan. Saya pikir System.Text.Json tidak harus kompatibel tapi... Anda tahu.

Saya telah menerapkan konverter yang mendukung Serialisasi dan Deserialisasi untuk IDictionary<TKey, TValue> mana TKey memiliki metode statis 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;
        }
    }
}

Tes:

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

Hasil:

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

Namun masih tidak dapat membuat serial Kamus bersarang seperti Dictionary<int, Dictionary<int, int>> karena System.Text.Json tidak menerima tipe dalam Dictionary<int, int> . Saya pikir itu bug.

Namun masih tidak dapat membuat serial Kamus bersarang seperti Kamus> karena System.Text.Json tidak menerima Kamus tipe dalam. Saya pikir itu bug.

Hanya mendukung <string, x> berdasarkan desain karena pemotongan lingkup untuk dikirimkan untuk v3.0. Rilis 3.0 dimaksudkan untuk menjadi produk minimal yang layak dengan skenario paling umum yang didukung.

@steveharter Setidaknya Anda tidak boleh membuang pengecualian yang tidak mendukung ketika ada konverter yang bisa digunakan.

Apakah ada rencana untuk mendukung ini di .net core 3.1?

Untuk pendatang baru, solusi sementara adalah kembali ke Newtonsoft.Json .

  1. Tambahkan referensi paket ke Microsoft.AspNetCore.Mvc.NewtonsoftJson .
  2. Tambahkan .AddNewtonsoftJson() tepat setelah .AddControllers() / .AddMvc() atau kombinasi lainnya.

@steveharter Setidaknya Anda tidak boleh membuang pengecualian yang tidak mendukung ketika ada konverter yang bisa digunakan.

Ya itu adalah poin yang adil. Mungkin kita bisa menghapus batasan ini untuk 3.1. cc @layomia

Juga hanya untuk memperjelas bahwa elemen kamus hari ini diserialkan seperti properti yang dimungkinkan karena kuncinya adalah string. Mendukung kunci non-string berarti elemen akan diserialisasi sebagai KeyValuePair.

Ya ampun, punya masalah ini setelah saya memutakhirkan ke 3.0.

Harus menginstal paket newton dengan AddNewtonsoftJson.

Dari @israellot di https://github.com/dotnet/corefx/issues/41345

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

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

Serialisasi sederhana ini ditangani dengan baik oleh perpustakaan json Newtonsoft default sebelumnya dengan membuat serial kunci int sebagai string. Pada System.Text.Json melempar pengecualian yang tidak didukung.

@israellot , @unruledboy , dan lainnya di utas, dapatkah Anda memberikan detail tentang mengapa model objek Anda memerlukan Kamus dengan kunci integer dalam skenario Anda dan mengapa mengubahnya menjadi Dictionary<string, TValue> tidak akan berfungsi? Saya ingin melihat beberapa penggunaan untuk mengumpulkan persyaratan dan membantu memotivasi perbaikan.

Mereka akan mendapatkan serial sebagai string, jadi saya tidak mengerti dalam skenario apa Anda ingin kamus yang mendasari Anda memiliki kunci int32 sebagai gantinya.

@ahsonkhan Saya percaya motivasi utama adalah kompatibilitas.
Serializer default sebelumnya adalah Newtonsoft, jadi pengguna mungkin telah menulis seluruh model dengan mengandalkan kemampuannya untuk membuat serial dan deserialize kelas arbitrer. Migrasi dari 2.X ke 3.0 sekarang akan menyebabkan perubahan senyap karena kita hanya akan mengetahui ketidakcocokan saat runtime.

Saya percaya banyak skenario melibatkan penggunaan json seperti halnya transportasi melintasi kabel, dan dalam kasus ini, model domainnya mungkin Kamus. Saran Anda bermuara pada pembuatan Kamus objek DTO terpisahdan konversi di antara keduanya, tampaknya agak tidak efisien karena sekarang kita perlu mengalokasikan objek lain hanya agar sesuai dengan serializer.
Melihat secara ketat ke serializer, membatasi kamus untuk memiliki kunci string adalah logis karena itulah satu-satunya representasi json yang mungkin. Tetapi mengingat bahwa serializer berperan dalam aplikasi, saya yakin yang terbaik adalah menghilangkan gesekan sebanyak mungkin.

Saya mengalami masalah ini dengan program kecil yang saya tulis di mana bagian dari label versi disediakan melalui file json.
Bagian label memiliki kunci yang menentukan indeks tempat bagian label dapat dimasukkan. Ini berarti kuncinya adalah nilai numerik, mis

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

Menggunakan Newtonsoft, json dapat di-deserialized tanpa masalah ke Dictionary<int, string> . Setelah mengonversi ke System.Text.Json serialisasi gagal.

Saya telah menyelesaikan masalah ini dengan membuat DictionaryConverter saya sendiri, dan DictionaryConverter .
Saya juga membuat konverter sederhana yang memungkinkan bilangan bulat dideserialisasi dari string

Ini kemudian didaftarkan melalui opsi serializer: https://github.com/Kieranties/SimpleVersion/blob/master/src/SimpleVersion.Core/Serialization/Serializer.cs#L22

Perubahan ini memungkinkan kunci kamus untuk dideserialisasi alih-alih langsung dibaca sebagai string. Ini selanjutnya membuka dukungan untuk kunci menjadi tipe arbitrer yang dapat memiliki konverter mereka sendiri yang terdaftar untuk serialisasi (mis. enum/tipe/tipe yang dapat diserialisasi sebagai kunci unik, dll)

Saya belum secara resmi menguji hal-hal tetapi dalam perkembangan saat ini tampaknya telah menyelesaikan masalah.

Menyetel pencapaian untuk 3.1 untuk menghapus batasan apa pun yang mencegah pembuatan konverter kustom yang dapat menangani TKey untuk Dictionary<TKey,TValue> .

Pembaruan: Saya telah menambahkan sampel yang berfungsi dengan 3.0. Saya tidak melihat ada masalah seperti kamus bersarang seperti yang dilaporkan di atas.

Ada dua format JSON yang digunakan dalam sampel:

  • Objek JSON dengan properti string: {"1":"val1","2":"val2"} meskipun TKey bukan string.

    • Ini bergantung pada metode TryParse pada jenis kunci yang didukung terkait. Untuk v3.0 tidak layak untuk memberikan dukungan untuk TKey digeneralisasi karena metode TryParse dapat berbeda untuk TKey dan karena ada pengaturan budaya yang perlu disediakan (biasanya Invarian). Jadi contoh di bawah ini adalah untuk satu jenis TKey .

    • Sample Dictionary<int, string> . Ini adalah contoh sederhana untuk TValue == string .

    • Sample Dictionary<Guid, TValue> . Ini dapat menangani TValue .

    • Sample Dictionary<TKey, TValue> where TKey is Enum . Untuk Enum; ini dapat menangani TValue .

  • Array JSON dengan entri KeyValuePair: [{"Key":1,"Value":"val1"},{"Key":2,"Value":"val2"}]

Jika sampel ini memuaskan, saya akan mengubah masalah ini menjadi 5.0 untuk membahas apakah kami menyediakan dukungan bawaan yang tidak memerlukan konverter khusus.

Menetapkan tonggak sejarah ke 5.0 untuk dipertimbangkan (bagaimana jika salah satu contoh di atas harus berfungsi secara default).

Deserialisasi juga tampaknya memetakan tipe objek generik ke JsonDocument daripada tipe normal (primitif?).

Contoh:

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"] ditampilkan sebagai: ValueKind = Number : "86"
NewtonSoftJson[0]["id"] ditampilkan sebagai: 86

@steveharter Kamus enum yang dikunci dengan konverter itu bersambung sebagai:
{ "Stuff": [ { "Key": 1, "Value": "String" }, { "Key": 3, "Value": "String" }, { "Key": 2, "Value": "String" } ] }

Sementara JSON.NET memberikan apa yang saya anggap kebanyakan orang harapkan:
{ "Stuff": { "Item1": "String", "Item2": "String", "Item3": "String" }

Deserialisasi juga tampaknya memetakan tipe objek generik ke JsonDocument daripada tipe normal (primitif?).

Ya itu dengan desain. Lihat https://github.com/dotnet/corefx/issues/38713

@steveharter Kamus enum yang dikunci dengan konverter itu bersambung sebagai:
{ "Barang": [ { "Kunci": 1, "Nilai": "String" }, { "Kunci": 3, "Nilai": "String" }, { "Kunci": 2, "Nilai": " Rangkaian" } ] }
Sementara JSON.NET memberikan apa yang saya anggap kebanyakan orang harapkan:
{ "Barang": { "Item1": "String", "Item2": "String", "Item3": "String" }

Saya berasumsi Anda menggunakan "Kamus Contoh"? Jika demikian, ya yang menggunakan KeyValuePair yang memiliki properti "Kunci" dan "Nilai". Saya tidak memberikan contoh untuk kamus enum berbasis TKey yang bersambung sebagai nama properti, tetapi saya akan berupaya menambahkannya.

Ya, yang itu. Dan ah ok, saya pikir Anda bermaksud itu sebagai serializer kamus generik.
Tertarik untuk melihat sampel baru Anda jika tersedia, karena yang saat ini kami gunakan tampaknya tidak secepat yang saya inginkan.

@roguecode inilah contoh Enum untuk Dictionary<TKey, TValue> mana TKey adalah Enum dan menggunakan sintaks JSON "properti" alih-alih KeyValuePair. Saya juga memperbarui daftar sampel di atas untuk memasukkan sampel baru ini.

Halo, saya memiliki sesuatu yang mirip tetapi berbeda dan saya ingin tahu apakah Anda dapat mengarahkan saya ke tempat lain untuk mencari.

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

Startup berjalan sebagai 3.0:

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

Jika saya memiliki objek ProblemDetails ini:

// 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"

Di 2.2, mengeksekusi Newtonsoft.Json.JsonConvert.SerializeObject(problemDetails) mengembalikan

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

Dalam 3.0 ia mengembalikan:

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

String serial versi 3.0 termasuk IDictionarynama properti, Extensions , dan kami dapat membatalkan deserialisasi string itu dengan benar di 3.0. Anda dapat melihat bahwa nama properti ini tidak disertakan dalam versi 2.x.

Masalahnya adalah serialisasi 3.0 yang terjadi ketika respons dikembalikan dari filter menggunakan BadRequestObjectResult , dari kode di bawah ini:

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

..., konten respons yang dikembalikan adalah bentuk yang sama dengan versi 2.2 (nama properti Extensions tidak termasuk), yang menyebabkan properti Extensions deserialize sebagai koleksi kosong (menggunakan Newtonsoft.Json.JsonConvert.DeserializeObject<ValidationProblemDetails>() )

Entah bagaimana serialisasi ini tidak menggunakan serialisasi yang sama dengan perpustakaan Newtonsoft yang kami coba deserialize.

Terima kasih atas pertimbangannya!

Saya memiliki sesuatu yang mirip tetapi berbeda

@ts46235 bisakah Anda membuka masalah baru untuk ini karena ini berbeda dari masalah saat ini?

@ ts46235 Saya menanggapi pertanyaan Anda dalam masalah lain yang Anda buka di sini - https://github.com/aspnet/AspNetCore/issues/16618. Menandai percakapan di sini sebagai di luar topik.

diperbarui ke Core 3.1 dan masih belum diperbaiki

Saya baru saja memutakhirkan ke 3.1 dan dipukul dengan ini. Kembali ke JSON.NET saya pergi ... (Saya menggunakan kunci GUID)

Inti bersih titik 3.1
Kamusjuga tidak berfungsi, bahkan objek di kuncinya sebenarnya adalah string

Saya juga baru saja menemukan ini, dan batasan diam yang menakutkan itu, karena seperti yang telah ditunjukkan orang lain, Anda tidak akan melihat ini pada waktu kompilasi. Dalam kasus saya, saya ingin membuat serial Dictionary<int, List<string>> , yang menurut saya tidak terlalu eksotis.

Mereka harus memperbaikinya tetapi saya melihat ini berkali-kali bahkan dengan formatter lama, formatter biner newtsoft awal, kamus dalam kamus, kamus dengan antarmuka. Mereka harus memperbaikinya tetapi jika Anda tidak ingin masalah orang benar - benar tidak boleh menempatkan objek kompleks seperti Kamus dalam kontrak serialisasi Anda meminta masalah - newtsoft telah memanjakan orang . Lihat semua properti publik pada jumlah Kamus dll. Anda mengandalkan sesuatu yang khusus di serializer untuk memetakan ini.

Sayangnya tidak ada tipe sederhana untuk ini di C # untuk nama properti sehingga Kamus dipaksa. Jadi aku hanya sedih..

Inilah solusinya tetapi tidak berarti solusi lengkap:

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

Mungkin dukungan untuk (beberapa) serializer tipe umum dapat ditambahkan OOTB, misalnya list ini , dan juga jika salah satu dari tipe ini .IsAssignableFrom(systemDotObjectInstance.GetType()) mendukung Dictionary<<ins i="7">object</ins>, V> .

Berikut adalah proposal untuk menambahkan dukungan ke jenis TKey non-string dalam kamus.
https://github.com/dotnet/runtime/pull/32676

Harap beri tahu kami pemikiran atau masalah apa pun.

Secara khusus, berikan umpan balik tentang kumpulan tipe yang @Jozkee rencanakan untuk mendukung kunci kamus, terutama jika Anda memerlukan tipe lain untuk didukung (pada dasarnya, semua tipe numerik primitif bawaan, enum, dan beberapa lainnya):
https://github.com/dotnet/runtime/blob/a5d96c0e280b56412d4614848f5ee3b1e0d7f216/src/libraries/System.Text.Json/docs/KeyConverter_spec.md#keyconverter

Kamusjuga tidak berfungsi, bahkan objek di kuncinya sebenarnya adalah string

@andrew-vdb, mendukung serialisasi kunci objek arbitrer kemungkinan akan tetap tidak didukung. Namun, jika tipe runtime dari kunci objek adalah salah satu dari tipe "baru didukung", maka serialisasi akan berfungsi untuk itu setelah fitur selesai. Deserialisasi, bagaimanapun, akan tetap sebagai kotak JsonElement (sampai masalah terkait untuk memilih ke dalam perilaku yang berbeda ini ditangani): https://github.com/dotnet/runtime/issues/29960

@Jozkee apa tipe yang diaktifkan untuk TValue ? Agaknya apa pun yang saat ini dapat Anda ceritakan sebagai objek mandiri?

Agaknya apa pun yang saat ini dapat Anda ceritakan sebagai objek mandiri?

Ya.

Deserialisasi juga tampaknya memetakan tipe objek generik ke JsonDocument daripada tipe normal (primitif?).

Contoh:

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"] ditampilkan sebagai: ValueKind = Number : "86"
NewtonSoftJson[0]["id"] ditampilkan sebagai: 86

Dari semua masalah yang disebutkan, ini yang paling mengganggu saya. Daftaratau T[] atau Kamusharus deserialize dengan benar untuk semua tipe yang dapat dipetakan langsung dari beberapa tipe yang dimiliki Json hingga tipe CLR.

Rilis 3.0 dimaksudkan untuk menjadi produk minimal yang layak dengan skenario paling umum yang didukung

Saya bertanya-tanya bagaimana Daftar> bukan salah satu skenario paling umum:

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

Karena ini juga deserialize ke System.Text.JsonElement, di mana saya harapkan double (Number) dan string (String)

Saya mengalami masalah ini dengan program kecil yang saya tulis di mana bagian dari label versi disediakan melalui file json.
Bagian label memiliki kunci yang menentukan indeks tempat bagian label dapat dimasukkan. Ini berarti kuncinya adalah nilai numerik, mis

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

Menggunakan Newtonsoft, json dapat di-deserialized tanpa masalah ke Dictionary<int, string> . Setelah mengonversi ke System.Text.Json serialisasi gagal.

Saya telah menyelesaikan masalah ini dengan membuat DictionaryConverter saya sendiri, dan DictionaryConverter .
Saya juga membuat konverter sederhana yang memungkinkan bilangan bulat dideserialisasi dari string

Ini kemudian didaftarkan melalui opsi serializer: https://github.com/Kieranties/SimpleVersion/blob/feature/netcore3/src/SimpleVersion.Core/Serialization/Serializer.cs#L20

Perubahan ini memungkinkan kunci kamus untuk dideserialisasi alih-alih langsung dibaca sebagai string. Ini selanjutnya membuka dukungan untuk kunci menjadi tipe arbitrer yang dapat memiliki konverter mereka sendiri yang terdaftar untuk serialisasi (mis. enum/tipe/tipe yang dapat diserialisasi sebagai kunci unik, dll)

Saya belum secara resmi menguji hal-hal tetapi dalam perkembangan saat ini tampaknya telah menyelesaikan masalah.

hai @Kieranties , github menautkan 404 untuk saya

hai @Kieranties , github menautkan 404 untuk saya

@AirEssY Saya telah memperbaiki tautan di komentar asli saya, menurut saya sekarang ada di master di: https://github.com/Kieranties/SimpleVersion/tree/master/src/SimpleVersion.Core/Serialization

Jika Kamus setara dengan Peta JavaScript, maka apa pun (tipe JS yang diwakili dalam C #) harus dapat diterima,

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

Contoh pendekatan standar untuk deserialize Peta di JS adalah:

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

console.log(countries)

Yang menghasilkan:

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

TL;DR: membatasi kunci ke string tidak cocok dengan JS

@Jozkee jadi ini masuk .NET 5 saja atau akan masuk ke 3.*?

@onionhammer .NET 5.0, Anda juga dapat mencoba fitur ini di pratinjau berikutnya (5.0 preview8).
Tidak ada rencana untuk porting ini ke 3.x.

Solusi untuk 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 itu bukan keluaran yang diinginkan

@Jozkee jadi ini masuk .NET 5 saja atau akan masuk ke 3.*?

Ini tidak akan di-backport ke 3.x tetapi Anda dapat menambahkan menggunakan paket System.Text.Json NuGet di proyek Anda untuk mendapatkan semua fitur baru di .NET 5.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat