diff --git a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpDateTimeConverter.cs b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpDateTimeConverter.cs index a87add029e..e35589830d 100644 --- a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpDateTimeConverter.cs +++ b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpDateTimeConverter.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Text.Json; using System.Text.Json.Serialization; using Microsoft.Extensions.Options; @@ -20,7 +21,28 @@ namespace Volo.Abp.Json.SystemTextJson.JsonConverters public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - return _clock.Normalize(reader.GetDateTime()); + if (!_options.DefaultDateTimeFormat.IsNullOrWhiteSpace()) + { + if (reader.TokenType == JsonTokenType.String) + { + var s = reader.GetString(); + if (DateTime.TryParseExact(s, _options.DefaultDateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out var d1)) + { + return _clock.Normalize(d1); + } + + throw new JsonException($"{s} cannot parse to DateTime({_options.DefaultDateTimeFormat})!"); + } + + throw new JsonException("reader TokenType is not String!"); + } + + if (reader.TryGetDateTime(out var d2)) + { + return _clock.Normalize(d2); + } + + throw new JsonException("reader can't get datetime!"); } public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) diff --git a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpHasExtraPropertiesJsonConverter.cs b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpHasExtraPropertiesJsonConverter.cs index 40227dd5c3..e5e3f5e187 100644 --- a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpHasExtraPropertiesJsonConverter.cs +++ b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpHasExtraPropertiesJsonConverter.cs @@ -16,15 +16,21 @@ namespace Volo.Abp.Json.SystemTextJson.JsonConverters x.GetType() == typeof(AbpHasExtraPropertiesJsonConverterFactory)); var rootElement = JsonDocument.ParseValue(ref reader).RootElement; - var extensibleObject = JsonSerializer.Deserialize(rootElement.GetRawText(), newOptions); + if (rootElement.ValueKind == JsonValueKind.Object) + { + var extensibleObject = JsonSerializer.Deserialize(rootElement.GetRawText(), newOptions); - var extraProperties = rootElement.EnumerateObject().FirstOrDefault(x => - x.Name.Equals(nameof(IHasExtraProperties.ExtraProperties), StringComparison.OrdinalIgnoreCase)) - .Value.GetRawText(); - var extraPropertyDictionary = JsonSerializer.Deserialize(extraProperties, typeof(ExtraPropertyDictionary), newOptions); - ObjectHelper.TrySetProperty(extensibleObject, x => x.ExtraProperties, () => extraPropertyDictionary); + var extraProperties = rootElement.EnumerateObject().FirstOrDefault(x => x.Name.Equals(nameof(IHasExtraProperties.ExtraProperties), StringComparison.OrdinalIgnoreCase)); + if (extraProperties.Value.ValueKind == JsonValueKind.Object) + { + var extraPropertyDictionary = JsonSerializer.Deserialize(extraProperties.Value.GetRawText(), typeof(ExtraPropertyDictionary), newOptions); + ObjectHelper.TrySetProperty(extensibleObject, x => x.ExtraProperties, () => extraPropertyDictionary); + } - return extensibleObject; + return extensibleObject; + } + + throw new JsonException("RootElement ValueKind is not Object!"); } public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) diff --git a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpNullableDateTimeConverter.cs b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpNullableDateTimeConverter.cs index bad650b4d8..3a603af994 100644 --- a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpNullableDateTimeConverter.cs +++ b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpNullableDateTimeConverter.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Text.Json; using System.Text.Json.Serialization; using Microsoft.Extensions.Options; @@ -20,9 +21,25 @@ namespace Volo.Abp.Json.SystemTextJson.JsonConverters public override DateTime? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - if (reader.TryGetDateTime(out var dateTime)) + if (!_options.DefaultDateTimeFormat.IsNullOrWhiteSpace()) { - return _clock.Normalize(dateTime); + if (reader.TokenType == JsonTokenType.String) + { + var s = reader.GetString(); + if (DateTime.TryParseExact(s, _options.DefaultDateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out var d1)) + { + return _clock.Normalize(d1); + } + + throw new JsonException($"{s} cannot parse to DateTime({_options.DefaultDateTimeFormat})!"); + } + + throw new JsonException("reader TokenType is not String!"); + } + + if (reader.TryGetDateTime(out var d2)) + { + return _clock.Normalize(d2); } return null; diff --git a/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs index da49f7ebc6..3c32a99dec 100644 --- a/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs +++ b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs @@ -1,4 +1,6 @@ -using Shouldly; +using System; +using Microsoft.Extensions.DependencyInjection; +using Shouldly; using Volo.Abp.Data; using Volo.Abp.Json.SystemTextJson; using Volo.Abp.ObjectExtending; @@ -8,23 +10,23 @@ namespace Volo.Abp.Json { public class AbpSystemTextJsonSerializerProvider_Tests : AbpJsonTestBase { - private readonly AbpSystemTextJsonSerializerProvider _jsonSerializer; + protected AbpSystemTextJsonSerializerProvider JsonSerializer; public AbpSystemTextJsonSerializerProvider_Tests() { - _jsonSerializer = GetRequiredService(); + JsonSerializer = GetRequiredService(); } [Fact] public void Serialize_Deserialize_With_Boolean() { var json = "{\"name\":\"abp\",\"IsDeleted\":\"fAlSe\"}"; - var file = _jsonSerializer.Deserialize(json); + var file = JsonSerializer.Deserialize(json); file.Name.ShouldBe("abp"); file.IsDeleted.ShouldBeFalse(); file.IsDeleted = false; - var newJson = _jsonSerializer.Serialize(file); + var newJson = JsonSerializer.Serialize(file); newJson.ShouldBe("{\"name\":\"abp\",\"isDeleted\":false}"); } @@ -32,19 +34,19 @@ namespace Volo.Abp.Json public void Serialize_Deserialize_With_Nullable_Boolean() { var json = "{\"name\":\"abp\",\"IsDeleted\":null}"; - var file = _jsonSerializer.Deserialize(json); + var file = JsonSerializer.Deserialize(json); file.Name.ShouldBe("abp"); file.IsDeleted.ShouldBeNull(); - var newJson = _jsonSerializer.Serialize(file); + var newJson = JsonSerializer.Serialize(file); newJson.ShouldBe("{\"name\":\"abp\",\"isDeleted\":null}"); json = "{\"name\":\"abp\",\"IsDeleted\":\"true\"}"; - file = _jsonSerializer.Deserialize(json); + file = JsonSerializer.Deserialize(json); file.IsDeleted.ShouldNotBeNull(); file.IsDeleted.Value.ShouldBeTrue(); - newJson = _jsonSerializer.Serialize(file); + newJson = JsonSerializer.Serialize(file); newJson.ShouldBe("{\"name\":\"abp\",\"isDeleted\":true}"); } @@ -52,11 +54,11 @@ namespace Volo.Abp.Json public void Serialize_Deserialize_With_Enum() { var json = "{\"name\":\"abp\",\"type\":\"Exe\"}"; - var file = _jsonSerializer.Deserialize(json); + var file = JsonSerializer.Deserialize(json); file.Name.ShouldBe("abp"); file.Type.ShouldBe(FileType.Exe); - var newJson = _jsonSerializer.Serialize(file); + var newJson = JsonSerializer.Serialize(file); newJson.ShouldBe("{\"name\":\"abp\",\"type\":2}"); } @@ -64,35 +66,73 @@ namespace Volo.Abp.Json public void Serialize_Deserialize_With_Nullable_Enum() { var json = "{\"name\":\"abp\",\"type\":null}"; - var file = _jsonSerializer.Deserialize(json); + var file = JsonSerializer.Deserialize(json); file.Name.ShouldBe("abp"); file.Type.ShouldBeNull(); - var newJson = _jsonSerializer.Serialize(file); + var newJson = JsonSerializer.Serialize(file); newJson.ShouldBe("{\"name\":\"abp\",\"type\":null}"); json = "{\"name\":\"abp\",\"type\":\"Exe\"}"; - file = _jsonSerializer.Deserialize(json); + file = JsonSerializer.Deserialize(json); file.Type.ShouldNotBeNull(); file.Type.ShouldBe(FileType.Exe); - newJson = _jsonSerializer.Serialize(file); + newJson = JsonSerializer.Serialize(file); newJson.ShouldBe("{\"name\":\"abp\",\"type\":2}"); } - [Fact] public void Serialize_Deserialize_ExtensibleObject() { var json = "{\"name\":\"test\",\"extraProperties\":{\"One\":\"123\",\"Two\":456}}"; - var extensibleObject = _jsonSerializer.Deserialize(json); + var extensibleObject = JsonSerializer.Deserialize(json); extensibleObject.GetProperty("One").ShouldBe("123"); extensibleObject.GetProperty("Two").ShouldBe(456); - var newJson = _jsonSerializer.Serialize(extensibleObject); + var newJson = JsonSerializer.Serialize(extensibleObject); newJson.ShouldBe(json); } + [Fact] + public void Serialize_Deserialize_ExtensibleObject_With_ExtraProperties_String() + { + var json = "{\"name\":\"test\"}"; + var extensibleObject = JsonSerializer.Deserialize(json); + extensibleObject.ExtraProperties.ShouldNotBeNull(); + extensibleObject.ExtraProperties.ShouldBeEmpty(); + } + + [Fact] + public void Serialize_Deserialize_With_Datetime() + { + var json = "{\"name\":\"abp\",\"creationTime\":\"2020-11-20T00:00:00\"}"; + var file = JsonSerializer.Deserialize(json); + file.CreationTime.Year.ShouldBe(2020); + file.CreationTime.Month.ShouldBe(11); + file.CreationTime.Day.ShouldBe(20); + } + + [Fact] + public void Serialize_Deserialize_With_Nullable_Datetime() + { + var json = "{\"name\":\"abp\",\"creationTime\":null}"; + var file = JsonSerializer.Deserialize(json); + file.CreationTime.ShouldBeNull(); + + json = "{\"name\":\"abp\"}"; + file = JsonSerializer.Deserialize(json); + file.CreationTime.ShouldBeNull(); + + json = "{\"name\":\"abp\",\"creationTime\":\"2020-11-20T00:00:00\"}"; + file = JsonSerializer.Deserialize(json); + file.CreationTime.ShouldNotBeNull(); + + file.CreationTime.Value.Year.ShouldBe(2020); + file.CreationTime.Value.Month.ShouldBe(11); + file.CreationTime.Value.Day.ShouldBe(20); + } + class TestExtensibleObjectClass : ExtensibleObject { public string Name { get; set; } @@ -131,5 +171,60 @@ namespace Volo.Abp.Json Zip = 0, Exe = 2 } + + protected class FileWithDatetime + { + public string Name { get; set; } + + public DateTime CreationTime { get; set; } + } + + protected class FileWithNullableDatetime + { + public string Name { get; set; } + + public DateTime? CreationTime { get; set; } + } + } + + public class FormatAbpSystemTextJsonSerializerProvider_Tests : AbpSystemTextJsonSerializerProvider_Tests + { + protected override void AfterAddApplication(IServiceCollection services) + { + services.Configure(options => + { + options.DefaultDateTimeFormat = "yyyy*MM*dd"; + }); + } + + [Fact] + public void Serialize_Deserialize_With_Format_Datetime() + { + var json = "{\"name\":\"abp\",\"creationTime\":\"2020*11*20\"}"; + var file = JsonSerializer.Deserialize(json); + file.CreationTime.Year.ShouldBe(2020); + file.CreationTime.Month.ShouldBe(11); + file.CreationTime.Day.ShouldBe(20); + } + + [Fact] + public void Serialize_Deserialize_With_Nullable_Format_Datetime() + { + var json = "{\"name\":\"abp\",\"creationTime\":null}"; + var file = JsonSerializer.Deserialize(json); + file.CreationTime.ShouldBeNull(); + + json = "{\"name\":\"abp\"}"; + file = JsonSerializer.Deserialize(json); + file.CreationTime.ShouldBeNull(); + + json = "{\"name\":\"abp\",\"creationTime\":\"2020*11*20\"}"; + file = JsonSerializer.Deserialize(json); + file.CreationTime.ShouldNotBeNull(); + + file.CreationTime.Value.Year.ShouldBe(2020); + file.CreationTime.Value.Month.ShouldBe(11); + file.CreationTime.Value.Day.ShouldBe(20); + } } } diff --git a/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/JsonConverters/EntityJsonConverter.cs b/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/JsonConverters/EntityJsonConverter.cs index fbc222a46a..1e2c50a448 100644 --- a/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/JsonConverters/EntityJsonConverter.cs +++ b/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/JsonConverters/EntityJsonConverter.cs @@ -6,21 +6,28 @@ using Volo.Abp.Domain.Entities; namespace Volo.Abp.MemoryDb.JsonConverters { public class EntityJsonConverter : JsonConverter - where TEntity : IEntity + where TEntity : Entity { public override TEntity Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var jsonDocument = JsonDocument.ParseValue(ref reader); - var entity = (TEntity)JsonSerializer.Deserialize(jsonDocument.RootElement.GetRawText(), typeToConvert); - var idJsonElement = jsonDocument.RootElement.GetProperty(nameof(IEntity.Id)); - - var id = JsonSerializer.Deserialize(idJsonElement.GetRawText()); - if (id != null) + if (jsonDocument.RootElement.ValueKind == JsonValueKind.Object) { - EntityHelper.TrySetId(entity, () => id); + var entity = (TEntity)JsonSerializer.Deserialize(jsonDocument.RootElement.GetRawText(), typeToConvert); + + var idJsonElement = jsonDocument.RootElement.GetProperty(nameof(Entity.Id)); + if (idJsonElement.ValueKind != JsonValueKind.Undefined) + { + var id = JsonSerializer.Deserialize(idJsonElement.GetRawText()); + if (id != null) + { + EntityHelper.TrySetId(entity, () => id); + } + } + return entity; } - return entity; + throw new JsonException("RootElement ValueKind is not Object!"); } public override void Write(Utf8JsonWriter writer, TEntity value, JsonSerializerOptions options)