Runtime: Mendukung DOM "dinamis" dan dapat ditulis

Dibuat pada 29 Mei 2019  ·  47Komentar  ·  Sumber: dotnet/runtime

Apakah JsonSerializer.Parse(String, Type, JsonSerializerOptions) mendukung jenis pengembalian dinamis ExpandoObject ?

Sesuatu seperti ini:
dynamic p = JsonSerializer.Parse(json, typeof(ExpandoObject));

area-System.Text.Json enhancement json-functionality-doc

Komentar yang paling membantu

Saya juga memiliki kasus penggunaan untuk ini - Saya hanya ingin memanggil REST API dengan HttpClient dan mengambil satu properti dari respons. Saya tidak ingin membuat kelas khusus hanya untuk mengurai respons... Dengan JSON.NET saya bisa menggunakan "dinamis" dan mengakses properti pilihan.

Semua 47 komentar

Tidak, fitur ini tidak didukung pada saat ini tetapi sesuatu yang harus kami pertimbangkan untuk vNext. Apakah Anda memiliki contoh penggunaan untuk memotivasi permintaan fitur?

Menandai sebagai masa depan.

System.NotSupportedException : The collection type 'System.Dynamic.ExpandoObject' is not supported.

@ahsonkhan GraphQL adalah contoh yang baik.

Spesifikasi merekomendasikan JSON tetapi tidak terikat dengan serialisasi tertentu dalam respons.
Ini menyiratkan bahwa bidang "data" dari respons adalah jenis: dinamis. Karena tidak bisa disimpulkan.
Tanpa ExpandoObject, deserialisasi membuat dinamis menjadi jenis pembuatan JSON. Jadi mengakses "data" dinamis abstrak itu harus dilakukan dengan mengetahui bahwa sebenarnya dinamis itu adalah JToken.

Dengan ExpandoObject, saya pikir kami dapat menerapkan akses "data" dinamis seperti objek umum

@ahsonkhan contoh lain adalah Layanan Konfigurasi di proyek kami. Ini memperlihatkan koleksi seperti titik akhir REST, yang membuat koleksi di MongoDB (bukan hanya pembungkus REST dummy, dan koleksi lainnya dan koleksi mongo tidak memiliki pemetaan 1-1 yang tepat, itu juga menegaskan aturan tertentu).

Jadi dalam proyek kami, kami membutuhkan dukungan dinamis/ExpandoObject.

Kami juga menggunakannya di layanan mikro lainnya.

Kami juga mengalami keterbatasan ini. Kasus penggunaan kami secara bertahap membangun objek dinamis sebelum serialisasi json. Kembali ke serializer Json.NET yang lebih matang.

Hai teman-teman

apa jalan-jalan untuk saat ini?
Bolehkah saya mengonfigurasi yang mana yang akan digunakan?
@SidShetye Anda mengatakan sesuatu tentang kembali ke yang lebih dewasa, bisakah Anda menjelaskannya?

@MickeyReznikov , apakah pertanyaan Anda dijawab? Saya percaya @SidShetye berarti kembali menggunakan Newtonsoft.Json untuk membuat serial objek dinamis karena pustaka dalam kotak (System.Text.Json) belum memiliki dukungan untuk itu. Dalam hal aplikasi asp.net, Anda dapat mengonfigurasinya kembali ke AddNewtonsoftJson.

Lihat https://docs.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-3.0&tabs=visual-studio#jsonnet -support

Saya juga memiliki kasus penggunaan untuk ini - Saya hanya ingin memanggil REST API dengan HttpClient dan mengambil satu properti dari respons. Saya tidak ingin membuat kelas khusus hanya untuk mengurai respons... Dengan JSON.NET saya bisa menggunakan "dinamis" dan mengakses properti pilihan.

Dari https://github.com/dotnet/corefx/issues/41472 oleh @ghost1372 :

hai saya harap ini adalah tempat yang baik untuk mengajukan pertanyaan ini
Sepertinya kita tidak bisa membatalkan deserialize objek dinamis
saya menggunakan kode ini tetapi tidak berhasil apakah ada cara untuk melakukan ini?

var objList = System.Text.Json.JsonSerializer.Deserialize<List<dynamic>>(json);

Dalam banyak aplikasi kami, kami mengontrol bidang data dari prosedur tersimpan dan secara dinamis merender halaman Daftar dan Daftar Pencarian dengan jquery.jtable

