Runtime: دعم DOM "الديناميكي" والقابل للكتابة

تم إنشاؤها على ٢٩ مايو ٢٠١٩  ·  47تعليقات  ·  مصدر: dotnet/runtime

هل JsonSerializer.Parse(String, Type, JsonSerializerOptions) دعم دينامية ExpandoObject نوع المقابل؟

شيء من هذا القبيل:
dynamic p = JsonSerializer.Parse(json, typeof(ExpandoObject));

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

التعليق الأكثر فائدة

لدي أيضًا حالة استخدام لهذا - أريد فقط استدعاء واجهة برمجة تطبيقات REST باستخدام HttpClient واسترداد خاصية واحدة من الاستجابة. لا أريد إنشاء فصل دراسي مخصص فقط لتحليل الاستجابة ... باستخدام JSON.NET يمكنني فقط استخدام "ديناميكي" والوصول إلى خاصية الاختيار.

ال 47 كومينتر

لا ، هذه الميزة غير مدعومة في هذه المرحلة ، لكن هناك شيء يجب أن نضعه في الاعتبار بالنسبة لـ vNext. هل لديك مثال على استخدام لتحفيز طلب الميزة؟

بمناسبة المستقبل.

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

ahsonkhan GraphQL مثال جيد.

توصي المواصفات بـ JSON ولكنها غير مرتبطة بأي تسلسل محدد في الاستجابة.
هذا يعني أن مجال "البيانات" للاستجابة من النوع: ديناميكي. كما لا يمكن استنتاجه.
بدون ExpandoObject ، يجعل إلغاء التسلسل الديناميكي نوعًا من صنع JSON. لذا فإن الوصول إلى تلك "البيانات" الديناميكية المجردة يجب أن يتم مع العلم أن الديناميكية في الواقع هي JToken.

مع ExpandoObject ، أعتقد أنه يمكننا فرض الوصول إلى "البيانات" الديناميكية مثل كائن شائع

ahsonkhan مثال آخر هو خدمة التكوين في مشروعنا. يعرض مجموعة مثل نقاط نهاية REST ، التي تنشئ مجموعات في MongoDB (إنها ليست مجرد غلاف REST وهمي ، ومجموعات الباقي ومجموعة mongo لا تحتوي على تعيين دقيق 1-1 ، فهي تؤكد أيضًا قواعد معينة).

لذلك نحتاج في مشروعنا إلى دعم ديناميكي / إكسباندو أوبجيكت.

نحن نستخدمه أيضًا في خدمات مصغرة أخرى.

لقد واجهنا أيضًا هذا القيد. تقوم حالة الاستخدام الخاصة بنا ببناء كائن ديناميكي تدريجيًا قبل تسلسل json. عدت إلى برنامج Json.NET المتسلسل الأكثر نضجًا.

مرحبا شباب

ما هو التجول الآن؟
هل يمكنني تكوين أي واحد لاستخدام؟
SidShetye قلت شيئًا عنه عاد إلى أكثر نضجًا ، هل يمكن أن تشرح من فضلك؟

MickeyReznikov : راجع https://stackoverflow.com/questions/15455304/deserialize-a-property-as-an-expandoobject-using-json-net أو google “json.net expandoobject”

MickeyReznikov ، هل تمت الإجابة على سؤالك؟ أعتقد أن SidShetye يعني العودة إلى استخدام Newtonsoft.Json لتسلسل الكائنات الديناميكية نظرًا لأن المكتبة الواردة في الصندوق (System.Text.Json) لا تدعم هذه حتى الآن. في حالة تطبيقات asp.net ، يمكنك تكوينه للعودة إلى AddNewtonsoftJson.

راجع https://docs.microsoft.com/en-us/aspnet/core/migration/22-to-30؟view=aspnetcore-3.0&tabs=visual-studio#jsonnet -support

لدي أيضًا حالة استخدام لهذا - أريد فقط استدعاء واجهة برمجة تطبيقات REST باستخدام HttpClient واسترداد خاصية واحدة من الاستجابة. لا أريد إنشاء فصل دراسي مخصص فقط لتحليل الاستجابة ... باستخدام JSON.NET يمكنني فقط استخدام "ديناميكي" والوصول إلى خاصية الاختيار.

