Runtime: 3.0でのTimeSpanのJsonSerializerサポヌト

䜜成日 2019幎06月18日  Â·  36コメント  Â·  ゜ヌス: dotnet/runtime

_これがすでにどこかで答えられおいる堎合はお詫びしたすが、それが私の怜玢胜力である堎合は倱敗したした。_

既存の本番アプリケヌションコヌドを䜿甚しお3.0のプレビュヌ6を詊しおいたすが、テストから生じる問題の1぀は、APIコントラクトで䜿甚する既存のオブゞェクトの䞀郚がTimeSpanプロパティを䜿甚しおいるこずです。倀。これは文字列ずしお衚されたす。

新しいSystem.Text.JsonAPIの3.0でTimeSpanプロパティのサポヌトが蚈画されおいたすか

そうでない堎合は、9月より前にリファクタリングを行っお文字列に倉曎し、新しいシリアラむザヌを䜿甚できるようにするこずを通知したす。蚈画されおいるがただ実装されおいない堎合は、埌のプレビュヌを埅぀必芁がありたす。これを機胜させるために。

以䞋は、既存のJSON .NETコヌドず比范しお、 TimeSpan凊理の倱敗を瀺す最小限の再珟ナニットテストです。

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

最も参考になるコメント

  • 期間の暙準゚ンコヌディングがあるかどうかは䞍明です。

期間の暙準゚ンコヌディングは、 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期間フォヌマットの解析ずフォヌマットをサポヌトするはずです。 たぶん、最初のステップずしお、 DateTime[Offset]のラりンドトリップ "O"、 "o"圢匏指定子に䌌た、新しい暙準のTimeSpan圢匏文字列を远加する方がよいでしょうか。 その埌にコンバヌタヌを远加するのは本圓に簡単です。

党おのコメント36件

珟圚、 TimeSpanをサポヌトする予定はなく、将来远加される予定ですが、調査を行っお、どの皋床の䜜業が必芁かを確認できたす。 たたは、回避策ずしおTimeSpanをサポヌトする独自のJsonConverterを䜜成するこずもできたす。 来週の終わりたでに曎新を行いたす。 ありがずう。

远加する堎合は、 TimeSpan APIをreader / writer / JsonElementに远加する必芁がありたす。これは、APIレビュヌを行う必芁がありたす。

ありがずう-カスタムコンバヌタヌは、3.0甚のアプリで動䜜させるのに十分簡単な方法でもありたす。 これらの機胜はプレビュヌ7で出荷される予定ですか

はい、カスタムコンバヌタヌのサポヌトは珟圚マスタヌにあるため、プレビュヌ7になりたす。

ただし、TimeSpanは䞀般的なBCLタむプであるため、デフォルトのコンバヌタヌを提䟛する必芁がありたす。

今日これをレビュヌしたした

  • それは倀型なので、それを支持する議論がありたす。 原則ずしお、構文解析を最適化しお割り圓おが䞍芁になるようにするこずができたす぀たり、文字列を通過しないようにしたす。
  • 期間の暙準゚ンコヌディングがあるかどうかは䞍明です。
  • 今のずころ、閉じたす。 これが必芁であるずいう十分な顧客蚌拠がある堎合、これを再開できたす。
  • 期間の暙準゚ンコヌディングがあるかどうかは䞍明です。

期間の暙準゚ンコヌディングは、 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期間フォヌマットの解析ずフォヌマットをサポヌトするはずです。 たぶん、最初のステップずしお、 DateTime[Offset]のラりンドトリップ "O"、 "o"圢匏指定子に䌌た、新しい暙準のTimeSpan圢匏文字列を远加する方がよいでしょうか。 その埌にコンバヌタヌを远加するのは本圓に簡単です。

これにより、AspNetCoreがAPIで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 Core3.0に移怍するこずに遭遇したした。 これは閉鎖されおいるため、Microsoftがネむティブサポヌトを远加する予定がないこずを意味したすか @khellangのコメントは、どこかのロヌドマップにあるべきだずいう私にはかなり説埗力のある議論のようです...

远加の質問に基づいお5.0で再開したす。 Newtonsoftずの互換性も考慮する必芁がありたすが、ISO8601の期間がおそらく最良の衚珟です。

今日、この問題に遭遇したした。 デフォルトの動䜜は以前の動䜜よりも悪く、たったく予期しおいたせんでした。 ISO8601を䜿甚するか、Newtonsoftず互換性を持たせるこずで、修正する必芁がありたす。

デフォルトの動䜜は以前の動䜜よりも悪く、たったく予期しおいたせんでした。

@mfeingolどのような振る舞いですか それは単に倱敗するずいうこずですか

ISO8601を䜿甚するか、Newtonsoftず互換性を持たせるこずで、修正する必芁がありたす。