Tanpa dukungan bawaan JsonSerializer untuk ExpandoObject, kami tidak dapat menggunakan Serialisasi Json bawaan inti dotnet

Kami berbagi kasus penggunaan yang sama seperti yang telah disebutkan oleh @estiler dan @SidShetye
Kami harus beralih kembali ke Json.NET untuk sementara.

Apakah mungkin untuk memiliki pencapaian yang lebih dekat ke _sekarang_ daripada Masa Depan ? 🤔

ExpandoObject sudah lama berada di BCL

Apakah ada alternatif untuk ExpandoObject?

@fatihyildizhan Tidak ada alternatif.

tetapi kami telah menulis konverter ExpandoObject kami sendiri, Anda dapat mengambil petunjuk dari artikel ini ASP.NET Core 3.0: Custom JsonConverter untuk System.Text.Json yang baru

Kami hanya membutuhkan serialisasi, jadi kami hanya membuat serialisasi

Saya terkejut ini belum didukung.

Pindah ini ke 5.0.

Pindah ke 5.0? Itu berarti kita harus menunggu setidaknya satu tahun? Kembali ke JSON.Net.

5.0? wah, pasti ngeselin.

Untuk solusi sementara yang jelek, saya dapat menggunakan JsonDocument dengan konverter khusus untuk itu, tetapi itu adalah IDisposable.

public sealed class EventObject
    {
        [JsonPropertyName("id")]
        public long Id
        {
            get; set;
        }

        [JsonPropertyName("eventData")]
        [JsonConverter(typeof(JsonDocumentConverter))]
        public System.Text.Json.JsonDocument EventData
        {
            get; set;
        }
    }

    internal sealed class JsonDocumentConverter
        : JsonConverter<System.Text.Json.JsonDocument>
    {
        public override JsonDocument Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            return JsonDocument.ParseValue(ref reader);
        }

        public override void Write(Utf8JsonWriter writer, JsonDocument value, JsonSerializerOptions options)
        {
            value.WriteTo(writer);
        }
    }

Kami tidak lagi dapat menggunakan tindakan POST berikut dengan adanya System.Text.Json:

[HttpPost]
public async Task<IActionResult> SubmitAsync(dynamic model)

Sebagai gantinya, kami harus menggunakan metode berikut tetapi tidak ada cara langsung untuk menggunakan 'model' dalam kode backend hilir:

[HttpPost]
public async Task<IActionResult> SubmitAsync(JsonElement model)

'model' adalah objek kompleks dan mungkin berisi kumpulan objek dan/atau objek kompleks bersarang lainnya. Untuk dapat menyimpulkan objek dynamic dari JsonElement , kita harus memanggil model.GetRawText() dan meminta Newtonsoft mendekodekannya menjadi objek dinamis. Cara ini bukanlah cara yang diinginkan karena tujuan utama dari latihan ini adalah untuk menonaktifkan Newtonsoft.json dari proyek.

Dapatkah saya berasumsi bahwa menangani tiket/masalah ini menyiratkan perbaikan untuk masalah yang kami alami? Tampaknya menjadi masalah yang agak mendesak untuk ditangani, jadi bisakah itu diselesaikan lebih cepat daripada nanti?

/cc @ahsonkhan @terrajobst

.NET Core 3.0 JsonSerializer.Deserialize ke objek dinamis

Dukungan JsonSerializer untuk ExpandoObject (tindakan sementara)
Saya pemula, banyak tempat yang tidak sempurna, selamat datang semua orang untuk memodifikasi
.net Core3 tidak mendukung

Tambahkan Konverter Json

tambahkan menggunakan:

  • menggunakan System.Text.Json;
  • menggunakan System.Text.Json.Serialization;