من https://github.com/dotnet/corefx/issues/41472 بواسطة @ ghost1372 :

مرحباً ، آمل أن يكون هذا مكانًا جيدًا لطرح هذا السؤال
يبدو أننا لا نستطيع إلغاء تسلسل الكائنات الديناميكية
لقد استخدمت هذا الكود ولكني لم أعمل ، فهل هناك طريقة للقيام بذلك؟

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

في تطبيقاتنا العديدة ، نتحكم في حقول البيانات من الإجراءات المخزنة ونعرض ديناميكيًا صفحات القائمة وقائمة البحث باستخدام jquery.jtable

بدون دعم JsonSerializer المدمج لـ ExpandoObject ، لا يمكننا استخدام Dotnet core المدمج في Json Serialization

نحن نشارك نفس حالة الاستخدام كما ذكرنا سابقًا بواسطة estiller وSidShetye
كان علينا العودة إلى Json.NET في هذه الأثناء.

هل من الممكن أن يكون هناك معلم أقرب إلى _ الآن_ من المستقبل ؟ 🤔

كان ExpandoObject في BCL لفترة طويلة

هل يوجد بديل لـ ExpandoObject؟

fatihyildizhan لا يوجد بديل.

لكننا كتبنا محول ExpandoObject الخاص بنا ، يمكنك أخذ التلميح من هذه المقالة ASP.NET Core 3.0: Custom JsonConverter لنظام System.Text.Json الجديد

نحتاج فقط إلى التسلسل ، لذلك نقوم فقط بإنشاء التسلسل

لقد فوجئت أن هذا غير مدعوم حتى الآن.

نقل هذا إلى 5.0.

الانتقال إلى 5.0؟ هذا يعني أننا يجب أن ننتظر لمدة عام على الأقل؟ عودة إلى JSON.Net.

5.0؟ واو ، هذا سيء بالتأكيد.

بالنسبة للحل المؤقت القبيح ، يمكنني استخدام JsonDocument مع محول مخصص له ، لكنه قابل للتصرف.

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

لم نعد قادرين على استخدام إجراء POST التالي في وجود System.Text.Json:

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

بدلاً من ذلك ، كان علينا استخدام الطريقة التالية ، ولكن لم يكن هناك أمر مباشر لاستخدام "النموذج" في التعليمات البرمجية الخلفية المصب:

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

"النموذج" هو كائن معقد وقد يحتوي على مجموعة من الكائنات و / أو غيرها من الكائنات المعقدة المتداخلة. لكي نتمكن من إتمام كائن dynamic من JsonElement ، كان علينا استدعاء model.GetRawText () وجعل Newtonsoft فك تشفيره إلى كائن ديناميكي. هذه الطريقة ليست هي الطريقة المرغوبة لأن الغرض الرئيسي من هذا التمرين هو إخراج Newtonsoft.json من المشروع.

هل يمكنني أن أفترض أن معالجة هذه البطاقة / المشكلة تتضمن إصلاحًا لمشكلتنا التي كنا نواجهها؟ يبدو أنها مسألة ملحة إلى حد ما يجب معالجتها ، فهل يمكن معالجتها عاجلاً وليس آجلاً؟

تضمين التغريدة

NET Core 3.0 JsonSerializer.Deserialize إلى كائن ديناميكي

دعم JsonSerializer لـ ExpandoObject (إجراءات مؤقتة)
أنا مبتدئ ، العديد من الأماكن ليست مثالية ، أرحب بالجميع للتعديل
.net Core3 لا يوجد دعم

أضف محول Json

أضف باستخدام:

  • باستخدام System.Text.Json ؛
  • باستخدام System.Text.Json.Serialization ؛

ج #
///


/// Temp Dynamic Converter
/// بواسطة: [email protected]
///

