diff --git a/Extensions/Revo.Extensions.History/ChangeTracking/Model/EntityAttributeData.cs b/Extensions/Revo.Extensions.History/ChangeTracking/Model/EntityAttributeData.cs index 9c6aeba4..dc7f906f 100644 --- a/Extensions/Revo.Extensions.History/ChangeTracking/Model/EntityAttributeData.cs +++ b/Extensions/Revo.Extensions.History/ChangeTracking/Model/EntityAttributeData.cs @@ -1,6 +1,6 @@ using System; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; +using System.Collections.Generic; +using System.Text.Json; using Revo.DataAccess.Entities; using Revo.Domain.ReadModel; @@ -10,7 +10,7 @@ namespace Revo.Extensions.History.ChangeTracking.Model public class EntityAttributeData : EntityReadModel { private string attributeValueMapJson; - private JObject attributeValueMap; + private Dictionary attributeValueMap = []; public EntityAttributeData(Guid id, Guid? aggregateId, Guid? entityId) { @@ -28,38 +28,42 @@ protected EntityAttributeData() public string AttributeValueMapJson { - get { return attributeValueMapJson ?? (attributeValueMapJson = attributeValueMap?.ToString(Formatting.None) ?? "{}"); } + get + { + return attributeValueMapJson ??= JsonSerializer.Serialize(attributeValueMap); + } private set { attributeValueMapJson = value; - attributeValueMap = JObject.Parse(attributeValueMapJson); + if (string.IsNullOrWhiteSpace(attributeValueMapJson)) + { + attributeValueMap = []; + } + else + { + attributeValueMap = JsonSerializer.Deserialize>(attributeValueMapJson); + } } } public bool TryGetAttributeValue(string attributeName, out T value) { - JToken token = null; - if (attributeValueMap?.TryGetValue(attributeName, out token) ?? false) + if (attributeValueMap.TryGetValue(attributeName, out dynamic token)) { - value = (T)(dynamic)token; + value = (T)token; return true; } else { - value = default(T); + value = default; return false; } } public void SetAttributeValue(string attributeName, T attributeValue) { - if (attributeValueMap == null) - { - attributeValueMap = new JObject(); - } - - attributeValueMap[attributeName] = attributeValue != null ? JToken.FromObject(attributeValue) : JValue.CreateNull(); + attributeValueMap[attributeName] = attributeValue; attributeValueMapJson = null; } diff --git a/Extensions/Revo.Extensions.History/ChangeTracking/TrackedChangeRecordConverter.cs b/Extensions/Revo.Extensions.History/ChangeTracking/TrackedChangeRecordConverter.cs index 7842c421..ac2615b4 100644 --- a/Extensions/Revo.Extensions.History/ChangeTracking/TrackedChangeRecordConverter.cs +++ b/Extensions/Revo.Extensions.History/ChangeTracking/TrackedChangeRecordConverter.cs @@ -1,5 +1,5 @@ using System; -using Newtonsoft.Json; +using System.Text.Json; using Revo.Extensions.History.ChangeTracking.Model; namespace Revo.Extensions.History.ChangeTracking @@ -15,8 +15,8 @@ public TrackedChangeRecordConverter(IChangeDataTypeCache changeDataTypeCache) public TrackedChange FromRecord(TrackedChangeRecord record) { - Type changeDataType = changeDataTypeCache.GetClrChangeDataType(record.ChangeDataClassName); - ChangeData changeData = (ChangeData)JsonConvert.DeserializeObject(record.ChangeDataJson, changeDataType); + var changeDataType = changeDataTypeCache.GetClrChangeDataType(record.ChangeDataClassName); + var changeData = (ChangeData)JsonSerializer.Deserialize(record.ChangeDataJson, changeDataType); return new TrackedChange( record.Id, @@ -41,7 +41,7 @@ public TrackedChangeRecord ToRecord(TrackedChange change) AggregateClassId = change.AggregateClassId, AggregateId = change.AggregateId, ChangeDataClassName = changeDataTypeCache.GetChangeDataTypeName(change.ChangeData.GetType()), - ChangeDataJson = JsonConvert.SerializeObject(change.ChangeData), + ChangeDataJson = JsonSerializer.Serialize(change.ChangeData, change.ChangeData.GetType()), ChangeTime = change.ChangeTime, EntityClassId = change.EntityClassId, EntityId = change.EntityId diff --git a/Extensions/Revo.Extensions.Notifications/NotificationSerializer.cs b/Extensions/Revo.Extensions.Notifications/NotificationSerializer.cs index b70b7ad8..bd25f2a5 100644 --- a/Extensions/Revo.Extensions.Notifications/NotificationSerializer.cs +++ b/Extensions/Revo.Extensions.Notifications/NotificationSerializer.cs @@ -1,42 +1,28 @@ -using System; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using Newtonsoft.Json.Serialization; +using System.Text.Json; +using System.Text.Json.Serialization; namespace Revo.Extensions.Notifications { - public class NotificationSerializer : INotificationSerializer + public class NotificationSerializer(INotificationTypeCache notificationTypeCache) : INotificationSerializer { - private static readonly JsonSerializerSettings JsonSerializerSettings = new JsonSerializerSettings + private static readonly JsonSerializerOptions JsonSerializerSettings = new() { - ContractResolver = new DefaultContractResolver - { - NamingStrategy = new CamelCaseNamingStrategy - { - ProcessDictionaryKeys = false - } - }, - Formatting = Formatting.None + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, }; static NotificationSerializer() { - JsonSerializerSettings.Converters.Add(new StringEnumConverter()); + JsonSerializerSettings.Converters.Add(new JsonStringEnumConverter()); } - private readonly INotificationTypeCache notificationTypeCache; - - public NotificationSerializer(INotificationTypeCache notificationTypeCache) - { - this.notificationTypeCache = notificationTypeCache; - } + private readonly INotificationTypeCache notificationTypeCache = notificationTypeCache; public SerializedNotification ToJson(INotification notification) { - SerializedNotification serialized = new SerializedNotification() + var serialized = new SerializedNotification() { NotificationClassName = notificationTypeCache.GetNotificationTypeName(notification.GetType()), - NotificationJson = JsonConvert.SerializeObject(notification, JsonSerializerSettings) + NotificationJson = JsonSerializer.Serialize(notification, notification.GetType(), JsonSerializerSettings) }; return serialized; @@ -44,8 +30,8 @@ public SerializedNotification ToJson(INotification notification) public INotification FromJson(SerializedNotification serializedNotification) { - Type notificationType = notificationTypeCache.GetClrNotificationType(serializedNotification.NotificationClassName); - return (INotification)JsonConvert.DeserializeObject(serializedNotification.NotificationJson, notificationType, JsonSerializerSettings); + var notificationType = notificationTypeCache.GetClrNotificationType(serializedNotification.NotificationClassName); + return (INotification)JsonSerializer.Deserialize(serializedNotification.NotificationJson, notificationType, JsonSerializerSettings); } } } diff --git a/Extensions/Tests/Revo.Extensions.History.Tests/ChangeTracking/TrackedChangeRecordConverterTests.cs b/Extensions/Tests/Revo.Extensions.History.Tests/ChangeTracking/TrackedChangeRecordConverterTests.cs index 768f7976..fc051823 100644 --- a/Extensions/Tests/Revo.Extensions.History.Tests/ChangeTracking/TrackedChangeRecordConverterTests.cs +++ b/Extensions/Tests/Revo.Extensions.History.Tests/ChangeTracking/TrackedChangeRecordConverterTests.cs @@ -1,5 +1,7 @@ using System; -using Newtonsoft.Json.Linq; +using System.Collections.Generic; +using System.Text.Json; + using NSubstitute; using Revo.Extensions.History.ChangeTracking; using Revo.Extensions.History.ChangeTracking.Model; @@ -67,7 +69,7 @@ public void ToRecord_Result() Assert.Equal(change.EntityId, record.EntityId); Assert.Equal(change.EntityClassId, record.EntityClassId); Assert.Equal("TestChangeData", record.ChangeDataClassName); - Assert.Equal("bar", JObject.Parse(record.ChangeDataJson)["Foo"]); + Assert.Equal("bar", JsonSerializer.Deserialize>(record.ChangeDataJson)["Foo"]); Assert.Equal(change.ChangeTime, record.ChangeTime); } diff --git a/Providers/EF6/Revo.EF6/Configuration/EF6InfrastructureConfigurationSection.cs b/Providers/EF6/Revo.EF6/Configuration/EF6InfrastructureConfigurationSection.cs index 4e6b6971..9212fa12 100644 --- a/Providers/EF6/Revo.EF6/Configuration/EF6InfrastructureConfigurationSection.cs +++ b/Providers/EF6/Revo.EF6/Configuration/EF6InfrastructureConfigurationSection.cs @@ -1,5 +1,5 @@ using System; -using Newtonsoft.Json; +using System.Text.Json; using Revo.Core.Configuration; namespace Revo.EF6.Configuration @@ -14,6 +14,6 @@ public class EF6InfrastructureConfigurationSection : IRevoConfigurationSection public bool UseEventSourcedAggregateStore { get; set; } public bool UseCrudAggregateStore { get; set; } - public Func CustomizeEventJsonSerializer { get; set; } = settings => settings; + public Func CustomizeEventJsonSerializer { get; set; } = settings => settings; } } diff --git a/Providers/EF6/Revo.EF6/Events/EF6EventsModule.cs b/Providers/EF6/Revo.EF6/Events/EF6EventsModule.cs index a3dfa7fc..49eb817d 100644 --- a/Providers/EF6/Revo.EF6/Events/EF6EventsModule.cs +++ b/Providers/EF6/Revo.EF6/Events/EF6EventsModule.cs @@ -1,5 +1,5 @@ using System; -using Newtonsoft.Json; +using System.Text.Json; using Ninject.Modules; using Revo.Core.Core; using Revo.DataAccess.Entities; @@ -15,9 +15,9 @@ namespace Revo.EF6.Events [AutoLoadModule(false)] public class EF6AsyncEventsModule : NinjectModule { - private readonly Func customizeEventJsonSerializer; + private readonly Func customizeEventJsonSerializer; - public EF6AsyncEventsModule(Func customizeEventJsonSerializer) + public EF6AsyncEventsModule(Func customizeEventJsonSerializer) { this.customizeEventJsonSerializer = customizeEventJsonSerializer; } diff --git a/Providers/EFCore/Revo.EFCore/Configuration/EFCoreInfrastructureConfigurationSection.cs b/Providers/EFCore/Revo.EFCore/Configuration/EFCoreInfrastructureConfigurationSection.cs index 4181478b..0f3b6ebc 100644 --- a/Providers/EFCore/Revo.EFCore/Configuration/EFCoreInfrastructureConfigurationSection.cs +++ b/Providers/EFCore/Revo.EFCore/Configuration/EFCoreInfrastructureConfigurationSection.cs @@ -1,5 +1,5 @@ using System; -using Newtonsoft.Json; +using System.Text.Json; using Revo.Core.Configuration; namespace Revo.EFCore.Configuration @@ -14,6 +14,6 @@ public class EFCoreInfrastructureConfigurationSection : IRevoConfigurationSectio public bool UseEventSourcedAggregateStore { get; set; } public bool UseCrudAggregateStore { get; set; } - public Func CustomizeEventJsonSerializer { get; set; } = settings => settings; + public Func CustomizeEventJsonSerializer { get; set; } = settings => settings; } } diff --git a/Providers/EFCore/Revo.EFCore/DataAccess/Conventions/LowerCaseConvention.cs b/Providers/EFCore/Revo.EFCore/DataAccess/Conventions/LowerCaseConvention.cs index 51cdd063..bd1bdc8f 100644 --- a/Providers/EFCore/Revo.EFCore/DataAccess/Conventions/LowerCaseConvention.cs +++ b/Providers/EFCore/Revo.EFCore/DataAccess/Conventions/LowerCaseConvention.cs @@ -19,7 +19,7 @@ public override void Finalize(ModelBuilder modelBuilder) foreach (var property in entity.GetProperties()) { - property.SetColumnName(property.GetColumnBaseName().ToLowerInvariant()); + property.SetColumnName(property.GetColumnName().ToLowerInvariant()); } foreach (var key in entity.GetKeys()) diff --git a/Providers/EFCore/Revo.EFCore/DataAccess/Conventions/PrefixConvention.cs b/Providers/EFCore/Revo.EFCore/DataAccess/Conventions/PrefixConvention.cs index 5a0cc83b..fbb33656 100644 --- a/Providers/EFCore/Revo.EFCore/DataAccess/Conventions/PrefixConvention.cs +++ b/Providers/EFCore/Revo.EFCore/DataAccess/Conventions/PrefixConvention.cs @@ -52,7 +52,7 @@ private void PrefixColumnNames(IMutableEntityType entity, string namespacePrefix { foreach (var property in entity.GetProperties()) { - if (property.DeclaringEntityType != entity) + if (property.DeclaringType != entity) { continue; } @@ -64,7 +64,7 @@ private void PrefixColumnNames(IMutableEntityType entity, string namespacePrefix ?? property.FieldInfo?.DeclaringType; TablePrefixAttribute clrTypePrefixAttribute; if (clrDeclaringType != null - && property.DeclaringEntityType.ClrType != clrDeclaringType + && property.DeclaringType.ClrType != clrDeclaringType && (clrTypePrefixAttribute = GetTablePrefixAttribute(clrDeclaringType)) != null) //this might happen e.g. if clrDeclaringType is abstract but not { propertyNamespacePrefix = clrTypePrefixAttribute.NamespacePrefix; @@ -73,12 +73,12 @@ private void PrefixColumnNames(IMutableEntityType entity, string namespacePrefix if (propertyColumnPrefix?.Length > 0) { - property.SetColumnName($"{propertyColumnPrefix}_{property.GetColumnBaseName()}"); + property.SetColumnName($"{propertyColumnPrefix}_{property.GetColumnName()}"); } if (propertyNamespacePrefix?.Length > 0) { - property.SetColumnName($"{propertyNamespacePrefix}_{property.GetColumnBaseName()}"); + property.SetColumnName($"{propertyNamespacePrefix}_{property.GetColumnName()}"); } } } diff --git a/Providers/EFCore/Revo.EFCore/DataAccess/Conventions/SnakeCaseColumnNamesConvention.cs b/Providers/EFCore/Revo.EFCore/DataAccess/Conventions/SnakeCaseColumnNamesConvention.cs index 83b66eb5..afc27f66 100644 --- a/Providers/EFCore/Revo.EFCore/DataAccess/Conventions/SnakeCaseColumnNamesConvention.cs +++ b/Providers/EFCore/Revo.EFCore/DataAccess/Conventions/SnakeCaseColumnNamesConvention.cs @@ -15,7 +15,7 @@ public override void Finalize(ModelBuilder modelBuilder) { foreach (var property in entity.GetProperties()) { - property.SetColumnName(ToSnakeCase(property.GetColumnBaseName())); + property.SetColumnName(ToSnakeCase(property.GetColumnName())); } foreach (var key in entity.GetKeys()) diff --git a/Providers/EFCore/Revo.EFCore/Domain/ReadModelConvention.cs b/Providers/EFCore/Revo.EFCore/Domain/ReadModelConvention.cs index 50f66cc8..5c3c3625 100644 --- a/Providers/EFCore/Revo.EFCore/Domain/ReadModelConvention.cs +++ b/Providers/EFCore/Revo.EFCore/Domain/ReadModelConvention.cs @@ -38,7 +38,7 @@ public override void Finalize(ModelBuilder modelBuilder) var originalEntity = modelBuilder.Model.GetEntityTypes().FirstOrDefault(x => x.ClrType == attr.EntityType) ?? throw new InvalidOperationException($"Cannot map {entity.ClrType} as ReadModelForEntity for {attr.EntityType} because the latter is not part of the model."); - entity.FindProperty("Id").SetColumnName(originalEntity.FindProperty("Id").GetColumnBaseName()); + entity.FindProperty("Id").SetColumnName(originalEntity.FindProperty("Id").GetColumnName()); } } } diff --git a/Providers/EFCore/Revo.EFCore/Events/EFCoreEventsModule.cs b/Providers/EFCore/Revo.EFCore/Events/EFCoreEventsModule.cs index 669db1f2..a2761ee3 100644 --- a/Providers/EFCore/Revo.EFCore/Events/EFCoreEventsModule.cs +++ b/Providers/EFCore/Revo.EFCore/Events/EFCoreEventsModule.cs @@ -1,5 +1,5 @@ using System; -using Newtonsoft.Json; +using System.Text.Json; using Ninject.Modules; using Revo.Core.Core; using Revo.Infrastructure; @@ -13,9 +13,9 @@ namespace Revo.EFCore.Events [AutoLoadModule(false)] public class EFCoreAsyncEventsModule : NinjectModule { - private readonly Func customizeEventJsonSerializer; + private readonly Func customizeEventJsonSerializer; - public EFCoreAsyncEventsModule(Func customizeEventJsonSerializer) + public EFCoreAsyncEventsModule(Func customizeEventJsonSerializer) { this.customizeEventJsonSerializer = customizeEventJsonSerializer; } diff --git a/Providers/EFCore/Tests/Revo.EFCore.Tests/Projections/EFCoreSyncProjectionHookTests.cs b/Providers/EFCore/Tests/Revo.EFCore.Tests/Projections/EFCoreSyncProjectionHookTests.cs index 052ff4fa..ff78005b 100644 --- a/Providers/EFCore/Tests/Revo.EFCore.Tests/Projections/EFCoreSyncProjectionHookTests.cs +++ b/Providers/EFCore/Tests/Revo.EFCore.Tests/Projections/EFCoreSyncProjectionHookTests.cs @@ -37,7 +37,7 @@ public async Task ProjectsBeforeCommit() await sut.OnBeforeCommitAsync(); - projectionSubSystem.Received(1).ExecuteProjectionsAsync( + await projectionSubSystem.Received(1).ExecuteProjectionsAsync( Arg.Is>>( evs => evs.SequenceEqual(events)), unitOfWork, @@ -54,15 +54,15 @@ public async Task ProjectsOnlyAdditionalEvents() events.Add(new TestEvent().ToMessageDraft()); await sut.OnBeforeCommitAsync(); - projectionSubSystem.ReceivedWithAnyArgs(2).ExecuteProjectionsAsync(null, null, null); + await projectionSubSystem.ReceivedWithAnyArgs(2).ExecuteProjectionsAsync(null, null, null); - projectionSubSystem.Received(1).ExecuteProjectionsAsync( + await projectionSubSystem.Received(1).ExecuteProjectionsAsync( Arg.Is>>( evs => evs.SequenceEqual(new[] { events[0] })), unitOfWork, Arg.Is(x => x.IsSynchronousProjection)); - projectionSubSystem.Received(1).ExecuteProjectionsAsync( + await projectionSubSystem.Received(1).ExecuteProjectionsAsync( Arg.Is>>( evs => evs.SequenceEqual(new[] { events[1] })), unitOfWork, @@ -79,7 +79,7 @@ public async Task ProjectsAgainAfterCommitSucceeded() await sut.OnCommitSucceededAsync(); await sut.OnBeforeCommitAsync(); - projectionSubSystem.Received(2).ExecuteProjectionsAsync( + await projectionSubSystem.Received(2).ExecuteProjectionsAsync( Arg.Is>>( evs => evs.SequenceEqual(events)), unitOfWork, @@ -96,7 +96,7 @@ public async Task ProjectsAgainAfterCommitFailed() await sut.OnCommitFailedAsync(); await sut.OnBeforeCommitAsync(); - projectionSubSystem.Received(2).ExecuteProjectionsAsync( + await projectionSubSystem.Received(2).ExecuteProjectionsAsync( Arg.Is>>( evs => evs.SequenceEqual(events)), unitOfWork, diff --git a/Providers/EasyNetQ/Tests/Revo.EasyNetQ.Tests/EasyNetQBusTests.cs b/Providers/EasyNetQ/Tests/Revo.EasyNetQ.Tests/EasyNetQBusTests.cs index 6360d52a..59ec8ba0 100644 --- a/Providers/EasyNetQ/Tests/Revo.EasyNetQ.Tests/EasyNetQBusTests.cs +++ b/Providers/EasyNetQ/Tests/Revo.EasyNetQ.Tests/EasyNetQBusTests.cs @@ -38,7 +38,7 @@ public async Task PublishAsync_Event() await sut.PublishAsync(event1); - pubSub.Received(1).PublishAsync(Arg.Is>( + await pubSub.Received(1).PublishAsync(Arg.Is>( x => x.GetType().IsConstructedGenericType && x.GetType().GetGenericTypeDefinition() == typeof(EventMessage<>) && x.Event == event1 @@ -56,7 +56,7 @@ public async Task PublishAsync_EventMessage() await sut.PublishAsync(message); - pubSub.Received(1).PublishAsync(Arg.Is>( + await pubSub.Received(1).PublishAsync(Arg.Is>( x => x.GetType().IsConstructedGenericType && x.GetType().GetGenericTypeDefinition() == typeof(EventMessage<>) && x.Event == event1 diff --git a/Providers/EasyNetQ/Tests/Revo.EasyNetQ.Tests/EasyNetQSubscriptionHandlerTests.cs b/Providers/EasyNetQ/Tests/Revo.EasyNetQ.Tests/EasyNetQSubscriptionHandlerTests.cs index 04938d88..7d335e6f 100644 --- a/Providers/EasyNetQ/Tests/Revo.EasyNetQ.Tests/EasyNetQSubscriptionHandlerTests.cs +++ b/Providers/EasyNetQ/Tests/Revo.EasyNetQ.Tests/EasyNetQSubscriptionHandlerTests.cs @@ -32,7 +32,7 @@ public async Task HandleMessage() await sut.HandleMessageAsync(eventMessage); - eventBus.Received(1).PublishAsync(eventMessage); + await eventBus.Received(1).PublishAsync(eventMessage); currentTaskContext.Should().NotBeNull(); currentTaskContext.Should().NotBe(prevTaskContext); } diff --git a/Providers/EasyNetQ/Tests/Revo.EasyNetQ.Tests/EventTransportTests.cs b/Providers/EasyNetQ/Tests/Revo.EasyNetQ.Tests/EventTransportTests.cs index d31072af..eb805256 100644 --- a/Providers/EasyNetQ/Tests/Revo.EasyNetQ.Tests/EventTransportTests.cs +++ b/Providers/EasyNetQ/Tests/Revo.EasyNetQ.Tests/EventTransportTests.cs @@ -24,7 +24,7 @@ public async Task HandleAsync() var message = (IEventMessage) EventMessage.FromEvent(new Event2(), new Dictionary()); await sut.HandleAsync(message, CancellationToken.None); - easyNetQBus.Received(1).PublishAsync(message); + await easyNetQBus.Received(1).PublishAsync(message); } private class Event1 : IEvent diff --git a/Revo.Core/Events/JsonMetadata.cs b/Revo.Core/Events/JsonMetadata.cs index 5fdf1bf8..3f5ff076 100644 --- a/Revo.Core/Events/JsonMetadata.cs +++ b/Revo.Core/Events/JsonMetadata.cs @@ -2,15 +2,16 @@ using System.Collections; using System.Collections.Generic; using System.Linq; -using Newtonsoft.Json.Linq; +using System.Text.Json; +using System.Text.Json.Nodes; namespace Revo.Core.Events { public class JsonMetadata : IReadOnlyDictionary { - private readonly JObject jsonMetadata; + private readonly JsonObject jsonMetadata; - public JsonMetadata(JObject jsonMetadata) + public JsonMetadata(JsonObject jsonMetadata) { this.jsonMetadata = jsonMetadata; } @@ -36,9 +37,9 @@ public bool ContainsKey(string key) public bool TryGetValue(string key, out string value) { - if (jsonMetadata.TryGetValue(key, out JToken token)) + if (jsonMetadata.TryGetPropertyValue(key, out JsonNode token)) { - value = token.Type != JTokenType.Null ? token.ToString() : null; + value = token?.ToString(); return true; } @@ -48,7 +49,7 @@ public bool TryGetValue(string key, out string value) public string this[string key] => jsonMetadata[key]?.ToString() ?? throw new ArgumentException($"JSON metadata key not found: {key}"); - public IEnumerable Keys => jsonMetadata.Properties().Select(x => x.Name); - public IEnumerable Values => jsonMetadata.PropertyValues().Select(x => x.ToString()); + public IEnumerable Keys => jsonMetadata.Select(x => x.Key); + public IEnumerable Values => jsonMetadata.Select(x => x.Value.ToString()); } } diff --git a/Revo.Core/Revo.Core.csproj b/Revo.Core/Revo.Core.csproj index ffe22573..470c1fbc 100644 --- a/Revo.Core/Revo.Core.csproj +++ b/Revo.Core/Revo.Core.csproj @@ -13,11 +13,11 @@ Core package of the framework. - + diff --git a/Revo.Core/ValueObjects/SingleValueObject.cs b/Revo.Core/ValueObjects/SingleValueObject.cs index 1b132351..735adf3a 100644 --- a/Revo.Core/ValueObjects/SingleValueObject.cs +++ b/Revo.Core/ValueObjects/SingleValueObject.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace Revo.Core.ValueObjects { diff --git a/Revo.Core/ValueObjects/SingleValueObjectJsonConverter.cs b/Revo.Core/ValueObjects/SingleValueObjectJsonConverter.cs index 2e5ff61b..32f43a1c 100644 --- a/Revo.Core/ValueObjects/SingleValueObjectJsonConverter.cs +++ b/Revo.Core/ValueObjects/SingleValueObjectJsonConverter.cs @@ -2,47 +2,50 @@ using System.Collections.Concurrent; using System.Linq; using System.Reflection; -using Newtonsoft.Json; +using System.Text.Json; +using System.Text.Json.Serialization; namespace Revo.Core.ValueObjects { - public class SingleValueObjectJsonConverter : JsonConverter + public class SingleValueObjectJsonConverter : JsonConverter + { + public override bool HandleNull => true; + private static readonly ConcurrentDictionary ConstructorArgumentTypes = new ConcurrentDictionary(); - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - var valueObject = value as ISingleValueObject; - if (valueObject == null) + public override void Write(Utf8JsonWriter writer, ISingleValueObject value, JsonSerializerOptions options) { + if (value == null) { - writer.WriteNull(); + writer.WriteNullValue(); } else { - serializer.Serialize(writer, valueObject.Value); + var valueObject = value.Value; + JsonSerializer.Serialize(writer, valueObject, valueObject.GetType(), options); } } - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { + + public override ISingleValueObject Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var parameterType = ConstructorArgumentTypes.GetOrAdd( - objectType, + typeToConvert, x => { var constructors = x.GetTypeInfo().GetConstructors(BindingFlags.Public | BindingFlags.Instance).Where(c => c.GetParameters().Count() == 1).ToArray(); - Type valueType = objectType.GetInterfaces().First(t => + Type valueType = x.GetInterfaces().First(t => t.IsConstructedGenericType && t.GetGenericTypeDefinition() == typeof(ISingleValueObject<>)) .GetGenericArguments()[0]; var constructorInfo = constructors.FirstOrDefault(c => c.GetParameters()[0].ParameterType == valueType) - ?? constructors.FirstOrDefault() - ?? throw new InvalidOperationException($"Single-value type {x.FullName} must define a public single-parameter constructor"); + ?? constructors.FirstOrDefault() + ?? throw new InvalidOperationException($"Single-value type {x.FullName} must define a public single-parameter constructor"); return constructorInfo.GetParameters()[0].ParameterType; }); - object value = serializer.Deserialize(reader, parameterType); - return Activator.CreateInstance(objectType, value); + var value = JsonSerializer.Deserialize(ref reader, parameterType, options); + return (ISingleValueObject)Activator.CreateInstance(typeToConvert, value); } public override bool CanConvert(Type objectType) diff --git a/Revo.Domain/Events/DomainEvent.cs b/Revo.Domain/Events/DomainEvent.cs index a104065a..13a3b22b 100644 --- a/Revo.Domain/Events/DomainEvent.cs +++ b/Revo.Domain/Events/DomainEvent.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json; +using System.Text.Json; using Revo.Core.Events; namespace Revo.Domain.Events @@ -7,7 +7,7 @@ public abstract class DomainEvent : IEvent { public override string ToString() { - return $"{this.GetType().FullName} : {JsonConvert.SerializeObject(this)}"; + return $"{GetType().FullName} : {JsonSerializer.Serialize(this, GetType())}"; } } } diff --git a/Revo.Domain/Revo.Domain.csproj b/Revo.Domain/Revo.Domain.csproj index 03e46305..e205352f 100644 --- a/Revo.Domain/Revo.Domain.csproj +++ b/Revo.Domain/Revo.Domain.csproj @@ -11,6 +11,7 @@ Base domain model package. + diff --git a/Revo.Infrastructure/EventStores/Generic/Model/EventStream.cs b/Revo.Infrastructure/EventStores/Generic/Model/EventStream.cs index 1df390fa..4672c8d1 100644 --- a/Revo.Infrastructure/EventStores/Generic/Model/EventStream.cs +++ b/Revo.Infrastructure/EventStores/Generic/Model/EventStream.cs @@ -2,8 +2,8 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.IO; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; +using System.Text.Json; +using System.Text.Json.Nodes; using Revo.Core.Events; using Revo.DataAccess.Entities; @@ -14,7 +14,7 @@ namespace Revo.Infrastructure.EventStores.Generic.Model public class EventStream : IRowVersioned, IHasId { private IReadOnlyDictionary metadata; - private JObject metadataJsonObject; + private JsonObject metadataJsonObject; public EventStream(Guid id) { @@ -41,8 +41,8 @@ public IReadOnlyDictionary Metadata try { metadataJsonObject = MetadataJson?.Length > 0 - ? JObject.Parse(MetadataJson) - : new JObject(); + ? JsonObject.Parse(MetadataJson).AsObject() + : []; } catch (JsonException e) { @@ -58,13 +58,13 @@ public IReadOnlyDictionary Metadata set { - metadataJsonObject = new JObject(); + metadataJsonObject = []; foreach (var pair in value) { metadataJsonObject[pair.Key] = pair.Value; } - MetadataJson = metadataJsonObject.ToString(Formatting.None); + MetadataJson = metadataJsonObject.ToString(); metadata = new JsonMetadata(metadataJsonObject); } } diff --git a/Revo.Infrastructure/Events/Async/AsyncEventProcessingException.cs b/Revo.Infrastructure/Events/Async/AsyncEventProcessingException.cs index 73b88059..2db83547 100644 --- a/Revo.Infrastructure/Events/Async/AsyncEventProcessingException.cs +++ b/Revo.Infrastructure/Events/Async/AsyncEventProcessingException.cs @@ -16,9 +16,5 @@ public AsyncEventProcessingException(string message) : base(message) public AsyncEventProcessingException(string message, Exception innerException) : base(message, innerException) { } - - protected AsyncEventProcessingException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } diff --git a/Revo.Infrastructure/Events/Async/AsyncEventProcessingSequenceException.cs b/Revo.Infrastructure/Events/Async/AsyncEventProcessingSequenceException.cs index e333e774..f7c4f629 100644 --- a/Revo.Infrastructure/Events/Async/AsyncEventProcessingSequenceException.cs +++ b/Revo.Infrastructure/Events/Async/AsyncEventProcessingSequenceException.cs @@ -20,10 +20,6 @@ public AsyncEventProcessingSequenceException(string message, Exception innerExce LastSequenceNumberProcessed = lastSequenceNumberProcessed; } - protected AsyncEventProcessingSequenceException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } - public long? LastSequenceNumberProcessed { get; } } } diff --git a/Revo.Infrastructure/Events/EventSerializer.cs b/Revo.Infrastructure/Events/EventSerializer.cs index 57c0c628..1da1806f 100644 --- a/Revo.Infrastructure/Events/EventSerializer.cs +++ b/Revo.Infrastructure/Events/EventSerializer.cs @@ -1,10 +1,9 @@ using System; using System.Collections.Generic; using System.IO; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using Newtonsoft.Json.Linq; -using Newtonsoft.Json.Serialization; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; using Revo.Core.Events; using Revo.Core.Types; @@ -12,26 +11,19 @@ namespace Revo.Infrastructure.Events { public class EventSerializer : IEventSerializer { - private readonly JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings + private readonly JsonSerializerOptions jsonSerializerSettings = new() { - ContractResolver = new DefaultContractResolver - { - NamingStrategy = new CamelCaseNamingStrategy - { - ProcessDictionaryKeys = false - } - }, - Formatting = Formatting.None + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, }; - + private readonly IVersionedTypeRegistry versionedTypeRegistry; public EventSerializer(IVersionedTypeRegistry versionedTypeRegistry, - Func customizeEventJsonSerializer) + Func customizeEventJsonSerializer) { this.versionedTypeRegistry = versionedTypeRegistry; - jsonSerializerSettings.Converters.Add(new StringEnumConverter()); + jsonSerializerSettings.Converters.Add(new JsonStringEnumConverter()); jsonSerializerSettings = customizeEventJsonSerializer(jsonSerializerSettings); } @@ -45,8 +37,7 @@ public IEvent DeserializeEvent(string eventJson, VersionedTypeId typeId) throw new ArgumentException($"Cannot deserialize event of an unknown type ID {typeId}"); } - Type clrType = versionedType.ClrType; - return (IEvent)JsonConvert.DeserializeObject(eventJson, clrType, jsonSerializerSettings); + return (IEvent)JsonSerializer.Deserialize(eventJson, versionedType.ClrType, jsonSerializerSettings); } catch (JsonException e) { @@ -62,18 +53,18 @@ public IEvent DeserializeEvent(string eventJson, VersionedTypeId typeId) throw new ArgumentException($"Cannot serialize event of an unknown type {@event.GetType()}"); } - return (JsonConvert.SerializeObject(@event, jsonSerializerSettings), versionedType.Id); + return (JsonSerializer.Serialize(@event, @event.GetType(), jsonSerializerSettings), versionedType.Id); } public string SerializeEventMetadata(IReadOnlyDictionary metadata) { - return JsonConvert.SerializeObject(metadata, Formatting.None); + return JsonSerializer.Serialize(metadata); } public IReadOnlyDictionary DeserializeEventMetadata(string metadataJson) { - return new JsonMetadata(metadataJson?.Length > 0 - ? JObject.Parse(metadataJson) : new JObject()); + return new JsonMetadata(string.IsNullOrWhiteSpace(metadataJson) + ? [] : JsonObject.Parse(metadataJson).AsObject()); } } } diff --git a/Revo.Infrastructure/Validation/CommandValidationException.cs b/Revo.Infrastructure/Validation/CommandValidationException.cs index 74843ee6..1794e7f5 100644 --- a/Revo.Infrastructure/Validation/CommandValidationException.cs +++ b/Revo.Infrastructure/Validation/CommandValidationException.cs @@ -16,9 +16,5 @@ public CommandValidationException(string message) : base(message) public CommandValidationException(string message, Exception innerException) : base(message, innerException) { } - - protected CommandValidationException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } diff --git a/Tests/Revo.Core.Tests/Events/JsonMetadataTests.cs b/Tests/Revo.Core.Tests/Events/JsonMetadataTests.cs index a9212d6b..7788de93 100644 --- a/Tests/Revo.Core.Tests/Events/JsonMetadataTests.cs +++ b/Tests/Revo.Core.Tests/Events/JsonMetadataTests.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; +using System.Text.Json.Nodes; using FluentAssertions; -using Newtonsoft.Json.Linq; using Revo.Core.Events; using Xunit; @@ -9,13 +9,15 @@ namespace Revo.Core.Tests.Events public class JsonMetadataTests { private JsonMetadata sut; - private JObject json; + private JsonObject json; public JsonMetadataTests() { - json = new JObject(); - json["key1"] = "value1"; - json["key2"] = "value2"; + json = new JsonObject + { + ["key1"] = "value1", + ["key2"] = "value2" + }; sut = new JsonMetadata(json); } diff --git a/Tests/Revo.Core.Tests/ValueObjects/SingleValueObjectTests.cs b/Tests/Revo.Core.Tests/ValueObjects/SingleValueObjectTests.cs index 38cfda31..a12eb591 100644 --- a/Tests/Revo.Core.Tests/ValueObjects/SingleValueObjectTests.cs +++ b/Tests/Revo.Core.Tests/ValueObjects/SingleValueObjectTests.cs @@ -1,6 +1,6 @@ -using FluentAssertions; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; +using FluentAssertions; using Revo.Core.ValueObjects; using Xunit; @@ -13,21 +13,22 @@ public void JsonSerializesAsSingleValue() { var value = new MyValue("hello"); - string json = JsonConvert.SerializeObject(value); - JToken jtoken = JToken.Parse(json); + var json = JsonSerializer.Serialize(value); + var jdoc = JsonDocument.Parse(json); - jtoken.Type.Should().Be(JTokenType.String); - jtoken.Value().Should().Be("hello"); + jdoc.RootElement.ValueKind.Should().Be(JsonValueKind.String); + jdoc.RootElement.GetString().Should().Be("hello"); } [Fact] public void JsonDeserializesAsSingleValue() { string json = "\"hello\""; - MyValue value = JsonConvert.DeserializeObject(json); + var value = JsonSerializer.Deserialize(json); value.Value.Should().Be("hello"); } + [JsonConverter(typeof(SingleValueObjectJsonConverter))] public class MyValue : SingleValueObject { public MyValue(string value) : base(value) diff --git a/Tests/Revo.Infrastructure.Tests/EventStores/Generic/EventStoreTests.cs b/Tests/Revo.Infrastructure.Tests/EventStores/Generic/EventStoreTests.cs index 16f1ef63..7de936a6 100644 --- a/Tests/Revo.Infrastructure.Tests/EventStores/Generic/EventStoreTests.cs +++ b/Tests/Revo.Infrastructure.Tests/EventStores/Generic/EventStoreTests.cs @@ -1,10 +1,10 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; +using System.Text.Json; +using System.Text.Json.Nodes; using System.Threading.Tasks; using FluentAssertions; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using NSubstitute; using Revo.Core.Core; using Revo.Core.Events; @@ -34,12 +34,12 @@ public EventStoreTests() { inMemoryCrudRepository = Substitute.ForPartsOf(); - eventStreams = new[] - { + eventStreams = + [ new EventStream(Guid.NewGuid()), new EventStream(Guid.NewGuid()), new EventStream(Guid.NewGuid()) - }; + ]; inMemoryCrudRepository.AttachRange(eventStreams); eventSerializer = Substitute.For(); @@ -47,30 +47,30 @@ public EventStoreTests() eventSerializer.SerializeEvent(null) .ReturnsForAnyArgs(ci => ("{\"bar\":" + ci.ArgAt(0).Foo + "}", new VersionedTypeId("EventName", 5))); eventSerializer.DeserializeEvent(Arg.Any(), new VersionedTypeId("EventName", 5)) - .Returns(ci => new Event1((int) JObject.Parse(ci.ArgAt(0))["bar"])); + .Returns(ci => new Event1((int) JsonNode.Parse(ci.ArgAt(0))["bar"])); eventSerializer.SerializeEventMetadata(null) - .ReturnsForAnyArgs(ci => JsonConvert.SerializeObject(ci.Arg>() + .ReturnsForAnyArgs(ci => JsonSerializer.Serialize(ci.Arg>() .Append(new KeyValuePair("fakeSer", "true")) .ToDictionary(x => x.Key, x => x.Value))); eventSerializer.DeserializeEventMetadata(null) .ReturnsForAnyArgs(ci => { - var json = JObject.Parse(ci.Arg()); + var json = JsonNode.Parse(ci.Arg()).AsObject(); json["fakeDeser"] = "true"; return new JsonMetadata(json); }); FakeClock.Setup(); - eventStreamRows = new[] - { + eventStreamRows = + [ new EventStreamRow(Guid.NewGuid(), "{\"bar\":1}", "EventName", 5, eventStreams[0].Id, 1, Clock.Current.UtcNow, "{\"doh\":\"1\"}"), new EventStreamRow(Guid.NewGuid(), "{\"bar\":2}", "EventName", 5, eventStreams[0].Id, 2, Clock.Current.UtcNow, "{\"doh\":\"2\"}"), new EventStreamRow(Guid.NewGuid(), "{\"bar\":3}", "EventName", 5, eventStreams[1].Id, 3, Clock.Current.UtcNow, "{\"doh\":\"3\"}"), new EventStreamRow(Guid.NewGuid(), "{\"bar\":4}", "EventName", 5, eventStreams[1].Id, 2, Clock.Current.UtcNow, "{\"doh\":\"4\"}"), new EventStreamRow(Guid.NewGuid(), "{\"bar\":5}", "EventName", 5, eventStreams[1].Id, 4, Clock.Current.UtcNow, "{\"doh\":\"5\"}"), new EventStreamRow(Guid.NewGuid(), "{\"bar\":6}", "EventName", 5, eventStreams[1].Id, 5, Clock.Current.UtcNow, "{\"doh\":\"6\"}") - }; + ]; expectedStoreRecords = eventStreamRows.Select((x, i) => new FakeEventStoreRecord() @@ -190,9 +190,11 @@ await sut.PushEventsAsync(eventStreams[0].Id, new[] newRows[0].StreamSequenceNumber.Should().Be(3); newRows[0].StreamId.Should().Be(eventStreams[0].Id); - JObject jsonMetadata = JObject.Parse(newRows[0].AdditionalMetadataJson); - jsonMetadata.Should().Contain("doh", "42"); - jsonMetadata.Should().Contain("fakeSer", "true"); + var jsonMetadata = JsonNode.Parse(newRows[0].AdditionalMetadataJson).AsObject(); + jsonMetadata.ContainsKey("doh").Should().BeTrue(); + jsonMetadata["doh"].GetValue().Should().Be("42"); + jsonMetadata.ContainsKey("fakeSer").Should().BeTrue(); + jsonMetadata["fakeSer"].GetValue().Should().Be("true"); } [Fact] @@ -224,7 +226,7 @@ await sut.PushEventsAsync(eventStreams[0].Id, new[] var newRows = inMemoryCrudRepository.GetEntities(EntityState.Added).ToList(); newRows.Should().HaveCount(1); - JObject jsonMetadata = JObject.Parse(newRows[0].AdditionalMetadataJson); + var jsonMetadata = JsonNode.Parse(newRows[0].AdditionalMetadataJson).AsObject(); jsonMetadata.Should().ContainKey(BasicEventMetadataNames.EventSourceName); jsonMetadata[BasicEventMetadataNames.EventSourceName]?.ToString().Should().NotBeNullOrEmpty(); } diff --git a/Tests/Revo.Infrastructure.Tests/Events/EventSerializerTests.cs b/Tests/Revo.Infrastructure.Tests/Events/EventSerializerTests.cs index 65903be5..a6563f3c 100644 --- a/Tests/Revo.Infrastructure.Tests/Events/EventSerializerTests.cs +++ b/Tests/Revo.Infrastructure.Tests/Events/EventSerializerTests.cs @@ -1,9 +1,8 @@ using System.Collections.Generic; using System.Collections.Immutable; +using System.Text.Json; +using System.Text.Json.Nodes; using FluentAssertions; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using Newtonsoft.Json.Serialization; using NSubstitute; using Revo.Core.Events; using Revo.Core.Types; @@ -55,9 +54,10 @@ public void SerializeEvent() { var result = sut.SerializeEvent(new Event1(123)); - JObject json = JObject.Parse(result.EventJson); + var json = JsonNode.Parse(result.EventJson).AsObject(); json.Should().HaveCount(1); - json.Should().Contain("fooBar", 123); + json.ContainsKey("fooBar").Should().BeTrue(); + json["fooBar"].GetValue().Should().Be(123); result.TypeId.Should().Be(new VersionedTypeId("Event1", 1)); } @@ -65,9 +65,10 @@ public void SerializeEvent() public void SerializeEventMetadata() { var result = sut.SerializeEventMetadata(new Dictionary() { { "Bar", "123" } }); - JObject json = JObject.Parse(result); + var json = JsonNode.Parse(result).AsObject(); json.Should().HaveCount(1); - json.Should().Contain("Bar", "123"); + json.ContainsKey("Bar").Should().BeTrue(); + json["Bar"].GetValue().Should().Be("123"); } [Fact] @@ -80,10 +81,10 @@ public void SerializeEvent_DictionariesOriginalCase() {"key", "value"} }.ToImmutableDictionary())); - JObject json = JObject.Parse(result.EventJson); - json.Should().ContainKey("pairs"); - json["pairs"].Children().Should().Contain(x => x.Name == "Key" && x.Value.ToString() == "Value"); - json["pairs"].Children().Should().Contain(x => x.Name == "key" && x.Value.ToString() == "value"); + var json = JsonDocument.Parse(result.EventJson).RootElement; + json.TryGetProperty("pairs", out var pairsProperty); + pairsProperty.EnumerateObject().Should().Contain(x => x.Name == "Key" && x.Value.ToString() == "Value"); + pairsProperty.EnumerateObject().Should().Contain(x => x.Name == "key" && x.Value.ToString() == "value"); } [Fact] @@ -91,34 +92,27 @@ public void SerializeEvent_CustomizeEventJsonSerializer() { sut = new EventSerializer(versionedTypeRegistry, x => - new JsonSerializerSettings + new JsonSerializerOptions { - ContractResolver = new DefaultContractResolver - { - NamingStrategy = new SnakeCaseNamingStrategy() - }, - Formatting = Formatting.None + PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, }); var result = sut.SerializeEvent(new Event1(123)); - JObject json = JObject.Parse(result.EventJson); + var json = JsonNode.Parse(result.EventJson).AsObject(); json.Should().HaveCount(1); - json.Should().Contain("foo_bar", 123); + json.ContainsKey("foo_bar").Should().BeTrue(); + json["foo_bar"].GetValue().Should().Be(123); } - + [Fact] public void DeserializeEvent_CustomizeEventJsonSerializer() { sut = new EventSerializer(versionedTypeRegistry, x => - new JsonSerializerSettings + new JsonSerializerOptions { - ContractResolver = new DefaultContractResolver - { - NamingStrategy = new SnakeCaseNamingStrategy() - }, - Formatting = Formatting.None + PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, }); var result = sut.DeserializeEvent("{\"foo_bar\":123}", new VersionedTypeId("Event1", 1)); @@ -126,7 +120,7 @@ public void DeserializeEvent_CustomizeEventJsonSerializer() result.Should().BeOfType(); ((Event1)result).FooBar.Should().Be(123); } - + public class Event1 : IEvent { public Event1(int fooBar)