```C#
///


/// Konverter Dinamis Suhu
/// oleh:[email protected]
///

DynamicJsonConverter kelas publik : JsonConverter
{
Baca dinamis menimpa publik (ref Utf8JsonReader reader,
Ketik typeToConvert,
Opsi JsonSerializerOptions)
{

        if (reader.TokenType == JsonTokenType.True)
        {
            return true;
        }

        if (reader.TokenType == JsonTokenType.False)
        {
            return false;
        }

        if (reader.TokenType == JsonTokenType.Number)
        {
            if (reader.TryGetInt64(out long l))
            {
                return l;
            }

            return reader.GetDouble();
        }

        if (reader.TokenType == JsonTokenType.String)
        {
            if (reader.TryGetDateTime(out DateTime datetime))
            {
                return datetime;
            }

            return reader.GetString();
        }

        if (reader.TokenType == JsonTokenType.StartObject)
        {
            using JsonDocument documentV = JsonDocument.ParseValue(ref reader);
            return ReadObject(documentV.RootElement);
        }
        // Use JsonElement as fallback.
        // Newtonsoft uses JArray or JObject.
        JsonDocument document = JsonDocument.ParseValue(ref reader);
        return document.RootElement.Clone();
    }

    private object ReadObject(JsonElement jsonElement)
    {
        IDictionary<string, object> expandoObject = new ExpandoObject();
        foreach (var obj in jsonElement.EnumerateObject())
        {
            var k = obj.Name;
            var value = ReadValue(obj.Value);
            expandoObject[k] = value;
        }
        return expandoObject;
    }
    private object? ReadValue(JsonElement jsonElement)
    {
        object? result = null;
        switch (jsonElement.ValueKind)
        {
            case JsonValueKind.Object:
                result = ReadObject(jsonElement);
                break;
            case JsonValueKind.Array:
                result = ReadList(jsonElement);
                break;
            case JsonValueKind.String:
                //TODO: Missing Datetime&Bytes Convert
                result = jsonElement.GetString();
                break;
            case JsonValueKind.Number:
                //TODO: more num type
                result = 0;
                if (jsonElement.TryGetInt64(out long l))
                {
                    result = l;
                }
                break;
            case JsonValueKind.True:
                result = true;
                break;
            case JsonValueKind.False:
                result = false;
                break;
            case JsonValueKind.Undefined:
            case JsonValueKind.Null:
                result = null;
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }
        return result;
    }

    private object? ReadList(JsonElement jsonElement)
    {
        IList<object?> list = new List<object?>();
        foreach (var item in jsonElement.EnumerateArray())
        {
            list.Add(ReadValue(item));
        }
        return list.Count == 0 ? null : list;
    }
    public override void Write(Utf8JsonWriter writer,
        object value,
        JsonSerializerOptions options)
    {
       // writer.WriteStringValue(value.ToString());
    }
}
## How to Use?

```C#
var serializerOptions = new JsonSerializerOptions
{
    Converters = { new DynamicJsonConverter() }
};
return JsonSerializer.Deserialize<dynamic>("{OK:"200"}", serializerOptions);

@tchivs , solusi Anda berhasil untuk saya; tetapi karena properti Converters bersifat read-only, saya harus melakukan sesuatu seperti ini:
c# var serializerOptions = new JsonSerializerOptions(); serializerOptions.Converters.Add(new DynamicJsonConverter()); return JsonSerializer.Deserialize<dynamic>("{OK:"200"}", serializerOptions);

Coba gunakan tipe JsonElement:

public JsonElement MyProperty {get; set;}

@tchivs , saya membuat beberapa modifikasi pada kode Anda -- mengerjakannya kembali sehingga menggunakan tipe "proyeksi" yang dihasilkan secara dinamis (memiliki subset properti dari tipe dasar), daripada ExpandoObject. Saya memposting kode (dalam contoh proyek konsol) di sini: EDennis.DynamicDeserialization .

Saya akan menguji pendekatan ini dengan objek yang lebih rumit dan dalam berbagai skenario (misalnya, deserialisasi objek dinamis yang digunakan untuk menambal entitas EF Core yang sudah ada dan diketik penuh; deserialisasi objek json dan array untuk kasus uji). Beri tahu saya jika Anda merasa ini berguna atau jika Anda melihat sesuatu yang bermasalah.

Terima kasih atas solusi komunitas. Sangat mengejutkan bahwa Microsoft tidak dapat mengeluarkan fitur ini dalam jangka waktu yang masuk akal. Bekerja dengan respons GraphQL tanpa objek dinamis semacam itu mengarah ke banyak kode verbose dan jelek. Atau bahkan hanya memeriksa keberadaan properti yang sangat bersarang.

Saya membaca utas komentar dan sebagian besar berfokus pada deserialisasi, saya menghadapi masalah di mana juga serialisasi objek dinamis tampaknya diam-diam "gagal". Dalam upaya untuk mereproduksi skenario saya, saya membuat repro minimal berikut:

using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Text.Json;