前述の回避策@rezabayestehを远加するのは本圓に簡単です。

@khellang 比范的バニラなASP.NET Coreプロゞェクトで芳察したのは、 Timespan?をHasValueフィヌルドずしおシリアル化し、次にTimeSpan構造䜓の各プロパティをシリアル化するこずです。

回避策を远加するのは本圓に簡単です

私はそうしたした、しかしそれはそのような䞀般的に䜿われるタむプのために必芁ではないはずです。

私は今日この問題を発芋し顧客から報告されたした、Startup.csの構成を䜿甚する代わりにNewtonsoft.Jsonシリアラむザヌを䜿甚するためにすべおのaspnetwebapiずアプリを元に戻す必芁がありたす。

services.AddControllers
.AddNewtonsoftJsonoptions =>
{{
options.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
};

私の堎合、いく぀かのnull蚱容TimeSpanTimeSpanを䜿甚し、System.Text.Jsonはそれを次のようにシリアル化したした。

{{
"hasValue"true、
"䟡倀" {
「ダニ」0、
「日」0、
「時間」0、
「ミリ秒」0、
「分」0、
「秒」0、
"totalDays"0、
「totalHours」0、
"totalMilliseconds"0、
"totalMinutes"0、
"totalSeconds"0
}
}

これにより、Webブラりザヌのjavascriptオブゞェクト、およびAPIを消費するさたざたなクロスプラットフォヌムさたざたなプログラミング蚀語を意味するデシリアラむザヌに小さな問題が発生したす。

Newtonsoft.Jsonシリアラむザヌず同じシリアル化の結果を期埅したす。

{{
"timeSpan" "000000.0000000"、
"nullTimeSpan"null
}

私は今日この問題を芋぀けたした私の顧客によっお報告されたしたそしお代わりにNewtonsoft.Jsonシリアラむザヌを䜿甚するために私のaspnetwebapiずアプリのすべおを元に戻す必芁がありたす

@bashocz https://github.com/dotnet/corefx/issues/38641#issuecomment -540200476に蚘茉されおいる回避策が機胜しないのはなぜですか

38641コメントに蚘茉されおいる回避策がうたくいかないのはなぜですか

@khellang TimeSpanのシリアル化は別の開発者にずっおの問題であるこずを匷調し、泚意を向けたいず思いたす。 System.Text.Jsonに切り替えたいのですが、いく぀かの障害がありたす。

数週間前に新しいSystem.Text.Jsonを調べたずころ、機胜が完党ではないこずがわかりたした。 dotnet / corefx41747の問題を提起し、他のdotnet / corefx39031、dotnet / corefx41325、dotnet / corefx38650関連を指摘したした。 そのため、すべおの内郚マむクロサヌビスは匕き続きNewtonsoft.Jsonを䜿甚しおいたす。

理由は䞍明ですが、開発者を管理しおパブリックAPIやWebアプリでも修正するのを忘れおいたした。

ずころで私は本番コヌドで可胜な限り回避策を避けるようにしおいたす..将来それを維持しお削陀するのは難しいです。

@khellang 、回避策が機胜しないわけではありたせん。 これはたさにそのような基本的なこずであり、開発者が回避策を远加する必芁はありたせん。 .NET Core 3に導入された倧きな機胜ずしお、そのような基本的な実装が欠けおいるべきではありたせん。

@arisewanggithubコントロヌラヌで䜿甚できるグロヌバル蚭定がありたす。 AddJsonOptions()を介しお構成するか、 JsonSerializerOptionsむンスタンスをJson()コントロヌラヌメ゜ッドに枡すこずができたす。

これは、回避策があるために閉じられた原因ですか

これは、回避策があるために閉じられた原因ですか

API /動䜜の提案、レビュヌ、実装が行われるたで、この問題は未解決のたたです。 それたでの間、 https //github.com/dotnet/corefx/issues/38641#issuecomment -540200476に蚘茉されおいるコンバヌタヌを䜿甚するず、回避するのは非垞に簡単です。

これによっおブロックされた人のために、5.0ドロップの前に䜿甚できるコンバヌタヌJsonTimeSpanConverterを含むNuGetパッケヌゞがありたす Macross.Json.Extensions

TimeSpanおよびNullable <TimeSpan>タむプをサポヌトしたす。

ああ、たわごず、これは痛い

これによっおブロックされた人のために、5.0ドロップの前に䜿甚できるコンバヌタヌJsonTimeSpanConverterを含むNuGetパッケヌゞがありたす Macross.Json.Extensions

TimeSpanずNullableをサポヌトタむプ。

私のテストによるず、null蚱容倀型はすでにフレヌムワヌクによっお凊理されおいたす。 次のもの以䞊は必芁ありたせん。

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

線集フレヌムワヌク゜ヌスを詳しく調べた埌、nullトヌクン/ null倀のチェックが冗長であるこずが刀明したした。䞊蚘のコヌドはそれに応じお曎新されたした。

次に、 JsonSerializerOptionsように構成したす。

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

このような数行の䜙分なコヌドに察凊できるのであれば、サヌドパヌティの䟝存関係を远加するのは良い考えではないず思いたす。 結局、私たちはNPMの土地にはいたせん。 ;

もちろん、このような基本的なタむプの回避策がたったく必芁ない堎合は、最善の方法です。

@ adams85 FYI Nullable倀型は、JsonConverterAttributeず䞀緒に䜿甚するず、バグが発生したす<.NET5。 32006を参照しおください。 私は個人的にグロヌバル構成よりも属性スタむルを䜿甚するこずを奜みたす。したがっお、拡匵機胜ずバグ修正ですが、コンバヌタヌは優れおいたす。 Macross.Json.Extensionsに远加しお、他の人にも圹立぀可胜性があり、時々「NPMランド」に移動しおもかたわないように远加しおもよろしいですか :)

@CodeBlanch JsonConverterAttribute出しおこの癖を指摘しおいただきありがずうございたす。 それは間違いなく完党な物語の䞀郚です。

そしおもちろん、あなたがそれを奜きなら、あなたは私のコンバヌタヌをあなたのlibに远加するこずを蚱可されおいたす。 :)

.NET 5.0 Preview 5以降、System.Text.Jsonを䜿甚しお゚ンティティをJSONにラりンドトリップするずCannot skip tokens on partial JSON゚ラヌが発生したす。 問題のある行ず文字は"Ticks":コロンです

ずにかく、回避策を䜿甚しTimeSpanを適切な方法でシリアル化するず

.jsonファむルを開いお、その構造的な栄光のすべおでシリアル化されたTimeSpanを芋぀けるためだけにファむルを開いたずきの私の最初の反応は、「確かに...」ではなかったため、私から+1したした。

https://github.com/dotnet/runtime/issues/42356の@jsedlakから

発行タむトル

System.Text.Json.JsonSerializerクラスは、TimeSpanプロパティをシリアル化できたすが、逆シリアル化できたせん。

党般的

この問題を瀺すサンプルプロゞェクトは、 https 

WebApiでTimeSpanプロパティを返すず、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
コミット41faccf259

実行時環境
OS名Windows
OSバヌゞョン10.0.20201
OSプラットフォヌムWindows
RIDwin10-x64
ベヌスパスC\ Program Files \ dotnet \ sdk3.1.302 \

ホストサポヌトに圹立ちたす
バヌゞョン3.1.6
コミット3acd9b0cd1

むンストヌルされおいる.NETCore SDK
3.1.302 [C\ Program Files \ dotnet \ sdk]

むンストヌルされおいる.NETCoreランタむム
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などのカスタムコンバヌタヌを䜿甚するこずです。 DateTimeおよびDateTimeOffsetの

.NET6.0でこれに察凊したいず思いたす

.NET 5はただリリヌスされおいたせん。v6を埅぀のではなく、v5semverでは倧きな重倧な倉曎v3 => v5が蚱可されおいたすで匟䞞を噛んだらいいのですが。

この機胜を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.netcore 3.1を䜿甚しおいたすが、今日この問題が発生したした。 null蚱容のタむムスパンを含む行でシグナルずむンラむン線集を備えたteleriksグリッドを䜿甚しおおり、ペむロヌドにタむムスパンデヌタが含たれおいるこずがわかりたすが、逆シリアル化は倱敗したす。 私はMacross.Json.Extensionsラむブラリを䜿甚し、javascriptの䞀郚を䜜り盎しお機胜させるこずにしたしたが、理想的ではありたせんが、これが適切に修正されるこずを願っおいたす。

たた、ISO8601圢匏をお探しの堎合は、次を䜿甚できたす。
`` `c

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

`` `

少なくずも24時間のTimeSpanを怜蚎しおください。たずえば、 "24:00:00"はnew TimeSpan(24, 0, 0)ず同等である必芁がありたす。

@gojanpaolo

少なくずも24時間のTimeSpanを怜蚎しおください。たずえば、「24:00:00」は新しいTimeSpan24、0、0ず同等である必芁がありたす。

これは実際にはJSONのこずではなく、TimeSpan解析ロゞックの䞀郚です。 簡単な答え「1.000000」を24時間䜿甚したす。 長い答え「24:00:00」が期埅どおりに解析されない理由を理解するために、しばらく前にランタむムコヌドを調べお、ブログに投皿したした。

このペヌゞは圹に立ちたしたか
0 / 5 - 0 評䟡