diff --git a/src/Volo.Abp/Volo/Abp/Json/IJsonSerializer.cs b/src/Volo.Abp/Volo/Abp/Json/IJsonSerializer.cs new file mode 100644 index 0000000000..38cfd6d0be --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Json/IJsonSerializer.cs @@ -0,0 +1,9 @@ +namespace Volo.Abp.Json +{ + public interface IJsonSerializer + { + string Serialize(object obj, bool camelCase = false, bool indented = false); + + T Deserialize(string jsonString); + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Json/JsonExtensions.cs b/src/Volo.Abp/Volo/Abp/Json/JsonExtensions.cs deleted file mode 100644 index d4caa3ea3f..0000000000 --- a/src/Volo.Abp/Volo/Abp/Json/JsonExtensions.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; - -namespace Volo.Abp.Json -{ - public static class JsonExtensions - { - //TODO: Remove this extension method, create IJsonSerializer abstraction (if there is not already such an abstraction). - - /// - /// Converts given object to JSON string. - /// - /// - public static string ToJsonString(this object obj, bool camelCase = false, bool indented = false) - { - var options = new JsonSerializerSettings(); - - if (camelCase) - { - options.ContractResolver = new CamelCasePropertyNamesContractResolver(); - } - - if (indented) - { - options.Formatting = Formatting.Indented; - } - - //TODO: AbpDateTimeConverter contains Clock, so it should be injected! - //options.Converters.Insert(0, new AbpDateTimeConverter()); - - return JsonConvert.SerializeObject(obj, options); - } - } -} diff --git a/src/Volo.Abp/Volo/Abp/Json/Newtonsoft/AbpJsonIsoDateTimeConverter.cs b/src/Volo.Abp/Volo/Abp/Json/Newtonsoft/AbpJsonIsoDateTimeConverter.cs new file mode 100644 index 0000000000..479d763d84 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Json/Newtonsoft/AbpJsonIsoDateTimeConverter.cs @@ -0,0 +1,46 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Volo.Abp.Timing; +using Volo.DependencyInjection; + +namespace Volo.Abp.Json.Newtonsoft +{ + public class AbpJsonIsoDateTimeConverter : IsoDateTimeConverter, ITransientDependency + { + private readonly IClock _clock; + + public AbpJsonIsoDateTimeConverter(IClock clock) + { + _clock = clock; + } + + public override bool CanConvert(Type objectType) + { + if (objectType == typeof(DateTime) || objectType == typeof(DateTime?)) + { + return true; + } + + return false; + } + + 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); + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Json/Newtonsoft/NewtonsoftJsonSerializer.cs b/src/Volo.Abp/Volo/Abp/Json/Newtonsoft/NewtonsoftJsonSerializer.cs new file mode 100644 index 0000000000..6ed5a055be --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Json/Newtonsoft/NewtonsoftJsonSerializer.cs @@ -0,0 +1,45 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Volo.DependencyInjection; + +namespace Volo.Abp.Json.Newtonsoft +{ + public class NewtonsoftJsonSerializer : IJsonSerializer, ITransientDependency + { + private readonly AbpJsonIsoDateTimeConverter _dateTimeConverter; + + public NewtonsoftJsonSerializer(AbpJsonIsoDateTimeConverter dateTimeConverter) + { + _dateTimeConverter = dateTimeConverter; + } + + public string Serialize(object obj, bool camelCase = false, bool indented = false) + { + var serializerSettings = CreateDefaultSerializerSettings(); + + if (camelCase) + { + serializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); + } + + if (indented) + { + serializerSettings.Formatting = Formatting.Indented; + } + + return JsonConvert.SerializeObject(obj, serializerSettings); + } + + protected virtual JsonSerializerSettings CreateDefaultSerializerSettings() + { + var settings = new JsonSerializerSettings(); + settings.Converters.Insert(0, _dateTimeConverter); + return settings; + } + + public T Deserialize(string jsonString) + { + return JsonConvert.DeserializeObject(jsonString); + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Timing/Clock.cs b/src/Volo.Abp/Volo/Abp/Timing/Clock.cs new file mode 100644 index 0000000000..23896685d8 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Timing/Clock.cs @@ -0,0 +1,42 @@ +using System; +using Microsoft.Extensions.Options; +using Volo.DependencyInjection; + +namespace Volo.Abp.Timing +{ + public class Clock : IClock, ITransientDependency + { + protected ClockOptions Options { get; } + + public Clock(IOptions options) + { + Options = options.Value; + } + + public virtual DateTime Now => Options.Kind == DateTimeKind.Utc ? DateTime.UtcNow : DateTime.Now; + + public virtual DateTimeKind Kind => Options.Kind; + + public virtual bool SupportsMultipleTimezone => Options.Kind == DateTimeKind.Utc; + + public virtual DateTime Normalize(DateTime dateTime) + { + if (Kind == DateTimeKind.Unspecified || Kind == dateTime.Kind) + { + return dateTime; + } + + if (Kind == DateTimeKind.Local && dateTime.Kind == DateTimeKind.Utc) + { + return dateTime.ToLocalTime(); + } + + if (Kind == DateTimeKind.Utc && dateTime.Kind == DateTimeKind.Local) + { + return dateTime.ToUniversalTime(); + } + + return DateTime.SpecifyKind(dateTime, Kind); + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Timing/ClockOptions.cs b/src/Volo.Abp/Volo/Abp/Timing/ClockOptions.cs new file mode 100644 index 0000000000..529f048857 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Timing/ClockOptions.cs @@ -0,0 +1,17 @@ +using System; + +namespace Volo.Abp.Timing +{ + public class ClockOptions + { + /// + /// Default: + /// + public DateTimeKind Kind { get; set; } + + public ClockOptions() + { + Kind = DateTimeKind.Unspecified; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Timing/IClock.cs b/src/Volo.Abp/Volo/Abp/Timing/IClock.cs new file mode 100644 index 0000000000..18615b767b --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Timing/IClock.cs @@ -0,0 +1,29 @@ +using System; + +namespace Volo.Abp.Timing +{ + public interface IClock + { + /// + /// Gets Now. + /// + DateTime Now { get; } + + /// + /// Gets kind. + /// + DateTimeKind Kind { get; } + + /// + /// Is that provider supports multiple time zone. + /// + bool SupportsMultipleTimezone { get; } + + /// + /// Normalizes given . + /// + /// DateTime to be normalized. + /// Normalized DateTime + DateTime Normalize(DateTime dateTime); + } +} diff --git a/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/App/AppModule.cs b/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/App/AppModule.cs index 1d91ab15b8..f1ab08739b 100644 --- a/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/App/AppModule.cs +++ b/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/App/AppModule.cs @@ -24,13 +24,15 @@ namespace Volo.Abp.AspNetCore.App app.Run(async (ctx) => { var manager = ctx.RequestServices.GetRequiredService(); + var jsonSerializer = ctx.RequestServices.GetRequiredService(); var dictionary = new Dictionary { ["TenantId"] = manager.CurrentTenant?.Id ?? "" }; - - await ctx.Response.WriteAsync(dictionary.ToJsonString()); + + var result = jsonSerializer.Serialize(dictionary); + await ctx.Response.WriteAsync(result); }); } }