namespace SampleConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            dynamic d = new CustomDynamicObject();
            d.AProperty = 10;
            var s = JsonSerializer.Serialize(d);

            Console.WriteLine(s);
            Console.Read();
        }
    }

    class CustomDynamicObject : DynamicObject 
    {
        private readonly IDictionary<string, object> properties = new Dictionary<string, object>();

        public override bool TryGetMember(GetMemberBinder binder, out object result) => properties.TryGetValue(binder.Name, out result);

        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            properties[binder.Name] = value;
            return true;
        }

        public override IEnumerable<string> GetDynamicMemberNames()
        {
            foreach (var item in properties.Keys)
            {
                yield return item;
            }
        }
    }
}

Saat dijalankan s adalah {} , jadi serialisasi tidak gagal tetapi menghasilkan objek json kosong.

Apakah ini masalah yang benar? atau haruskah saya menaikkan/mengikuti yang lain?

Apakah ini masalah yang benar? atau haruskah saya menaikkan/mengikuti yang lain?

Ini adalah masalah yang tepat. Tidak perlu membuka satu untuk dua bagian dari fitur yang sama. Masalah ini adalah tentang menambahkan dukungan untuk objek expando (yang berarti untuk kedua sisi, membuat serial dan deserialize).

Saya telah menemui masalah ini hari ini - ingin mengatakan bahwa JsonElement akan berfungsi dengan baik jika tidak bergantung pada dibuang JsonDocument . Salah satu cara yang dapat saya pikirkan untuk mengatasi masalah ini adalah dengan mengimplementasikan destruktor untuk JsonDocument sehingga pembuangannya dapat ditunda ke lain waktu - setelah semua objek JsonElement dikumpulkan.

Saya juga membutuhkan objek dinamis. Namun, itu belum dilaksanakan. Solusi saya adalah menggunakan kamus.
JsonSerializer.Deserialize<Dictionary<string, string>>(response)
Dan kemudian cari kunci yang saya butuhkan :)

Hanya untuk kewarasan saya sendiri - apakah masalah yang dicakup untuk 5.0 khususnya dukungan ExpandoObject atau kemampuan untuk deserialize menjadi objek dinamis? Percakapan di sini sepertinya tidak semuanya cocok dengan judul masalah, dan saya pasti membutuhkan yang terakhir. Dalam kasus saya, saya deserializing dinamis bersarang, khususnya List<Dictionary<string, dynamic>> . Beralih kembali ke Newtonsoft untuk saat ini :(

hanya ingin berbagi beberapa mod gula sintaks c# 8 dari kode bermanfaat @tchivs :)

/// <summary>
/// Temp Dynamic Converter with c# 8
/// by:[email protected]
/// </summary>
public class DynamicJsonConverter : JsonConverter<dynamic>
{
    public override dynamic Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        => reader.TokenType switch
        {
            JsonTokenType.True => true,
            JsonTokenType.False => false,
            JsonTokenType.Number => reader.TryGetInt64(out long l) ? 1 : reader.GetDouble(),
            JsonTokenType.String => reader.TryGetDateTime(out DateTime datetime) ? datetime.ToString() : reader.GetString(),
            JsonTokenType.StartObject =>  ReadObject(JsonDocument.ParseValue(ref reader).RootElement),
            // Use JsonElement as fallback.
                _ =>JsonDocument.ParseValue(ref reader).RootElement.Clone()
        };

    private object ReadObject(JsonElement jsonElement)
    {
        IDictionary<string, object> expandoObject = new ExpandoObject();
        foreach (var obj in jsonElement.EnumerateObject())
        {
            var k = obj.Name;
            var value = ReadValue(obj.Value);
            expandoObject[k] = value;
        }
        return expandoObject;
    }
    private object? ReadValue(JsonElement jsonElement)
        =>
         jsonElement.ValueKind switch
        {
            JsonValueKind.Object => ReadObject(jsonElement),
            JsonValueKind.Array => ReadList(jsonElement),
            JsonValueKind.String => jsonElement.GetString(),
            JsonValueKind.Number =>  jsonElement.TryGetInt64(out long l) ? 1 :0,
            JsonValueKind.True => true,
            JsonValueKind.False =>false,
            JsonValueKind.Undefined => null,
            JsonValueKind.Null => null,
                _ => throw new ArgumentOutOfRangeException()
        };

    private object? ReadList(JsonElement jsonElement)
    {
        var list = new List<object?>();
        jsonElement.EnumerateArray().ToList().ForEach(j => list.Add(ReadValue(j)));
        return list.Count == 0 ? null : list;
    }

    public override void Write(Utf8JsonWriter writer,
        object value,
        JsonSerializerOptions options)
        {
        // writer.WriteStringValue(value.ToString());
        }
}

@ rs38 terima kasih untuk kodenya di sini, persis seperti yang saya butuhkan. Ingin menunjukkan perubahan yang sangat halus tetapi penting yang diperlukan. Dua baris yang menguraikan jenis "Nomor" salah dalam versi terkompresi Anda:

JsonTokenType.Number => reader.TryGetInt64(out long l) ? 1 : reader.GetDouble(),

seharusnya

JsonTokenType.Number => reader.TryGetInt64(out long l) ? l : reader.GetDouble(),

JsonValueKind.Number => jsonElement.TryGetInt64(out long l) ? 1 :0,

seharusnya

JsonValueKind.Number => jsonElement.TryGetInt64(out long l) ? l :0,

@layomia Ini tidak serius. Dukungan seharusnya sudah dilakukan sejak lama (sebenarnya System.Text.Json seharusnya tidak diluncurkan tanpa itu menurut saya)! Dan kita bahkan tidak punya tenggat waktu!

Skenario saya adalah tentang CosmosDB. Saya bertanya menggunakan .NET SDK baru yang menggunakan JsonSerializer ini dan karena ini adalah database tanpa skema, saya tidak ingin membuat kelas untuk setiap proyeksi data database yang perlu saya lakukan (ada banyak pertanyaan berbeda) .
Saya membutuhkan hasil kueri sebagai daftar objek dinamis.

@SocVi100 Jangan berharap pada Microsoft untuk yang satu ini. Lebih baik tetap menggunakan Json.net Newtonsoft. @layomia telah

kenapa kamu tidak berkontribusi? menerapkannya dan dimasukkan ke dalam PR!

https://github.com/dotnet/runtime/blob/master/docs/coding-guidelines/adding-api-guidelines.md
https://github.com/dotnet/runtime/blob/master/docs/area-owners.md (Pemimpin:
@ericstj |, pemilik: @layomia @steveharter @jozkee)

