Browse Source

Add `InputDateTimeFormats` and `OutputDateTimeFormat` to `AbpJsonOptions`.

Resolve #12338
pull/13357/head
maliming 4 years ago
parent
commit
46d8b417ac
No known key found for this signature in database GPG Key ID: 96224957E51C89E
  1. 18
      framework/src/Volo.Abp.Json.Abstractions/Volo/Abp/Json/AbpJsonOptions.cs
  2. 8
      framework/src/Volo.Abp.Json.Newtonsoft/Volo/Abp/Json/Newtonsoft/AbpCamelCasePropertyNamesContractResolver.cs
  3. 116
      framework/src/Volo.Abp.Json.Newtonsoft/Volo/Abp/Json/Newtonsoft/AbpDateTimeConverter.cs
  4. 8
      framework/src/Volo.Abp.Json.Newtonsoft/Volo/Abp/Json/Newtonsoft/AbpDefaultContractResolver.cs
  5. 60
      framework/src/Volo.Abp.Json.Newtonsoft/Volo/Abp/Json/Newtonsoft/AbpJsonIsoDateTimeConverter.cs
  6. 24
      framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpDateTimeConverter.cs
  7. 24
      framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpNullableDateTimeConverter.cs
  8. 2
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Json/JsonResultController_Tests.cs
  9. 2
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Json/JsonSerializer_Tests.cs
  10. 19
      framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs

18
framework/src/Volo.Abp.Json.Abstractions/Volo/Abp/Json/AbpJsonOptions.cs

@ -1,9 +1,21 @@
namespace Volo.Abp.Json;
using System.Collections.Generic;
namespace Volo.Abp.Json;
public class AbpJsonOptions
{
/// <summary>
/// Used to set default value for the DateTimeFormat.
/// Formats of input JSON date, Empty string means default format.
/// </summary>
public string DefaultDateTimeFormat { get; set; }
public List<string> InputDateTimeFormats { get; set; }
/// <summary>
/// Format of output json date, Null or empty string means default format.
/// </summary>
public string OutputDateTimeFormat { get; set; }
public AbpJsonOptions()
{
InputDateTimeFormats = new List<string>();
}
}

8
framework/src/Volo.Abp.Json.Newtonsoft/Volo/Abp/Json/Newtonsoft/AbpCamelCasePropertyNamesContractResolver.cs

