_Maaf jika ini sudah dijawab di tempat lain, tetapi jika itu adalah kemampuan pencarian saya, saya gagal._
Saya mencoba pratinjau 6 dari 3.0 dengan beberapa kode aplikasi produksi yang ada, dan salah satu masalah yang muncul dari pengujian adalah bahwa beberapa objek kami yang ada yang kami gunakan dalam kontrak API kami menggunakan properti yaitu TimeSpan
nilai, yang kemudian direpresentasikan sebagai string.
Apakah dukungan untuk properti TimeSpan
direncanakan untuk 3.0 untuk API System.Text.Json yang baru?
Jika tidak akan memberi kami pemberitahuan untuk melakukan beberapa refactoring sebelum September untuk mengubahnya menjadi string sehingga kami dapat menggunakan serializer baru, di mana seolah-olah direncanakan tetapi belum diterapkan maka kami hanya perlu menunggu pratinjau nanti untuk mendapatkan ini bekerja.
Di bawah ini adalah pengujian unit repro minimal yang menunjukkan kegagalan penanganan TimeSpan
dibandingkan dengan kode .NET JSON kami yang ada.
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; }
}
}
}
Saat ini kami tidak memiliki rencana untuk mendukung TimeSpan
dan akan ditambahkan di masa mendatang, tetapi saya dapat melakukan penyelidikan untuk melihat seberapa banyak pekerjaan yang terlibat. Atau, Anda dapat membuat JsonConverter
untuk mendukung TimeSpan
sebagai solusi. Saya akan memberikan pembaruan pada akhir minggu depan. Terima kasih.
Jika kami ingin menambahkannya, kami juga ingin menambahkan TimeSpan
API ke reader/writer/JsonElement, yang harus melalui tinjauan api.
Terima kasih - konverter khusus juga akan menjadi cara yang cukup mudah untuk membuatnya berfungsi untuk aplikasi kami untuk 3.0. Apakah kemampuan tersebut direncanakan untuk dikirimkan dengan pratinjau 7?
Ya, dukungan konverter khusus sekarang ada di master dan dengan demikian akan ada di pratinjau 7.
Namun, karena TimeSpan adalah tipe BCL yang umum, kami masih harus menyediakan konverter default.
Kami meninjau ini hari ini:
- Tidak jelas apakah ada pengkodean standar untuk rentang waktu.
Pengkodean standar untuk rentang waktu adalah "durasi" ISO8601 . Ini juga yang digunakan XML untuk mewakili TimeSpan
dan sudah diimplementasikan di XmlConvert
dan XsdDuration
:
Sekarang, bisa dibilang, TimeSpan
itu sendiri harus mendukung penguraian dan pemformatan format durasi ISO8601. Mungkin akan lebih baik, sebagai langkah pertama, untuk menambahkan string format TimeSpan
standar baru, mirip dengan penentu format pulang-pergi ("O", "o") DateTime[Offset]
? Menambahkan konverter setelah itu akan sangat sederhana.
Ini juga mengubah perilaku default tentang bagaimana AspNetCore menyerahkan kembali TimeSpan
ketik di API, lihat dotnet/AspnetCore#11724. Ini mungkin perubahan yang menghancurkan.
Solusi paling sederhana adalah membuat konverter khusus:
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());
}
}
Berbicara tentang bukti pelanggan: untuk dukungan pengembangan perangkat lunak kami untuk TimeSpan sangat diperlukan.
Berlari ke hari ini dengan mem-porting aplikasi ke .NET Core 3.0 juga. Karena ini ditutup, apakah itu berarti Microsoft tidak memiliki rencana untuk menambahkan dukungan asli? Komentar @khellang tampaknya merupakan argumen yang cukup meyakinkan bagi saya bahwa itu harus ada di peta jalan di suatu tempat ...
Pembukaan kembali untuk 5.0 berdasarkan permintaan tambahan. Durasi ISO8601 kemungkinan merupakan representasi terbaik meskipun kompatibilitas dengan Newtonsoft juga harus dipertimbangkan.
Baru saja mengalami masalah ini hari ini. Perilaku default lebih buruk dari perilaku sebelumnya dan sama sekali tidak terduga. Kita harus memperbaikinya, baik dengan menggunakan ISO8601 atau kompatibel dengan Newtonsoft.
Perilaku default lebih buruk dari perilaku sebelumnya dan sama sekali tidak terduga.
@mfeingol Perilaku apa? Bahwa itu hanya gagal?
Kita harus memperbaikinya, baik dengan menggunakan ISO8601 atau kompatibel dengan Newtonsoft.
Sangat mudah untuk menambahkan solusi yang disebutkan @rezabayesteh .
@khellang : apa yang saya amati dengan proyek ASP.NET Core yang relatif vanilla adalah bahwa ia membuat serial Timespan?
sebagai bidang HasValue
, dan kemudian masing-masing properti dari struct TimeSpan.
Sangat mudah untuk hanya menambahkan solusinya
Saya melakukannya, tetapi itu seharusnya tidak diperlukan untuk tipe yang umum digunakan.
Saya baru saja menemukan masalah ini hari ini (dilaporkan oleh pelanggan saya) dan harus mengalihkan kembali semua webapi dan aplikasi aspnet saya untuk menggunakan serializer Newtonsoft.Json alih-alih menggunakan konfigurasi di Startup.cs:
services.AddControllers()
.AddNewtonsoftJson(opsi =>
{
options.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
});
Dalam kasus saya, saya menggunakan beberapa TimeSpan nullable (TimeSpan?) Dan System.Text.Json telah membuat serial sebagai:
{
"hasValue": benar,
"nilai": {
"centang":0,
"hari": 0,
"jam": 0,
"milidetik": 0,
"menit": 0,
"detik": 0,
"totalHari": 0,
"totalJam": 0,
"totalMillidetik": 0,
"totalMenit": 0,
"totalSeconds": 0
}
}
Ini menyebabkan sedikit masalah untuk objek javascript di browser web, serta untuk berbagai deserializer lintas platform (berarti berbagai bahasa pemrograman) yang menggunakan apis saya.
Saya mengharapkan hasil serialisasi yang sama dengan serializer Newtonsoft.Json:
{
"timeSpan": "00:00:00,0000000",
"nullTimeSpan": null
}
Saya baru saja menemukan masalah ini hari ini (dilaporkan oleh pelanggan saya) dan harus mengalihkan kembali semua webapi dan aplikasi aspnet saya untuk menggunakan serializer Newtonsoft.Json sebagai gantinya
@bashocz Mengapa solusi yang disebutkan di https://github.com/dotnet/corefx/issues/38641#issuecomment -540200476 bekerja untuk Anda?
Mengapa solusi yang disebutkan di #38641 (komentar) tidak berhasil untuk Anda?
@khellang Saya hanya ingin menyoroti serialisasi TimeSpan adalah masalah bagi pengembang lain, dan berikan perhatian. Saya sangat ingin beralih ke System.Text.Json, namun ada beberapa kendala.
Saya telah menyelidiki System.Text.Json baru beberapa minggu yang lalu dan ternyata fiturnya tidak lengkap. Saya telah mengangkat masalah dotnet/corefx#41747 dan diarahkan ke dotnet/corefx#39031 lainnya, dotnet/corefx#41325, dotnet/corefx#38650 terkait. Karena itu semua microservice internal kami masih menggunakan Newtonsoft.Json.
Untuk alasan yang tidak diketahui saya lupa mengelola pengembang untuk memperbaikinya di API publik dan aplikasi web juga.
BTW: Saya mencoba untuk menghindari solusi sebanyak mungkin dalam kode produksi .. sulit untuk mempertahankan dan menghapusnya di masa depan.
@khellang , bukan berarti solusi tidak akan berhasil. Ini hanya hal mendasar yang tidak perlu pengembang untuk menambahkan solusi. Sebagai fitur besar yang diperkenalkan untuk .NET core 3, seharusnya tidak kekurangan implementasi dasar seperti itu.
@arisewanggithub Ada pengaturan global yang tersedia untuk pengontrol. Anda dapat mengkonfigurasi melalui AddJsonOptions()
, atau Anda dapat lulus JsonSerializerOptions
misalnya ke Json()
metode controller.
Apakah ini ditutup karena kami memiliki solusi?!
Apakah ini ditutup karena kami memiliki solusi?!
Masalah ini masih terbuka, menunggu proposal, peninjauan, dan implementasi API/perilaku. Sementara itu, cukup mudah untuk mengatasinya dengan menggunakan konverter yang disebutkan di https://github.com/dotnet/corefx/issues/38641#issuecomment -540200476.
Untuk siapa pun yang diblokir oleh ini, inilah paket NuGet dengan konverter (JsonTimeSpanConverter) yang dapat kita gunakan sebelum penurunan 5.0: Macross.Json.Extensions
Mendukung jenis TimeSpan & Nullable<TimeSpan>.
Astaga, ini sakit!
Untuk siapa pun yang diblokir oleh ini, inilah paket NuGet dengan konverter (JsonTimeSpanConverter) yang dapat kita gunakan sebelum penurunan 5.0: Macross.Json.Extensions
Mendukung TimeSpan & Nullable
jenis.
Menurut pengujian saya, tipe nilai nullable sudah ditangani oleh framework. Anda tidak perlu lebih dari yang berikut ini:
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));
}
}
(EDIT: setelah melihat lebih dekat pada sumber kerangka kerja, ternyata pemeriksaan untuk token nol/nilai nol adalah mubazir. Kode di atas diperbarui sesuai dengan itu.)
Kemudian konfigurasikan JsonSerializerOptions
seperti ini:
options.Converters.Add(new DelegatedStringJsonConverter<TimeSpan>(
value => TimeSpan.Parse(value, CultureInfo.InvariantCulture),
value => value.ToString(null, CultureInfo.InvariantCulture)));
Saya tidak berpikir itu ide yang baik untuk menambahkan ketergantungan pihak ke-3 jika Anda dapat mengatasi beberapa baris kode tambahan. Kami tidak di tanah NPM setelah semua. ;)
Tentu saja, akan lebih baik jika kita tidak membutuhkan solusi untuk tipe dasar seperti itu sama sekali.
@adams85 FYI Jenis nilai Nullable disadap < .NET 5 saat digunakan dengan JsonConverterAttribute. Lihat #32006. Saya pribadi lebih suka menggunakan gaya atribut daripada konfigurasi global, oleh karena itu ekstensi & perbaikan bug, tetapi konverter Anda sangat baik. Apakah Anda keberatan jika saya menambahkannya ke Macross.Json.Extensions untuk membantu orang lain yang mungkin juga mendapat manfaat darinya dan tidak keberatan bepergian ke "NPM land" sesekali? :)
@CodeBlanch Terima kasih telah menunjukkan kekhasan ini dengan JsonConverterAttribute
keluar. Ini pasti bagian dari cerita lengkap.
Dan, tentu saja, Anda diizinkan untuk menambahkan konverter saya ke lib Anda jika Anda menyukainya. :)
Dimulai dengan .NET 5.0 Pratinjau 5 Saya telah mendapatkan Cannot skip tokens on partial JSON
kesalahan round-tripping entitas saya ke JSON dengan System.Text.Json. Baris dan karakter yang menyinggung adalah titik dua di "Ticks":
Bagaimanapun jika saya menggunakan solusi dan membuat serial TimeSpan
dengan cara yang masuk akal, itu berfungsi dengan baik.
+1 dari saya karena reaksi awal saya saat membuka file .json
untuk memeriksanya hanya untuk menemukan serial TimeSpan
dalam semua kemuliaan strukturalnya adalah "pasti tidak..."
Dari @jsedlak di https://github.com/dotnet/runtime/issues/42356 :
Judul Masalah
Kelas System.Text.Json.JsonSerializer tidak dapat melakukan deserialize properti TimeSpan, meskipun dapat membuat serialisasi.
Umum
Contoh proyek yang menunjukkan masalah ini tersedia di sini: https://github.com/jsedlak/TestTimeSpan
Jika Anda mengembalikan properti TimeSpan di WebApi, properti tersebut diserialisasikan dengan benar ke 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 } } ]
Tetapi ekstensi deserializer default (
HttpClient.GetFromJsonAsync
) tidak dapat menangani properti. Ini mengembalikan TimeSpan kosong. Sebuah konverter kustom harus digunakan untuk deserialize objek.Info DotNet
`.NET Core SDK (mencerminkan semua global.json):
Versi: 3.1.302
Komit: 41faccf259Lingkungan Waktu Proses:
Nama OS: Windows
Versi OS: 10.0.20201
Platform OS: Windows
RID: win10-x64
Jalur Dasar: C:\Program Files\dotnet\sdk3.1.302\Tuan rumah (berguna untuk dukungan):
Versi: 3.1.6
Komit: 3acd9b0cd1.NET Core SDK diinstal:
3.1.302 [C:\Program Files\dotnet\sdk].NET Core runtime terinstal:
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]`
Kami ingin membahas ini di .NET 6.0 dan akan menghargai kontribusi komunitas di sini. Solusi untuk sementara adalah dengan menggunakan konverter khusus, misalnya https://github.com/dotnet/runtime/issues/29932#issuecomment -540200476. Implementasinya harus didasarkan pada format ISO8601 agar durasinya konsisten dengan perilaku DateTime
dan DateTimeOffset
.
Kami ingin membahas ini di .NET 6.0
.NET 5 belum dirilis, saya akan senang jika kita menggigit peluru di v5 (semver mengizinkan perubahan besar yang melanggar v3 => v5) daripada menunggu v6.
Kami tidak dapat memasukkan fitur ini ke dalam versi 5.0. Kami harus memprioritaskan bersama dengan fitur 5.0 lainnya yang berfungsi , dan yang ini tidak cocok. FWIW ini dekat dengan garis potong, sehingga langkahnya terlambat.
Hanya untuk menyoroti solusi sederhana dengan konverter khusus lagi:
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 , saya dapat mengambil ini untuk 6.0.
Saya akan merekomendasikan untuk menggunakan budaya invarian ketika memilih untuk membuat konverter json sendiri:
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));
}
}
Saya bekerja dengan asp.net core 3.1 dan mengalami masalah ini hari ini. Saya menggunakan teleriks grid dengan signalr dan pengeditan sebaris pada baris yang berisi rentang waktu yang dapat dibatalkan dan dapat melihat bahwa muatan berisi data rentang waktu tetapi deserialisasi gagal. Saya akhirnya menggunakan perpustakaan Macross.Json.Extensions dan mengerjakan ulang beberapa javascript saya untuk membuatnya berfungsi, tidak ideal, saya harap ini mendapatkan perbaikan yang tepat.
Dan jika Anda mencari format ISO8601, Anda dapat menggunakan ini:
```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);
}
}
```
Harap pertimbangkan TimeSpan
setidaknya 24 jam.. misalnya "24:00:00"
harus setara dengan new TimeSpan(24, 0, 0)
@gojanpaolo
Harap pertimbangkan TimeSpan minimal 24 jam.. misalnya "24:00:00" harus setara dengan TimeSpan baru (24, 0, 0)
Itu sebenarnya bukan masalah JSON, itu bagian dari logika parse TimeSpan. Jawaban singkat: Gunakan "1.00:00:00" selama 24 jam. Jawaban panjang: Saya menelusuri kode runtime beberapa waktu lalu untuk mencari tahu mengapa "24:00:00" tidak mengurai seperti yang diharapkan dan menulis posting blog tentangnya.
Komentar yang paling membantu
Pengkodean standar untuk rentang waktu adalah "durasi" ISO8601 . Ini juga yang digunakan XML untuk mewakili
TimeSpan
dan sudah diimplementasikan diXmlConvert
danXsdDuration
: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
Sekarang, bisa dibilang,
TimeSpan
itu sendiri harus mendukung penguraian dan pemformatan format durasi ISO8601. Mungkin akan lebih baik, sebagai langkah pertama, untuk menambahkan string formatTimeSpan
standar baru, mirip dengan penentu format pulang-pergi ("O", "o")DateTime[Offset]
? Menambahkan konverter setelah itu akan sangat sederhana.