kenapa kamu tidak berkontribusi? menerapkannya dan dimasukkan ke dalam PR!

Aku harap aku bisa.

Skenario saya adalah tentang CosmosDB. Saya bertanya menggunakan .NET SDK baru yang menggunakan JsonSerializer ini dan karena ini adalah database tanpa skema, saya tidak ingin membuat kelas untuk setiap proyeksi data database yang perlu saya lakukan (ada banyak pertanyaan berbeda) .
Saya membutuhkan hasil kueri sebagai daftar objek dinamis.

Anda dapat mengonfigurasi serializer yang berbeda, menggunakan CosmosClientBuilder.WithCustomSerializer .

Berikut ini contohnya: CosmosJsonNetSerializer

Kami tidak dapat memasukkan fitur ini ke dalam versi 5.0. Kami harus memprioritaskan bersama dengan sisa pekerjaan fitur 5.0, dan yang ini tidak cocok. FWIW ini sangat dekat dengan garis potong, sehingga langkahnya terlambat.

@layomia telah

@RobbyDeLaet bisakah kita mencoba menjaga diskusi ini tetap konstruktif? Kami mencoba melakukan yang terbaik untuk menanggapi fitur pelanggan yang paling banyak diminta sambil mempertahankan prinsip-prinsip desain. Sehubungan dengan paritas fitur dengan Newtonsoft.Json, inilah yang harus kami katakan .

System.Text.Json berfokus terutama pada kinerja, keamanan, dan kepatuhan standar. Untuk beberapa skenario, System.Text.Json tidak memiliki fungsionalitas bawaan, tetapi ada solusi yang disarankan. Jika aplikasi Anda bergantung pada fitur yang hilang, pertimbangkan untuk mengajukan masalah untuk mengetahui apakah dukungan untuk skenario Anda dapat ditambahkan.

Kami tidak bertujuan untuk menggantikan Newtonsoft.JSON. Jika berhasil untuk Anda, terus gunakan. Kami akan melakukan yang terbaik untuk membuat System.Text.Json berguna mungkin sambil mempertahankan keuntungan yang telah kami capai dalam kinerja, keamanan, kepatuhan standar, dan pelapisan. Seiring waktu kami berharap untuk membuatnya bekerja untuk orang sebanyak mungkin. Saya mendengar minat di sini dan akan memastikan kami fokus pada ini bergerak maju.