DynamicJsonConverter الفئة العامة: JsonConverter
{
القراءة الديناميكية العامة للتجاوز (المرجع Utf8JsonReader reader ،
اكتب typeToConvert ،
خيارات الخيارات)
{

        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 ،
c# var serializerOptions = new JsonSerializerOptions(); serializerOptions.Converters.Add(new DynamicJsonConverter()); return JsonSerializer.Deserialize<dynamic>("{OK:"200"}", serializerOptions);

جرب استخدام نوع JsonElement:

public JsonElement MyProperty {get; set;}

tchivs ، لقد أجريت بعض التعديلات على التعليمات البرمجية الخاصة بك - أعدت صياغتها بحيث تستخدم أنواع "الإسقاط" التي تم إنشاؤها ديناميكيًا (لها مجموعة فرعية من الخصائص من النوع الأساسي) ، بدلاً من ExpandoObject. لقد نشرت الكود (في نموذج مشروع وحدة التحكم) هنا: EDennis.DynamicDeserialization .

سأختبر هذا الأسلوب مع كائنات أكثر تعقيدًا وفي ظل سيناريوهات مختلفة (على سبيل المثال ، إلغاء تسلسل الكائنات الديناميكية المستخدمة لتصحيح كيانات EF Core الموجودة والمكتوبة بالكامل ؛ وإلغاء تسلسل كائنات json والمصفوفات لحالات الاختبار). اسمحوا لي أن أعرف إذا وجدت هذا مفيدًا أو إذا كنت ترى أي مشكلة.

شكرا لحل المجتمع. من المدهش أن Microsoft غير قادرة على طرح هذه الميزة في إطار زمني معقول. يؤدي العمل مع استجابات GraphQL بدون كائن ديناميكي من نوع ما إلى الكثير من التعليمات البرمجية المطولة والقبيحة. أو حتى مجرد التحقق من وجود خصائص متداخلة بعمق.

قرأت سلسلة التعليقات ومعظمها يركز على إلغاء التسلسل ، وأواجه مشكلة حيث يبدو أيضًا أن تسلسل الكائنات الديناميكية "يفشل" بصمت. في محاولة لإعادة إنتاج السيناريو الخاص بي ، توصلت إلى الحد الأدنى من التكرار التالي:

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

عند تشغيل s هو {} ، فإن التسلسل لا يفشل ولكنه ينتج كائن json فارغًا.

هل هذه هي القضية الصحيحة؟ أم يجب أن أرفع / أتبع واحدة مختلفة؟

هل هذه هي القضية الصحيحة؟ أم يجب أن أرفع / أتبع واحدة مختلفة؟

هذه هي القضية الصحيحة. لا حاجة لفتح واحد لنصفي نفس الميزة. تتعلق هذه المشكلة بإضافة دعم لكائن expando (مما يعني إجراء تسلسل وإلغاء تسلسل للجانبين).

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

أنا أيضا بحاجة إلى كائن ديناميكي. ومع ذلك ، لم يتم تنفيذه بعد. كان الحل هو استخدام قاموس.
JsonSerializer.Deserialize<Dictionary<string, string>>(response)
ثم ابحث عن المفتاح الذي أحتاجه :)

فقط من أجل سلامة عقلي - هل تم تحديد نطاق الإصدار 5.0 على وجه التحديد لدعم ExpandoObject أم القدرة على إلغاء التسلسل إلى كائن ديناميكي؟ لا يبدو أن المحادثة هنا تتطابق مع عنوان المشكلة ، وأنا بالتأكيد بحاجة إلى الأخير. في حالتي ، أقوم بإلغاء تسلسل ديناميكية متداخلة ، على وجه التحديد List<Dictionary<string, dynamic>> . تم الرجوع إلى Newtonsoft الآن :(

أردت فقط مشاركة بعض تعديلات السكر في بناء الجملة c # 8 لشفرة مفيدة

/// <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 بفضل الكود هنا ، كان بالضبط ما احتاجه. أردت أن أشير إلى تغيير دقيق للغاية ولكنه مهم مطلوب. السطران اللذان يحللان النوع "الرقم" غير صحيحين في نسختك المضغوطة:

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

يجب ان يكون

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

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

يجب ان يكون

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

layomia هذا ليس جدياً. كان يجب تقديم الدعم منذ وقت طويل (في الواقع ، لم يكن يجب إطلاق System.Text.Json بدونه في رأيي)! وليس لدينا حتى موعد نهائي!

السيناريو الخاص بي هو حول CosmosDB. أنا أستعلم باستخدام .NET SDK الجديد الذي يستخدم JsonSerializer هذا ولأنه قاعدة بيانات غير مخطط ، لا أريد إنشاء فئة لكل إسقاط لبيانات قاعدة البيانات التي أحتاج إلى القيام بها (هناك مجموعة من الاستعلامات المختلفة) .
أحتاج إلى نتائج الاستعلامات كقوائم كائنات ديناميكية.

@ SocVi100 لا تضع آمالك على Microsoft في هذا الأمر. أفضل التمسك بـ Json.net من Newtonsoft. لقد أزاحتlayomia كل الآمال في التكامل في المستقبل القريب. يبدو أنهم لا يهتمون حقًا باحتياجات المطور. في البداية صرخوا "انسى json.net ، لقد قمنا بتغطيتك!" لحسن الحظ ، لم ينس المطورون json.net أو ما شابه.

لماذا لا تساهم تنفيذه ووضع العلاقات العامة!

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 (العميل المحتمل:
ericstjالمالكين:layomiasteveharterjozkee)

لماذا لا تساهم تنفيذه ووضع العلاقات العامة!

أتمنى لو أستطيع.

السيناريو الخاص بي هو حول CosmosDB. أنا أستعلم باستخدام .NET SDK الجديد الذي يستخدم JsonSerializer هذا ولأنه قاعدة بيانات غير مخطط ، لا أريد إنشاء فئة لكل إسقاط لبيانات قاعدة البيانات التي أحتاج إلى القيام بها (هناك مجموعة من الاستعلامات المختلفة) .
أحتاج إلى نتائج الاستعلامات كقوائم كائنات ديناميكية.

من الممكن تكوين مُسلسل مختلف باستخدام CosmosClientBuilder.WithCustomSerializer .

هنا مثال: CosmosJsonNetSerializer

لم نتمكن من احتواء هذه الميزة في 5.0. كان علينا تحديد الأولويات جنبًا إلى جنب مع بقية عمل ميزة 5.0 ، وهذا لم يكن مناسبًا. FWIW كان هذا قريبًا جدًا من خط القطع ، وبالتالي الحركة المتأخرة.

لقد أزاحتlayomia كل الآمال في التكامل في المستقبل القريب. يبدو أنهم لا يهتمون حقًا باحتياجات المطور. في البداية صرخوا "انسى json.net ، لقد قمنا بتغطيتك!" لحسن الحظ ، لم ينس المطورون json.net أو ما شابه.

RobbyDeLaet هل يمكننا محاولة إبقاء هذه المناقشة بناءة؟ نحن نحاول بذل قصارى جهدنا للاستجابة لأعلى ميزات العملاء المطلوبة مع الحفاظ على مبادئ التصميم. فيما يتعلق بميزة التكافؤ مع Newtonsoft.Json ، هذا ما يجب أن نقوله .

System.ext.Json يركز بشكل أساسي على الأداء والأمان والامتثال للمعايير. بالنسبة لبعض السيناريوهات ، لا يحتوي System.Text.Json على وظائف مضمنة ، ولكن هناك حلول موصى بها. إذا كان التطبيق الخاص بك يعتمد على ميزة مفقودة ، ففكر في تقديم مشكلة لمعرفة ما إذا كان يمكن إضافة دعم للسيناريو الخاص بك.

لا نهدف إلى استبدال Newtonsoft.JSON. إذا كان يعمل من أجلك ، فاستمر في استخدامه. سنبذل قصارى جهدنا لجعل System.ext.Json مفيدًا قدر الإمكان مع الحفاظ على المكاسب التي حققناها في الأداء والأمان والامتثال للمعايير والطبقات. مع مرور الوقت ، نأمل أن نجعلها تعمل لأكبر عدد ممكن من الأشخاص. أسمع الاهتمام هنا وسأحرص على التركيز على المضي قدمًا.

شكرا لاقتراحاتكم. لقد ذهبت أخيرًا إلى المنتصف ، باستخدام برنامج Newtonsoft المتسلسل فقط لإلغاء التسلسل ، ولكن لا يزال يتعين علي الاختبار. آمل ألا يستمر كثيرًا الحصول على دعم ExpandoObject على المسلسل الافتراضي لـ CosmosDB حتى أتمكن من التخلص من القديم.
RobbyDeLaet ، شكرا لتعليقك! ليس لدي الكثير من التوقعات بشأن قيام Microsoft بتنفيذ أي شيء ، لقد شعرت بالإحباط مرات كثيرة حيالها. بصفتي مطورًا مستقلاً ، فقد انتظرت لسنوات ، حرفيًا ، تنفيذ الميزات الأساسية على البنية التحتية CosmosDB و EntityFramework و Identity ، وهم يتجاهلوننا بطريقتهم الخاصة. لا يهم عدد الأصوات التي لديهم في UserVoice أو أي شيء آخر. أنا أزعم أن مثل هذه الميزات ليست ضرورية للتطورات الخاصة بها ، لذا فهي خارج النطاق ...
المشكلة هي نفسها دائمًا: الكثير من التسويق = الكثير من التوقعات

لمساعدة أولئك الذين يحتاجون إلى هذه الوظيفة ، راجع نماذج التعليمات البرمجية أعلاه للمساعدة في إلغاء حظر:

  • من tchivs (التي تستخدم ExpandoObject للعناصر و List<object> للمجموعات)
  • من denmitchell (الذي يستخدم IL Emit للكائنات بدلاً من ExpandoObject )

للمساعدة في تلبية متطلبات هذه الميزة ، سأقدم عينة محول مخصصة جديدة وسأربطها هنا عندما تكون جاهزة. بعد ذلك سأحصل على هذه العينة المضافة إلى قسم حلول Newtonsoft . MustafaHosny اللهم امين

أحد التفاصيل التي لم تتم مناقشتها بعد هو أن دعم dynamic يتطلب مرجعًا للتجميع الكبير جدًا System.Linq.Expressions.dll ؛ قد يعني هذا التعقيد في المستقبل أن نضيف المحول الديناميكي في مجموعة جديدة (على سبيل المثال System.Text.Json.Converters.dll ) للحصول على الدفع مقابل اللعب لأولئك الذين يهتمون بحجم النشر (مثل تطبيق مستقل أو Blazor تطبيق العميل).

فقط من أجل سلامة عقلي - هل تم تحديد نطاق الإصدار 5.0 على وجه التحديد لدعم ExpandoObject أم القدرة على إلغاء التسلسل إلى كائن ديناميكي؟ لا يبدو أن المحادثة هنا تتطابق مع عنوان المشكلة ، وأنا بالتأكيد بحاجة إلى الأخير. في حالتي ، أقوم بإلغاء تسلسل ديناميكية متداخلة ، على وجه التحديد List<Dictionary<string, dynamic>> . تم الرجوع إلى Newtonsoft الآن :(

أقوم الآن باختبار وظيفة قديمة قمت بتطبيقها لإلغاء تسلسل الكائنات المتداخلة وأرى نفس المشكلة التي تمت ملاحظتها في هذا الموضوع باستخدام "valueKind".

هل كانت هناك طريقة لإصلاح ذلك باستخدام حزم neetonsoft.json الجديدة؟ لأن الكود أعلاه يعمل لكائن مستوى الجذر ولكن ليس للكائنات المتداخلة.

ericstj اعتذار ، ربما كان رد فعلي سلبيًا وقاسًا بعض الشيء ، لكن مثل السائدة . لكن على الأقل بدأنا مناقشة هنا. شكرًا لك على ردك ، لقد تلقينا الآن على الأقل رسالة واضحة حول حالة هذا الطلب.

فقط من أجل سلامة عقلي - هل تم تحديد المشكلة لـ 5.0 على وجه التحديد دعم ExpandoObject أو القدرة على إلغاء التسلسل إلى كائن ديناميكي

أفترض أن الدلالات المرغوبة يجب أن يكون لها نوع ديناميكي ، لذا بعد إلغاء التسلسل ، يمكنك الوصول إلى جميع الخصائص (بما في ذلك المتداخلة) بطريقة محددة لاحقًا:

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

وأفترض أن الدلالات المرغوبة تدعم أيضًا ExpandoObject صراحة:

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

لذا فهمي للنطاق:

  • Deserialize<dynamic>(json) بإرجاع إما مجموعة أو كائن بدائي. من المحتمل أن يكون الكائن هنا ExpandoObject ولكن يمكن أن يكون IDynamicMetaObjectProvider أو نوع JITed اعتمادًا على التنفيذ. هذا يختلف عن اليوم الذي دائمًا ما يُرجع JsonElement .
  • يجب أن ينشئ Deserialize<ExpandoObject>(json) (الذي يطبق IDynamicMetaObjectProvider ) ExpandoObject مناسبًا والذي بدوره يمكن استخدامه مع dynamic . هذا يختلف عن اليوم الذي ينشئ فقط خصائص التوسيع لخصائص الجذر ومثيلات JsonElement لجميع الخصائص المتداخلة.
  • Serialize<ExpandoObect>() يعمل كما هو متوقع
  • قد يتم تنفيذ Serialize<IDynamicMetaObjectProvider ()> وقد لا يتم تنفيذه اعتمادًا على ما إذا كانت هناك سيناريوهات لا يتم فيها استخدام ExpandoObject .

دلالات في 3.0 - 5.0:

  • تسلسل عمل ExpandoObject منذ ExpandoObject ينفذ IDictionary<string, object> وستقوم STJ بتسلسل كل object بشكل صحيح سواء كانت مجموعة أو كائن بدائي.
  • إلغاء التسلسل إلى نوع ExpandoObject من الأعمال ولكن فقط خصائص الجذر هي خصائص expando "مناسبة" ؛ أي خصائص متداخلة ستكون JsonElement لذلك هناك تناقض في نموذج البرمجة وبالتالي يجب تجنب إلغاء تسلسل ExpandoObject ما لم يتم استخدام محول مخصص لذلك.

خيارات 3.0-5.0:

  • اكتب محولًا مخصصًا لـ ExpandoObject و \ أو object كما هو مذكور في المنشورات أعلاه ؛ سأقدم رابطًا قريبًا لعينة جديدة تحدد بشكل مثالي ميزة 6.0.
  • إذا قمت فقط بالتسلسل ، وليس إلغاء التسلسل ، فيمكنك استخدام ExpandoObject (أو dynamic إذا كان النوع يعتمد على ExpandoObject ). نظرًا لأن إلغاء التسلسل ExpandoObject به مشكلة عدم تناسق اليوم في STJ ، لا يوصى باستخدام ExpandoObject إذا تم إلغاء التسلسل.
  • استخدم JsonElement بدلاً من العناصر الديناميكية. لا يحاول JsonElement "تخمين" نوع خرائط JSON ، لذلك عليك أن تكون صريحًا من خلال استدعاء GetString () ، GetInt32 () ، إلخ.

أيضًا ، اعتمادًا على التنفيذ ، من المحتمل أن يعني تنفيذ محول مخصص أن هناك بعض "التخمينات" حول أنواع CLR لخرائط JSON أثناء إلغاء التسلسل. على سبيل المثال ، قد يتم تعيين سلسلة JSON إما إلى DateTime أو string ، وقد يتم تعيين رقم JSON إلى double أو long ، و يجب تحديد نوع مصفوفة JSON. يجب فحص هذا ومقارنته بدلالات Newtonsoft. يجب أن يتماشى هذا أيضًا مع النوع الذي يتم إرجاعه عندما تكون الخاصية من النوع object (اليوم هو JsonElement ).

فيما يلي بعض الأمثلة على دلالات 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 هل يمكنك إصلاح مقتطف الشفرة بناءً على ما أشار إليه @ ryan-hollister-q2؟

كما وعدت ، تقدم العلاقات العامة عينة تنفيذ ديناميكية على https://github.com/dotnet/runtime/pull/42097.

@ rs38 هل يمكنك إصلاح مقتطف الشفرة بناءً على ما أشار إليه @ ryan-hollister-q2؟

لم يكن هذا الرمز الخاص بي ، لقد قمت فقط بمشاركة بعض المقتطفات من tchivs سابقًا في هذا الموضوع.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات