Runtime: دعم JsonSerializer لـ TimeSpan في 3.0؟

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

_الأسف إذا تم الرد على هذا بالفعل في مكان آخر ، ولكن إذا كان هذا هو السبب في أن قدراتي في البحث فشلت.

أحاول معاينة الإصدار 6 من 3.0 مع بعض كود تطبيق الإنتاج الحالي ، وإحدى المشكلات التي خرجت من الاختبار هي أن بعض العناصر الحالية التي نستخدمها في عقود واجهة برمجة التطبيقات لدينا تستخدم خصائص تبلغ TimeSpan القيم ، والتي يتم تمثيلها بعد ذلك كسلاسل.

هل تم التخطيط لدعم خصائص TimeSpan لـ 3.0 لـ System.Text.Json APIs الجديدة؟

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

يوجد أدناه اختبار وحدة إعادة بروز صغير يوضح فشل معالجة TimeSpan مقارنةً بكود JSON .NET الحالي.

using System;
using System.Text.Json.Serialization;
using Xunit;
using JsonConvert = Newtonsoft.Json.JsonConvert;
using JsonSerializer = System.Text.Json.Serialization.JsonSerializer;

namespace JsonSerializerTimeSpanNotSupportedException
{
    public static class Repro
    {
        [Fact]
        public static void Can_Deserialize_Object_With_SystemTextJson()
        {
            // Arrange
            string json = "{\"child\":{\"value\":\"00:10:00\"}}";

            // Act (fails in preview 6, throws: System.Text.Json.JsonException : The JSON value could not be converted to System.TimeSpan. Path: $.child.value | LineNumber: 0 | BytePositionInLine: 28.)
            var actual = JsonSerializer.Parse<Parent>(json, new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });

            // Assert
            Assert.NotNull(actual);
            Assert.NotNull(actual.Child);
            Assert.Equal(TimeSpan.FromMinutes(10), actual.Child.Value);
        }

        [Fact]
        public static void Can_Deserialize_Object_With_NewtonsoftJson()
        {
            // Arrange
            string json = "{\"child\":{\"value\":\"00:10:00\"}}";

            var actual = JsonConvert.DeserializeObject<Parent>(json);

            // Assert
            Assert.NotNull(actual);
            Assert.NotNull(actual.Child);
            Assert.Equal(TimeSpan.FromMinutes(10), actual.Child.Value);
        }

        private sealed class Parent
        {
            public Child Child { get; set; }
        }

        private sealed class Child
        {
            public TimeSpan Value { get; set; }
        }
    }
}
api-suggestion area-System.Text.Json json-functionality-doc

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

  • من غير الواضح ما إذا كان هناك ترميز قياسي لفترات زمنية.

سيكون الترميز القياسي للفترات الزمنية هو "مدة" ISO8601 . هذا أيضًا ما يستخدمه XML لتمثيل TimeSpan وقد تم تنفيذه بالفعل في XmlConvert و XsdDuration :

https://github.com/dotnet/corefx/blob/6d723b8e5ae3129c0a94252292322fc19673478f/src/System.Private.Xml/src/System/Xml/XmlConvert.cs#L1128 -L1146

https://github.com/dotnet/corefx/blob/6d723b8e5ae3129c0a94252292322fc19673478f/src/System.Private.Xml/src/System/Xml/Schema/XsdDuration.cs#L229 -L236

الآن ، يمكن القول إن TimeSpan نفسه يجب أن يدعم تحليل وتنسيق تنسيقات المدة ISO8601. ربما سيكون من الأفضل ، كخطوة أولى ، إضافة سلسلة تنسيق قياسية جديدة TimeSpan ، على غرار محدد تنسيق ذهابًا وإيابًا ("O" ، "o") DateTime[Offset] ؟ ستكون إضافة محول بعد ذلك أمرًا بسيطًا حقًا.

ال 36 كومينتر

ليس لدينا حاليًا أي خطط لدعم TimeSpan وستتم إضافته في المستقبل ولكن يمكنني إجراء بعض التحقيقات لمعرفة حجم العمل المطلوب. بدلاً من ذلك ، يمكنك إنشاء JsonConverter الخاص بك لدعم TimeSpan كحل بديل. سأقدم تحديثًا بحلول نهاية الأسبوع المقبل. شكرا.

إذا أردنا إضافته ، فسنرغب أيضًا في إضافة TimeSpan APIs إلى القارئ / الكاتب / JsonElement ، والذي يجب أن يخضع لمراجعة واجهة برمجة التطبيقات.

شكرًا - سيكون المحول المخصص أيضًا طريقة سهلة بما يكفي لجعله يعمل مع تطبيقنا لـ 3.0. هل هذه القدرات مخطط لها للشحن مع المعاينة 7؟

نعم ، أصبح دعم المحول المخصص الآن في الوضع الرئيسي ، وبالتالي سيكون في المعاينة 7.

ومع ذلك ، نظرًا لأن TimeSpan هو نوع BCL شائع ، فلا يزال يتعين علينا توفير محول افتراضي.

راجعنا هذا اليوم:

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

سيكون الترميز القياسي للفترات الزمنية هو "مدة" ISO8601 . هذا أيضًا ما يستخدمه XML لتمثيل TimeSpan وقد تم تنفيذه بالفعل في XmlConvert و XsdDuration :

https://github.com/dotnet/corefx/blob/6d723b8e5ae3129c0a94252292322fc19673478f/src/System.Private.Xml/src/System/Xml/XmlConvert.cs#L1128 -L1146

https://github.com/dotnet/corefx/blob/6d723b8e5ae3129c0a94252292322fc19673478f/src/System.Private.Xml/src/System/Xml/Schema/XsdDuration.cs#L229 -L236

الآن ، يمكن القول إن TimeSpan نفسه يجب أن يدعم تحليل وتنسيق تنسيقات المدة ISO8601. ربما سيكون من الأفضل ، كخطوة أولى ، إضافة سلسلة تنسيق قياسية جديدة TimeSpan ، على غرار محدد تنسيق ذهابًا وإيابًا ("O" ، "o") DateTime[Offset] ؟ ستكون إضافة محول بعد ذلك أمرًا بسيطًا حقًا.

أدى هذا أيضًا إلى تغيير السلوك الافتراضي لكيفية تسليم AspNetCore لنوع TimeSpan في واجهة برمجة التطبيقات ، راجع dotnet / AspnetCore # 11724. قد يكون هذا تغيير جذري.

أبسط حل هو إنشاء محول مخصص:

public class TimeSpanConverter : JsonConverter<TimeSpan>
    {
        public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            return TimeSpan.Parse(reader.GetString());
        }

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

التحدث عن أدلة العملاء: من الضروري للغاية دعم تطوير البرامج لـ TimeSpan.

ركض اليوم في نقل تطبيق إلى .NET Core 3.0 أيضًا. لأن هذا مغلق ، هل هذا يعني أن Microsoft ليس لديها خطط لإضافة دعم محلي؟ يبدو تعليق khellang حجة مقنعة جدًا بالنسبة لي بأنه يجب أن يكون على خريطة طريق في مكان ما ...

إعادة الفتح مقابل 5.0 بناءً على الطلبات الإضافية. من المحتمل أن تكون مدة ISO8601 هي أفضل تمثيل على الرغم من أنه يجب أيضًا مراعاة التوافق مع Newtonsoft.

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

السلوك الافتراضي أسوأ من السلوك السابق وكان غير متوقع تمامًا.

mfeingol ما هو السلوك؟ أنه ببساطة فشل؟

يجب أن نصلحه ، إما باستخدام ISO8601 أو التوافق مع Newtonsoft.

من السهل حقًا إضافة الحل البديل rezabayesteh المذكور.

khellang : ما لاحظته في مشروع ASP.NET Core الفانيليا نسبيًا هو أنه يقوم بتسلسل Timespan? HasValue كحقل

من السهل حقًا إضافة الحل البديل

لقد فعلت ذلك ، لكن هذا لا ينبغي أن يكون ضروريًا لمثل هذا النوع الشائع الاستخدام.

لقد عثرت للتو على هذه المشكلة اليوم (أبلغ عنها عميلي) ويجب أن أعيد جميع تطبيقات وتطبيقات aspnet webapi الخاصة بي لاستخدام Newtonsoft.Json المتسلسل بدلاً من ذلك باستخدام التكوين في Startup.cs:

services.AddControllers ()
.AddNewtonsoftJson (خيارات =>
{
options.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore ؛
}) ؛

في حالاتي ، أستخدم عددًا قليلاً من TimeSpan (TimeSpan؟) والقابل System.Text.Json تسلسلها على النحو التالي:

{
"hasValue": صحيح ،
"القيمة": {
"وضع علامة": 0 ،
"الأيام": 0 ،
"ساعات": 0،
"مللي ثانية": 0 ،
"الدقائق": 0،
"ثواني": 0،
"مجموع الأيام": 0 ،
"totalHours": 0،
"إجمالي ميلي ثانية": 0 ،
"totalMinutes": 0 ،
"totalSeconds": 0
}
}

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

أتوقع نفس نتيجة التسلسل مثل Newtonsoft.Json المسلسل:

{
"timeSpan": "00: 00: 00.0000000"،
"nullTimeSpan": فارغ
}

لقد وجدت هذه المشكلة اليوم (أبلغ عنها عميلي) ويجب أن أعود جميع تطبيقات وتطبيقات aspnet webapi الخاصة بي لاستخدام Newtonsoft.Json serializer بدلاً من ذلك

bashocz لماذا لا يعمل الحل المذكور في https://github.com/dotnet/corefx/issues/38641#issuecomment -540200476 من أجلك؟

لماذا لا يعمل الحل المذكور في # 38641 (تعليق) من أجلك؟

khellang أريد فقط تسليط الضوء على أن تسلسل TimeSpan يمثل مشكلة لمطورين آخرين ، وإيلاء الاهتمام لها. أرغب بشدة في التبديل إلى System.ext.Json ، لكن هناك بعض العقبات.

لقد تحققت من System.Text.Json الجديد قبل بضعة أسابيع ووجدت أنه ليس ميزة كاملة. لقد أثرت مشكلة dotnet / corefx # 41747 وتمت الإشارة إلى dotnet / corefx # 39031 ، dotnet / corefx # 41325 ، dotnet / corefx # 38650 ذات الصلة. لهذا السبب ، لا تزال جميع خدماتنا المصغرة الداخلية تستخدم Newtonsoft.Json.

لسبب غير معروف ، نسيت إدارة المطورين لإصلاحها على واجهات برمجة التطبيقات وتطبيقات الويب العامة أيضًا.

راجع للشغل: أحاول تجنب الحلول البديلة قدر الإمكان في كود الإنتاج .. من الصعب الحفاظ عليه وإزالته في المستقبل.

khellang ، ليس الأمر أن الحل البديل لن ينجح. إنه مجرد شيء أساسي لا يحتاج إلى مطورين لإضافة حل بديل. كميزة كبيرة تم تقديمها لـ .NET core 3 ، لا ينبغي أن تفتقر إلى مثل هذه التطبيقات الأساسية.

arisewanggithub هناك إعدادات عامة متاحة لوحدات التحكم. يمكنك تكوين عبر AddJsonOptions() ، أو يمكنك تمرير JsonSerializerOptions سبيل المثال إلى Json() طريقة المراقب المالي.

هل هذا مغلق لأننا نواجه الحل ؟!

هل هذا مغلق لأننا نواجه الحل ؟!

لا تزال المشكلة مفتوحة ، في انتظار مراجعة وتنفيذ واجهة برمجة التطبيقات / اقتراح السلوك. في غضون ذلك ، من السهل جدًا التعامل مع المحول المذكور في https://github.com/dotnet/corefx/issues/38641#issuecomment -540200476.

بالنسبة لأي شخص يحظره هذا ، إليك حزمة NuGet مع محول (JsonTimeSpanConverter) يمكننا استخدامه قبل الإصدار 5.0: Macross.Json.Extensions

يدعم أنواع TimeSpan & Nullable <TimeSpan>.

أوه ، اللعنة ، هذا يؤلم!

بالنسبة لأي شخص يحظره هذا ، إليك حزمة NuGet مع محول (JsonTimeSpanConverter) يمكننا استخدامه قبل الإصدار 5.0: Macross.Json.Extensions

يدعم TimeSpan & Nullableأنواع.

وفقًا لاختباراتي ، يتم التعامل بالفعل مع أنواع القيم الفارغة بواسطة إطار العمل. لا تحتاج إلى أكثر بكثير مما يلي:

public class DelegatedStringJsonConverter<T> : JsonConverter<T>
    where T : notnull
{
    private static readonly bool s_typeAllowsNull = !typeof(T).IsValueType || Nullable.GetUnderlyingType(typeof(T)) != null;

    private readonly Func<string, T> _parse;
    private readonly Func<T, string> _toString;

    public DelegatedStringJsonConverter(Func<string, T> parse, Func<T, string> toString)
    {
        _parse = parse;
        _toString = toString;
    }

    public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        // null tokens are handled by the framework except when the expected type is a non-nullable value type
        // https://github.com/dotnet/corefx/blob/v3.1.4/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandleNull.cs#L58
        if (!s_typeAllowsNull && reader.TokenType == JsonTokenType.Null)
            throw new JsonException($"{typeof(T)} does not accept null values.");

        return _parse(reader.GetString());
    }

    public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
    {
        // value is presumably not null here as null values are handled by the framework
        writer.WriteStringValue(_toString(value));
    }
}

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

ثم قم بتكوين JsonSerializerOptions النحو التالي:

options.Converters.Add(new DelegatedStringJsonConverter<TimeSpan>(
    value => TimeSpan.Parse(value, CultureInfo.InvariantCulture),
    value => value.ToString(null, CultureInfo.InvariantCulture)));

لا أعتقد أنه من الجيد إضافة تبعية لطرف ثالث إذا كان بإمكانك التعامل مع مثل هذه الأسطر القليلة من التعليمات البرمجية الإضافية. نحن لسنا في أرض NPM بعد كل شيء. ؛)

بالطبع ، سيكون من الأفضل إذا لم نكن بحاجة إلى حل بديل لهذا النوع الأساسي على الإطلاق.

@ adams85 لمعلوماتك يتم التنصت على أنواع القيم

CodeBlanch شكرًا لك على الإشارة إلى هذا JsonConverterAttribute للخارج. إنها بالتأكيد جزء من القصة الكاملة.

وبالطبع ، يتم منحك إضافة المحول الخاص بي إلى lib الخاص بك إذا كنت ترغب في ذلك. :)

بدءًا من .NET 5.0 Preview 5 ، كنت أحصل على أخطاء Cannot skip tokens on partial JSON ، حيث أقوم بتعطيل الكيانات الخاصة بي إلى JSON باستخدام System.Text.Json. السطر والحرف المخالفان هما النقطتان في "Ticks":

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

+1 مني لأن ردة فعلي الأولية عند فتح ملف .json لفحصه فقط للعثور على TimeSpan متسلسل في كل مجدها الهيكلي كان "بالتأكيد ليس ..."

من jsedlak في https://github.com/dotnet/runtime/issues/42356 :

عنوان الإصدار

فئة System.Text.Json.JsonSerializer غير قادرة على إلغاء تسلسل خاصية TimeSpan ، على الرغم من أنها يمكن أن تسلسلها.

عام

يتوفر نموذج لمشروع يوضح هذه المشكلة هنا: https://github.com/jsedlak/TestTimeSpan

إذا قمت بإرجاع خاصية TimeSpan في WebApi ، فسيتم تسلسلها بشكل صحيح إلى JSON:

[ { "forecastLength": { "ticks": 36000000000, "days": 0, "hours": 1, "milliseconds": 0, "minutes": 0, "seconds": 0, "totalDays": 0.041666666666666664, "totalHours": 1, "totalMilliseconds": 3600000, "totalMinutes": 60, "totalSeconds": 3600 } } ]

لكن ملحقات إلغاء التسلسل الافتراضية ( HttpClient.GetFromJsonAsync ) لا يمكنها معالجة الخاصية. تقوم بإرجاع TimeSpan فارغة. يجب استخدام محول مخصص لإلغاء تسلسل الكائن.

معلومات DotNet

NET Core SDK (تعكس أي global.json):
الإصدار: 3.1.302.0
الالتزام: 41faccf259

بيئة التشغيل:
اسم نظام التشغيل: Windows
إصدار نظام التشغيل: 10.0.20201
نظام التشغيل: Windows
RID: win10-x64
المسار الأساسي: C: \ Program Files \ dotnet \ sdk3.1.302 \

المضيف (مفيد للدعم):
الإصدار: 3.1.6.1
الالتزام: 3acd9b0cd1

NET Core SDKs المثبتة:
3.1.302 [C: \ Program Files \ dotnet \ sdk]