Terima kasih atas saran Anda. Saya akhirnya pergi ke tengah, menggunakan serializer Newtonsoft hanya untuk deserialisasi, tetapi saya masih harus menguji. Saya harap itu tidak bertahan terlalu lama untuk mendapatkan dukungan ExpandoObject pada serializer default CosmosDB sehingga saya bisa menyingkirkan yang lama.
@RobbyDeLaet , terima kasih atas komentar Anda! Saya tidak memiliki banyak harapan tentang Microsoft yang mengimplementasikan apa pun, saya sudah terlalu sering merasa frustrasi tentang mereka. Sebagai pengembang lepas, saya telah menunggu selama bertahun-tahun, secara harfiah, agar fitur-fitur penting diimplementasikan pada infrastruktur CosmosDB, EntityFramework, dan Identity, dan mereka mengabaikan kami dengan caranya sendiri. Tidak peduli berapa banyak suara yang mereka miliki di UserVoice atau apa pun. Saya kira fitur seperti itu tidak diperlukan untuk pengembangannya sendiri sehingga di luar jangkauan ...
Masalahnya selalu sama: Terlalu banyak pemasaran = terlalu banyak berharap

Untuk membantu mereka yang membutuhkan fungsi ini, lihat contoh kode di atas untuk membantu membuka blokir:

  • Dari @tchivs (yang menggunakan ExpandoObject untuk objek dan List<object> untuk koleksi)
  • Dari @denmitchell (yang menggunakan IL Emit untuk objek, bukan ExpandoObject )

Untuk membantu persyaratan fitur ini, saya akan memberikan contoh konverter kustom baru dan akan menautkannya di sini jika sudah siap. Setelah itu saya akan menambahkan sampel itu ke bagian penyelesaian Newtonsoft . cc @tdykstra

Satu detail yang belum dibahas adalah bahwa mendukung dynamic memerlukan referensi ke System.Linq.Expressions.dll assembly yang sangat besar; komplikasi ini mungkin berarti di masa mendatang kami menambahkan konverter dinamis di rakitan baru (misalnya System.Text.Json.Converters.dll ) untuk mendapatkan pembayaran untuk dimainkan bagi mereka yang peduli dengan ukuran penerapan (seperti aplikasi mandiri atau Blazor aplikasi klien).

