Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Setup class for NewtonSoftJsonSerializer #4890

Merged
5 changes: 5 additions & 0 deletions src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4654,6 +4654,11 @@ namespace Akka.Serialization
public bool PreserveObjectReferences { get; }
public static Akka.Serialization.NewtonSoftJsonSerializerSettings Create(Akka.Configuration.Config config) { }
}
public sealed class NewtonSoftJsonSerializerSetup : Akka.Actor.Setup.Setup
{
public System.Action<Newtonsoft.Json.JsonSerializerSettings> ApplySettings { get; }
public static Akka.Serialization.NewtonSoftJsonSerializerSetup Create(System.Action<Newtonsoft.Json.JsonSerializerSettings> settings) { }
}
public class NullSerializer : Akka.Serialization.Serializer
{
public NullSerializer(Akka.Actor.ExtendedActorSystem system) { }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Akka.Actor;
using Akka.Actor.Setup;
using Akka.Configuration;
using Akka.Serialization;
using Akka.TestKit;
using Akka.TestKit.Configs;
using FluentAssertions;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using Xunit;
using Xunit.Abstractions;

namespace Akka.Tests.Serialization
{
public class NewtonSoftJsonSerializerSetupSpec : AkkaSpec
{
internal class DummyContractResolver : DefaultContractResolver
{ }

public static NewtonSoftJsonSerializerSetup SerializationSettings = NewtonSoftJsonSerializerSetup.Create(
settings =>
{
settings.ReferenceLoopHandling = ReferenceLoopHandling.Error;
settings.MissingMemberHandling = MissingMemberHandling.Error;
settings.NullValueHandling = NullValueHandling.Include;
settings.Converters = new List<JsonConverter> { new DummyConverter() };
settings.ObjectCreationHandling = ObjectCreationHandling.Auto;
settings.ContractResolver = new DummyContractResolver();
});

public static readonly BootstrapSetup Bootstrap = BootstrapSetup.Create().WithConfig(TestConfigs.DefaultConfig);

public static readonly ActorSystemSetup ActorSystemSettings = ActorSystemSetup.Create(SerializationSettings, Bootstrap);

public NewtonSoftJsonSerializerSetupSpec(ITestOutputHelper output)
: base(ActorSystem.Create("SerializationSettingsSpec", ActorSystemSettings), output) { }


[Fact]
public void Setup_should_be_used_inside_Json_serializer()
{
var serializer = (NewtonSoftJsonSerializer) Sys.Serialization.FindSerializerForType(typeof(object));
var settings = serializer.Settings;
settings.ReferenceLoopHandling.Should().Be(ReferenceLoopHandling.Error);
settings.MissingMemberHandling.Should().Be(MissingMemberHandling.Error);
settings.NullValueHandling.Should().Be(NullValueHandling.Include);
settings.Converters.Any(c => c is DummyConverter).Should().Be(true);
}

[Fact]
public void Setup_should_not_change_mandatory_settings()
{
var serializer = (NewtonSoftJsonSerializer) Sys.Serialization.FindSerializerForType(typeof(object));
var settings = serializer.Settings;
settings.ContractResolver.Should().BeOfType<NewtonSoftJsonSerializer.AkkaContractResolver>();
settings.ObjectCreationHandling.Should().Be(ObjectCreationHandling.Replace);
settings.Converters.Any(c => c is NewtonSoftJsonSerializer.SurrogateConverter).Should().Be(true);
settings.Converters.Any(c => c is DiscriminatedUnionConverter).Should().Be(true);
}
}
}
36 changes: 23 additions & 13 deletions src/core/Akka/Serialization/NewtonSoftJsonSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,30 +143,40 @@ public NewtonSoftJsonSerializer(ExtendedActorSystem system, Config config)
public NewtonSoftJsonSerializer(ExtendedActorSystem system, NewtonSoftJsonSerializerSettings settings)
: base(system)
{
var converters = settings.Converters
.Select(type => CreateConverter(type, system))
.ToList();

converters.Add(new SurrogateConverter(this));
converters.Add(new DiscriminatedUnionConverter());

Settings = new JsonSerializerSettings
{
PreserveReferencesHandling = settings.PreserveObjectReferences
? PreserveReferencesHandling.Objects
PreserveReferencesHandling = settings.PreserveObjectReferences
? PreserveReferencesHandling.Objects
: PreserveReferencesHandling.None,
Converters = converters,
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore,
MissingMemberHandling = MissingMemberHandling.Ignore,
ObjectCreationHandling = ObjectCreationHandling.Replace, //important: if reuse, the serializer will overwrite properties in default references, e.g. Props.DefaultDeploy or Props.noArgs
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
TypeNameHandling = settings.EncodeTypeNames
? TypeNameHandling.All
? TypeNameHandling.All
: TypeNameHandling.None,
ContractResolver = new AkkaContractResolver()
};

var settingsSetup = system.Settings.Setup.Get<NewtonSoftJsonSerializerSetup>()
.GetOrElse(NewtonSoftJsonSerializerSetup.Create(s => {}));

settingsSetup.ApplySettings(Settings);

var converters = settings.Converters
.Select(type => CreateConverter(type, system))
.ToList();

converters.Add(new SurrogateConverter(this));
converters.Add(new DiscriminatedUnionConverter());

foreach (var converter in converters)
{
Settings.Converters.Add(converter);
}

Settings.ObjectCreationHandling = ObjectCreationHandling.Replace; //important: if reuse, the serializer will overwrite properties in default references, e.g. Props.DefaultDeploy or Props.noArgs
Settings.ContractResolver = new AkkaContractResolver();

_serializer = JsonSerializer.Create(Settings);
}

Expand Down
32 changes: 32 additions & 0 deletions src/core/Akka/Serialization/NewtonSoftJsonSerializerSetup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Text;
using Akka.Actor.Setup;
using Newtonsoft.Json;

namespace Akka.Serialization
{
/// <summary>
/// Setup for the <see cref="NewtonSoftJsonSerializer"/> serializer.
///
/// Constructor is INTERNAL API. Use the factory method <see cref="Create"/>.
///
/// NOTE:
/// - <see cref="JsonSerializerSettings.ObjectCreationHandling"/> will always be overriden with
/// <see cref="ObjectCreationHandling.Replace"/>
/// - <see cref="JsonSerializerSettings.ContractResolver"/> will always be overriden with the internal
/// contract resolver <see cref="NewtonSoftJsonSerializer.AkkaContractResolver"/>
/// </summary>
public sealed class NewtonSoftJsonSerializerSetup : Setup
{
public static NewtonSoftJsonSerializerSetup Create(Action<JsonSerializerSettings> settings)
=> new NewtonSoftJsonSerializerSetup(settings);

public Action<JsonSerializerSettings> ApplySettings { get; }

private NewtonSoftJsonSerializerSetup(Action<JsonSerializerSettings> settings)
{
ApplySettings = settings;
}
}
}