NET Core runtimes المثبتة:
Microsoft.AspNetCore.All 2.1.20 [C: \ Program Files \ dotnet \ shared \ Microsoft.AspNetCore.All]
Microsoft.AspNetCore.App 2.1.20 [C: \ Program Files \ dotnet \ shared \ Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 3.1.6 [C: \ Program Files \ dotnet \ shared \ Microsoft.AspNetCore.App]
Microsoft.NETCore.App 2.1.20 [C: \ Program Files \ dotnet \ shared \ Microsoft.NETCore.App]
Microsoft.NETCore.App 3.1.6 [C: \ Program Files \ dotnet \ shared \ Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 3.1.6 [C: \ Program Files \ dotnet \ shared \ Microsoft.WindowsDesktop.App] `

نود معالجة هذا الأمر في .NET 6.0 وسنقدر مساهمة المجتمع هنا. الحل في هذه الأثناء هو استخدام محول مخصص ، مثل https://github.com/dotnet/runtime/issues/29932#issuecomment -540200476. يجب أن يعتمد التنفيذ على تنسيق ISO8601 للمدة لتكون متوافقة مع سلوك DateTime و DateTimeOffset .

نود معالجة هذا في .NET 6.0

لم يتم إصدار .NET 5 بعد ، وسأحب إذا قمنا بتقطيع الرصاصة في الإصدار 5 (يسمح semver بتغييرات كسر رئيسية v3 => v5) بدلاً من انتظار الإصدار 6.

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

فقط لإبراز الحل البسيط باستخدام محول مخصص مرة أخرى:

public class TimeSpanConverter : JsonConverter<TimeSpan>
{
    public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        return TimeSpan.Parse(reader.GetString());
    }

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

var options = new JsonSerializerOptions { Converters = { new TimeSpanConverter() };
JsonSerializer.Serialize(myObj, options);

...

layomia ، يمكنني أخذ هذا مقابل 6.0.

أود أن أوصي باستخدام الثقافة الثابتة عند اختيار إنشاء محول json بنفسك:

public class TimeSpanConverter : JsonConverter<TimeSpan>
{
    public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        return TimeSpan.Parse(reader.GetString(), CultureInfo.InvariantCulture);
    }

    public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(value.ToString(format: null, CultureInfo.InvariantCulture));
    }
}

أنا أعمل مع asp.net الأساسية 3.1 وواجهت هذه المشكلة اليوم. أنا أستخدم شبكة teleriks مع الإشارة والتحرير المضمن في الصفوف التي تحتوي على فترات زمنية لاغية ويمكن أن أرى أن الحمولة تحتوي على بيانات الفترات الزمنية ولكن تفشل عملية إلغاء التسلسل. انتهى بي الأمر باستخدام Macross.Json.Extensions مكتبة وإعادة صياغة بعض جافا سكريبت الخاص بي لجعله يعمل ، ولكن ليس مثاليًا ، آمل أن يحصل هذا على الإصلاح المناسب.

وإذا كنت تبحث عن تنسيق ISO8601 ، فيمكنك استخدام هذا:
ج #

public class TimeSpanConverter : JsonConverter<TimeSpan>
{
    public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        var stringValue = reader.GetString();
        return System.Xml.XmlConvert.ToTimeSpan(stringValue); //8601
    }

    public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options)
    {
        var stringValue = System.Xml.XmlConvert.ToString(value); //8601
        writer.WriteStringValue(stringValue);
    }
}

""

يرجى مراعاة أن TimeSpan 24 ساعة على الأقل .. على سبيل المثال ، يجب أن يكون "24:00:00" معادلاً لـ new TimeSpan(24, 0, 0)

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

يُرجى مراعاة أن TimeSpan لا يقل عن 24 ساعة .. على سبيل المثال ، يجب أن تكون "24:00:00" مساوية لـ TimeSpan الجديد (24 ، 0 ، 0)

هذا ليس شيئًا JSON حقًا ، إنه جزء من منطق تحليل TimeSpan. إجابة قصيرة: استخدم "1.00: 00: 00" لمدة 24 ساعة. إجابة طويلة: لقد ذهبت لاستكشاف شفرة وقت التشغيل مرة أخرى لمعرفة سبب عدم تحليل "24:00:00" كما هو متوقع وكتبت مشاركة مدونة حول هذا الموضوع.

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