Hanya untuk kewarasan saya sendiri - apakah masalah yang dicakup untuk 5.0 khususnya dukungan ExpandoObject atau kemampuan untuk deserialize menjadi objek dinamis? Percakapan di sini sepertinya tidak semuanya cocok dengan judul masalah, dan saya pasti membutuhkan yang terakhir. Dalam kasus saya, saya deserializing dinamis bersarang, khususnya List<Dictionary<string, dynamic>> . Beralih kembali ke Newtonsoft untuk saat ini :(

Saya sekarang sedang menguji fungsi lama yang saya terapkan untuk deserialize objek bersarang dan melihat masalah yang sama dicatat dalam topik ini dengan "valueKind".

Apakah ada cara untuk memperbaikinya menggunakan paket neetonsoft.json baru? Karena kode di atas berfungsi untuk objek level root tetapi tidak untuk objek bersarang.

@ericstj Mohon maaf, reaksi saya mungkin agak terlalu negatif dan kasar, tapi seperti @SocVi100 saya seorang pengembang lepas dan terkadang rasa frustrasi menguasai diri. Tapi setidaknya kita punya diskusi dimulai di sini. Terima kasih atas tanggapan Anda, sekarang kami mendapat setidaknya pesan yang jelas tentang status permintaan ini.

Hanya untuk kewarasan saya sendiri - apakah masalah yang dicakup untuk 5.0 khususnya dukungan ExpandoObject atau kemampuan untuk deserialize menjadi objek dinamis

Saya berasumsi semantik yang diinginkan adalah memiliki tipe dinamis sehingga setelah deserializing Anda dapat mengakses semua properti (termasuk bersarang) dengan cara yang terikat akhir:

dynamic obj = JsonSerializer.Deserialize<dynamic>(json);
string s = obj.MyChildProperty.MyStringList[2];

dan saya menganggap semantik yang diinginkan juga mendukung ExpandoObject secara eksplisit:

ExpandoObject expando = JsonSerializer.Deserialize<ExpandoObject>(json);
dynamic obj = expando;
string s = obj.MyChildProperty.MyStringList[2];

Jadi pemahaman saya tentang ruang lingkup:

  • Deserialize<dynamic>(json) mengembalikan baik primitif, koleksi, atau objek. Objek di sini kemungkinan akan menjadi ExpandoObject tetapi dapat berupa IDynamicMetaObjectProvider atau tipe JIT tergantung pada implementasi. Ini berbeda dari hari ini yang selalu mengembalikan JsonElement .
  • Deserialize<ExpandoObject>(json) (yang mengimplementasikan IDynamicMetaObjectProvider ) harus membuat ExpandoObject yang tepat yang pada gilirannya dapat digunakan dengan dynamic . Ini berbeda dari hari ini yang hanya membuat properti-perluasan untuk properti root dan instance JsonElement untuk semua properti bersarang.
  • Serialize<ExpandoObect>() berfungsi seperti yang diharapkan
  • Serialize<IDynamicMetaObjectProvider ()> mungkin atau mungkin tidak diimplementasikan tergantung pada apakah ada skenario di mana ExpandoObject tidak digunakan.

Semantik dalam 3.0 - 5.0:

  • Membuat serial ExpandoObject berfungsi karena ExpandoObject mengimplementasikan IDictionary<string, object> dan STJ akan membuat serial dengan benar setiap nilai object apakah itu primitif, koleksi, atau objek.
  • Deserialisasi menjadi semacam ExpandoObject berfungsi tetapi hanya properti root yang "tepat" expando-properties; setiap properti bersarang akan menjadi JsonElement sehingga ada inkonsistensi dalam model pemrograman sehingga deserializing ExpandoObject harus dihindari kecuali konverter khusus digunakan untuk itu.

Opsi untuk 3.0-5.0:

  • Tulis konverter khusus untuk ExpandoObject dan\atau object seperti yang disebutkan dalam posting di atas; Saya akan segera memberikan tautan ke contoh baru yang idealnya dipetakan ke fitur 6.0.
  • Jika Anda hanya membuat serial, bukan deserialize, Anda dapat menggunakan ExpandoObject (atau dynamic jika tipenya didasarkan pada ExpandoObject ). Karena deserializing ExpandoObject memiliki masalah inkonsistensi hari ini di STJ, ExpandoObject tidak disarankan jika deserializing.
  • Gunakan JsonElement sebagai ganti objek dinamis. JsonElement tidak mencoba untuk "menebak" apa jenis peta JSON, jadi Anda harus eksplisit dengan memanggil GetString(), GetInt32(), dll.

Juga, tergantung pada implementasinya, implementasi dari konverter kustom kemungkinan berarti ada beberapa "tebakan" tentang tipe CLR yang dipetakan oleh JSON selama deserialisasi. Misalnya, string JSON dapat dipetakan ke DateTime atau string , nomor JSON dapat dipetakan ke double atau long , dan Jenis array JSON perlu ditentukan. Ini perlu diperiksa dan dibandingkan dengan semantik Newtonsoft. Ini juga harus selaras dengan tipe apa yang dikembalikan ketika properti bertipe object (hari ini JsonElement ).

Berikut beberapa contoh semantik 3.0 - 5.0 STJ:

using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Diagnostics;
using System.Linq;
using System.Text.Json;

namespace ConsoleApp
{
    class Program
    {
        const string ExpectedJson = "{\"A\":\"A\",\"B\":[1,2],\"C\":42,\"D\":\"2020-01-01T00:00:00\",\"E\":{\"A_Child\":\"A_Child\"}}";

        static void Main(string[] args)
        {
            DateTime dateTime = new DateTime(2020, 1, 1);

            dynamic myDynamicChild = new ExpandoObject();
            myDynamicChild.A_Child = "A_Child";

            dynamic myDynamic = new ExpandoObject();
            myDynamic.A = "A";
            myDynamic.B = new List<int>() { 1, 2 };
            myDynamic.C = 42;
            myDynamic.D = dateTime;
            myDynamic.E = myDynamicChild;

            // Verify we can call late-bound property.
            int c = myDynamic.C;
            Debug.Assert(c == 42);

            // STJ can serialize with ExpandoObject since it implements IDictionary<string, object>.
            string json = JsonSerializer.Serialize<ExpandoObject>(myDynamic);
            Debug.Assert(json == ExpectedJson);

            // Using 'dynamic' against backing ExpandoObject works.
            json = JsonSerializer.Serialize<dynamic>(myDynamic);
            Debug.Assert(json == ExpectedJson);

            // Deserialize with <dynamic>, <object> and <JsonElement>.
            // For 5.0, using one of these is recommended over ExpandoObject because the programming model will be
            // consistent for the root type and all nested types.
            // Using <JsonElement> makes it clear and non-abiguous.
            // Using <object> by default uses 'JsonElement', but can be overridden by a custom converter.
            // Using <dynamic> uses 'object' which uses 'JsonElement'.
            {
                dynamic d = JsonSerializer.Deserialize<dynamic>(json);
                VerifyJsonElement(d);

                try
                {
                    // We will get an exception here if we try to access a dynamic property since 'object' is deserialized
                    // as a JsonElement and not an ExpandoObject.
                    c = d.C;
                    Debug.Fail("Should have thrown Exception!");
                }
                catch (Exception ex)
                {
                    Debug.Assert(ex.Message == "'System.Text.Json.JsonElement' does not contain a definition for 'C'");
                }

                // Serializing with <object> creates a JsonElement by default (can be changed by a custom converter).
                object o = JsonSerializer.Deserialize<object>(json);
                VerifyJsonElement((JsonElement)o);

                // Serialize with explicit <JsonElement>.
                JsonElement e = JsonSerializer.Deserialize<JsonElement>(json);
                VerifyJsonElement(e);
            }

            // Deserialize with ExpandoObject. This creates an ExpandoObject with the root Type having Expando-properties
            // but the value of those properties will be JsonElement. All other nested properties\objects\collections will
            // also be JsonElement. Due to the inconsistency of having only root-level Expando-properties (such as A_Child),
            // deserializing as ExpandoObject is not recommended (unless a ExpandoObject custom converter is used).
            {
                // When STJ deserializes, it creates ExpandoObjects via IDictionary<string, object> where 'object' is JsonElement.
                ExpandoObject expando = JsonSerializer.Deserialize<ExpandoObject>(json);
                Debug.Assert(((IDictionary<string, object>)expando).Keys.Count == 5);
                myDynamic = expando;

                JsonElement jsonElement = myDynamic.A;
                Debug.Assert(jsonElement.GetString() == "A");

                jsonElement = myDynamic.B;
                Debug.Assert(jsonElement.EnumerateArray().Count() == 2);

                jsonElement = myDynamic.C;
                Debug.Assert(jsonElement.GetInt32() == 42);

                jsonElement = myDynamic.D;
                Debug.Assert(jsonElement.GetDateTime() == dateTime);

                jsonElement = myDynamic.E;
                // Here we have an inconsistency. Nested object property must use JsonElement (not a dynamic property).
                Debug.Assert(jsonElement.GetProperty("A_Child").GetString() == "A_Child");

                // Re-serialize works as expected.
                json = JsonSerializer.Serialize<ExpandoObject>(myDynamic);
                Debug.Assert(json == ExpectedJson);

                // Re-serialize works as expected; dynamic works here since backed by ExpandoObject in this example.
                json = JsonSerializer.Serialize<dynamic>(myDynamic);
                Debug.Assert(json == ExpectedJson);
            }

            void VerifyJsonElement(JsonElement elem)
            {
                // Verify JsonElement
                Debug.Assert(elem.GetProperty("A").GetString() == "A");
                Debug.Assert(elem.GetProperty("B").EnumerateArray().Count() == 2);
                Debug.Assert(elem.GetProperty("C").GetInt32() == 42);
                Debug.Assert(elem.GetProperty("D").GetDateTime() == dateTime);
                Debug.Assert(elem.GetProperty("E").GetProperty("A_Child").GetString() == "A_Child");

                // Re-serialize
                json = JsonSerializer.Serialize<dynamic>(elem);
                Debug.Assert(json == ExpectedJson);

                json = JsonSerializer.Serialize<JsonElement>(elem);
                Debug.Assert(json == ExpectedJson);
            }
        }
    }
}

@rs38 Bisakah Anda memperbaiki cuplikan kode Anda berdasarkan apa yang ditunjukkan oleh @ryan-hollister-q2?

Seperti yang dijanjikan, PR yang menyediakan contoh implementasi dinamis ada di https://github.com/dotnet/runtime/pull/42097.

@rs38 Bisakah Anda memperbaiki cuplikan kode Anda berdasarkan apa yang ditunjukkan oleh @ryan-hollister-q2?

itu bukan kode saya, hanya membagikan beberapa cuplikan dari @tchivs sebelumnya di utas ini.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

GitAntoinee picture GitAntoinee  ·  3Komentar

jzabroski picture jzabroski  ·  3Komentar

yahorsi picture yahorsi  ·  3Komentar

nalywa picture nalywa  ·  3Komentar

matty-hall picture matty-hall  ·  3Komentar