@ -9,12 +9,12 @@ namespace Volo.Abp.Json.Newtonsoft;
public class AbpCamelCasePropertyNamesContractResolver : CamelCasePropertyNamesContractResolver, ITransientDependency
{
private readonly Lazy<AbpJsonIsoDateTimeConverter> _dateTimeConverter;
private readonly Lazy<AbpDateTimeConverter> _dateTimeConverter;
public AbpCamelCasePropertyNamesContractResolver(IServiceProvider serviceProvider)
{
_dateTimeConverter = new Lazy<AbpJsonIsoDateTimeConverter>(
serviceProvider.GetRequiredService<AbpJsonIsoDateTimeConverter>,
_dateTimeConverter = new Lazy<AbpDateTimeConverter>(
serviceProvider.GetRequiredService<AbpDateTimeConverter>,
true
);
@ -28,7 +28,7 @@ public class AbpCamelCasePropertyNamesContractResolver : CamelCasePropertyNamesC
{
var property = base.CreateProperty(member, memberSerialization);
if (AbpJsonIsoDateTimeConverter.ShouldNormalize(member, property))
if (AbpDateTimeConverter.ShouldNormalize(member, property))
{
property.Converter = _dateTimeConverter.Value;
}

116
framework/src/Volo.Abp.Json.Newtonsoft/Volo/Abp/Json/Newtonsoft/AbpDateTimeConverter.cs

@ -0,0 +1,116 @@
using System;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using Volo.Abp.Reflection;
using Volo.Abp.Timing;
namespace Volo.Abp.Json.Newtonsoft;
public class AbpDateTimeConverter : DateTimeConverterBase
{
private readonly string _dateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK";
private readonly DateTimeStyles _dateTimeStyles = DateTimeStyles.RoundtripKind;
private readonly CultureInfo _culture = CultureInfo.InvariantCulture;
private readonly IClock _clock;
private readonly AbpJsonOptions _options;
public AbpDateTimeConverter(IClock clock, IOptions<AbpJsonOptions> options)
{
_clock = clock;
_options = options.Value;
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(DateTime) || objectType == typeof(DateTime?);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var nullable = Nullable.GetUnderlyingType(objectType) != null;
if (reader.TokenType == JsonToken.Null)
{
if (!nullable)
{
throw new JsonSerializationException($"Cannot convert null value to {objectType.FullName}.");
}
return null;
}
if (reader.TokenType == JsonToken.Date)
{
return _clock.Normalize(reader.Value.To<DateTime>());
}
if (reader.TokenType != JsonToken.String)
{
throw new JsonSerializationException($"Unexpected token parsing date. Expected String, got {reader.TokenType}.");
}
var dateText = reader.Value?.ToString();
if (dateText.IsNullOrEmpty() && nullable)
{
return null;
}
if (_options.InputDateTimeFormats.Any())
{
foreach (var format in _options.InputDateTimeFormats)
{
if (DateTime.TryParseExact(dateText, format, _culture, _dateTimeStyles, out var d1))
{
return _clock.Normalize(d1);
}
}
}
var date = !_dateTimeFormat.IsNullOrEmpty() ?
DateTime.ParseExact(dateText, _dateTimeFormat, _culture, _dateTimeStyles) :
DateTime.Parse(dateText, _culture, _dateTimeStyles);
return _clock.Normalize(date);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value != null)
{
value = _clock.Normalize(value.To<DateTime>());
}
if (value is DateTime dateTime)
{
if ((_dateTimeStyles & DateTimeStyles.AdjustToUniversal) == DateTimeStyles.AdjustToUniversal ||
(_dateTimeStyles & DateTimeStyles.AssumeUniversal) == DateTimeStyles.AssumeUniversal)
{
dateTime = dateTime.ToUniversalTime();
}
writer.WriteValue(_options.OutputDateTimeFormat.IsNullOrWhiteSpace()
? dateTime.ToString(_dateTimeFormat, _culture)
: dateTime.ToString(_options.OutputDateTimeFormat, _culture));
}
else
{
throw new JsonSerializationException($"Unexpected value when converting date. Expected DateTime or DateTimeOffset, got {value.GetType()}.");
}
}
internal static bool ShouldNormalize(MemberInfo member, JsonProperty property)
{
if (property.PropertyType != typeof(DateTime) &&
property.PropertyType != typeof(DateTime?))
{
return false;
}
return ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault<DisableDateTimeNormalizationAttribute>(member) == null;
}
}

8
framework/src/Volo.Abp.Json.Newtonsoft/Volo/Abp/Json/Newtonsoft/AbpDefaultContractResolver.cs

@ -9,12 +9,12 @@ namespace Volo.Abp.Json.Newtonsoft;
public class AbpDefaultContractResolver : DefaultContractResolver, ITransientDependency
{
private readonly Lazy<AbpJsonIsoDateTimeConverter> _dateTimeConverter;
private readonly Lazy<AbpDateTimeConverter> _dateTimeConverter;
public AbpDefaultContractResolver(IServiceProvider serviceProvider)
{
_dateTimeConverter = new Lazy<AbpJsonIsoDateTimeConverter>(
serviceProvider.GetRequiredService<AbpJsonIsoDateTimeConverter>,
_dateTimeConverter = new Lazy<AbpDateTimeConverter>(
serviceProvider.GetRequiredService<AbpDateTimeConverter>,
true
);
}
@ -23,7 +23,7 @@ public class AbpDefaultContractResolver : DefaultContractResolver, ITransientDep
{
var property = base.CreateProperty(member, memberSerialization);
if (AbpJsonIsoDateTimeConverter.ShouldNormalize(member, property))
if (AbpDateTimeConverter.ShouldNormalize(member, property))
{
property.Converter = _dateTimeConverter.Value;
}

60
framework/src/Volo.Abp.Json.Newtonsoft/Volo/Abp/Json/Newtonsoft/AbpJsonIsoDateTimeConverter.cs

@ -1,60 +0,0 @@
using System;
using System.Reflection;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Reflection;
using Volo.Abp.Timing;
namespace Volo.Abp.Json.Newtonsoft;
public class AbpJsonIsoDateTimeConverter : IsoDateTimeConverter, ITransientDependency
{
private readonly IClock _clock;
public AbpJsonIsoDateTimeConverter(IClock clock, IOptions<AbpJsonOptions> abpJsonOptions)
{
_clock = clock;
if (abpJsonOptions.Value.DefaultDateTimeFormat != null)
{
DateTimeFormat = abpJsonOptions.Value.DefaultDateTimeFormat;
}
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(DateTime) || objectType == typeof(DateTime?);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var date = base.ReadJson(reader, objectType, existingValue, serializer) as DateTime?;
if (date.HasValue)
{
return _clock.Normalize(date.Value);
}
return null;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var date = value as DateTime?;
base.WriteJson(writer, date.HasValue ? _clock.Normalize(date.Value) : value, serializer);
}
internal static bool ShouldNormalize(MemberInfo member, JsonProperty property)
{
if (property.PropertyType != typeof(DateTime) &&
property.PropertyType != typeof(DateTime?))
{
return false;
}
return ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault<DisableDateTimeNormalizationAttribute>(member) == null;
}
}

24
framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpDateTimeConverter.cs

@ -1,5 +1,6 @@
using System;
using System.Globalization;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Options;
@ -21,20 +22,23 @@ public class AbpDateTimeConverter : JsonConverter<DateTime>, ITransientDependenc
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (!_options.DefaultDateTimeFormat.IsNullOrWhiteSpace())
if (_options.InputDateTimeFormats.Any())
{
if (reader.TokenType == JsonTokenType.String)
{
var s = reader.GetString();
if (DateTime.TryParseExact(s, _options.DefaultDateTimeFormat, CultureInfo.CurrentUICulture, DateTimeStyles.None, out var d1))
foreach (var format in _options.InputDateTimeFormats)
{
return _clock.Normalize(d1);
var s = reader.GetString();
if (DateTime.TryParseExact(s, format, CultureInfo.CurrentUICulture, DateTimeStyles.None, out var d1))
{
return _clock.Normalize(d1);
}
}
throw new JsonException($"'{s}' can't parse to DateTime({_options.DefaultDateTimeFormat})!");
}
throw new JsonException("Reader's TokenType is not String!");
else
{
throw new JsonException("Reader's TokenType is not String!");
}
}
if (reader.TryGetDateTime(out var d3))
@ -47,13 +51,13 @@ public class AbpDateTimeConverter : JsonConverter<DateTime>, ITransientDependenc
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
if (_options.DefaultDateTimeFormat.IsNullOrWhiteSpace())
if (_options.OutputDateTimeFormat.IsNullOrWhiteSpace())
{
writer.WriteStringValue(_clock.Normalize(value));
}
else
{
writer.WriteStringValue(_clock.Normalize(value).ToString(_options.DefaultDateTimeFormat, CultureInfo.CurrentUICulture));
writer.WriteStringValue(_clock.Normalize(value).ToString(_options.OutputDateTimeFormat, CultureInfo.CurrentUICulture));
}
}
}

24
framework/src/Volo.Abp.Json.SystemTextJson/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpNullableDateTimeConverter.cs

@ -1,5 +1,6 @@
using System;
using System.Globalization;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Options;
@ -21,20 +22,23 @@ public class AbpNullableDateTimeConverter : JsonConverter<DateTime?>, ITransient
public override DateTime? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (!_options.DefaultDateTimeFormat.IsNullOrWhiteSpace())
if (_options.InputDateTimeFormats.Any())
{
if (reader.TokenType == JsonTokenType.String)
{
var s = reader.GetString();
if (DateTime.TryParseExact(s, _options.DefaultDateTimeFormat, CultureInfo.CurrentUICulture, DateTimeStyles.None, out var d1))
foreach (var format in _options.InputDateTimeFormats)
{
return _clock.Normalize(d1);
var s = reader.GetString();
if (DateTime.TryParseExact(s, format, CultureInfo.CurrentUICulture, DateTimeStyles.None, out var d1))
{
return _clock.Normalize(d1);
}
}
throw new JsonException($"'{s}' can't parse to DateTime({_options.DefaultDateTimeFormat})!");
}
throw new JsonException("Reader's TokenType is not String!");
else
{
throw new JsonException("Reader's TokenType is not String!");
}
}
if (reader.TryGetDateTime(out var d2))
@ -53,13 +57,13 @@ public class AbpNullableDateTimeConverter : JsonConverter<DateTime?>, ITransient
}
else
{
if (_options.DefaultDateTimeFormat.IsNullOrWhiteSpace())
if (_options.OutputDateTimeFormat.IsNullOrWhiteSpace())
{
writer.WriteStringValue(_clock.Normalize(value.Value));
}
else
{
writer.WriteStringValue(_clock.Normalize(value.Value).ToString(_options.DefaultDateTimeFormat, CultureInfo.CurrentUICulture));
writer.WriteStringValue(_clock.Normalize(value.Value).ToString(_options.OutputDateTimeFormat, CultureInfo.CurrentUICulture));
}
}
}

2
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Json/JsonResultController_Tests.cs

@ -14,7 +14,7 @@ public class JsonResultController_Tests : AspNetCoreMvcTestBase
{
services.Configure<AbpJsonOptions>(options =>
{
options.DefaultDateTimeFormat = "yyyy*MM*dd";
options.OutputDateTimeFormat = "yyyy*MM*dd";
});
base.ConfigureServices(context, services);

2
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Json/JsonSerializer_Tests.cs

@ -21,7 +21,7 @@ public class JsonSerializer_Tests : AspNetCoreMvcTestBase
{
services.Configure<AbpJsonOptions>(options =>
{
options.DefaultDateTimeFormat = "yyyy*MM*dd";
options.OutputDateTimeFormat = "yyyy*MM*dd";
});
base.ConfigureServices(context, services);

19
framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs

@ -220,7 +220,8 @@ public class AbpSystemTextJsonSerializerProvider_DateTimeFormat_Tests : AbpSyste
{
services.Configure<AbpJsonOptions>(options =>
{
options.DefaultDateTimeFormat = "yyyy*MM*dd";
options.InputDateTimeFormats.Add("yyyy*MM*dd");
options.OutputDateTimeFormat = "yyyy*MM*dd HH*mm*ss";
});
}
@ -233,8 +234,12 @@ public class AbpSystemTextJsonSerializerProvider_DateTimeFormat_Tests : AbpSyste
file.CreationTime.Month.ShouldBe(11);
file.CreationTime.Day.ShouldBe(20);
var newJson = JsonSerializer.Serialize(file);
newJson.ShouldBe(json);
json = JsonSerializer.Serialize(new FileWithDatetime()
{
Name = "abp",
CreationTime = new DateTime(2020, 11, 20, 12, 34, 56)
});
json.ShouldContain("\"2020*11*20 12*34*56\"");
}
[Fact]
@ -256,8 +261,12 @@ public class AbpSystemTextJsonSerializerProvider_DateTimeFormat_Tests : AbpSyste
file.CreationTime.Value.Month.ShouldBe(11);
file.CreationTime.Value.Day.ShouldBe(20);
var newJson = JsonSerializer.Serialize(file);
newJson.ShouldBe(json);
json = JsonSerializer.Serialize(new FileWithDatetime()
{
Name = "abp",
CreationTime = new DateTime(2020, 11, 20, 12, 34, 56)
});
json.ShouldContain("\"2020*11*20 12*34*56\"");
}
}

Loading…
Cancel
Save