Skip to content

Commit

Permalink
Update NewtonSoftJsonSerializer to use new ThreadLocal instance(s) of…
Browse files Browse the repository at this point in the history
… JsonSerializer and improve performance for serializing and deserializing large objects.

Based on [recommendation from Json.NET docs](https://www.newtonsoft.com/json/help/html/Performance.htm#MemoryUsage)
  • Loading branch information
thzinc committed Nov 12, 2021
1 parent 3497ef6 commit 06368e6
Show file tree
Hide file tree
Showing 8 changed files with 335 additions and 164 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,14 @@ internal class DummyContractResolver : DefaultContractResolver

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

public NewtonSoftJsonSerializerSetupSpec(ITestOutputHelper output)
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 serializer = (NewtonSoftJsonSerializer)Sys.Serialization.FindSerializerForType(typeof(object));
var settings = serializer.Settings;
settings.ReferenceLoopHandling.Should().Be(ReferenceLoopHandling.Error);
settings.MissingMemberHandling.Should().Be(MissingMemberHandling.Error);
Expand All @@ -63,11 +63,12 @@ public void Setup_should_be_used_inside_Json_serializer()
[Fact]
public void Setup_should_not_change_mandatory_settings()
{
var serializer = (NewtonSoftJsonSerializer) Sys.Serialization.FindSerializerForType(typeof(object));
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 NewtonSoftJsonSerializer.PrimitiveNumberConverter).Should().Be(true);
settings.Converters.Any(c => c is NewtonSoftJsonSerializer.SurrogatedConverter).Should().Be(true);
settings.Converters.Any(c => c is DiscriminatedUnionConverter).Should().Be(true);
}
}
Expand Down
17 changes: 13 additions & 4 deletions src/core/Akka.Tests/Serialization/NewtonsoftJsonConfigSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@ public void Json_serializer_should_have_correct_defaults()
var serializer = (NewtonSoftJsonSerializer)system.Serialization.FindSerializerForType(typeof(object));
Assert.Equal(TypeNameHandling.All, serializer.Settings.TypeNameHandling);
Assert.Equal(PreserveReferencesHandling.Objects, serializer.Settings.PreserveReferencesHandling);
Assert.Equal(2, serializer.Settings.Converters.Count);
Assert.Equal(5, serializer.Settings.Converters.Count);
Assert.Contains(serializer.Settings.Converters, x => x is DiscriminatedUnionConverter);
Assert.Contains(serializer.Settings.Converters, x => x is NewtonSoftJsonSerializer.DelegatedObjectConverter);
Assert.Contains(serializer.Settings.Converters, x => x is NewtonSoftJsonSerializer.SurrogateConverter);
Assert.Contains(serializer.Settings.Converters, x => x is NewtonSoftJsonSerializer.SurrogatedConverter);
Assert.Contains(serializer.Settings.Converters, x => x is NewtonSoftJsonSerializer.PrimitiveNumberConverter);
}
}

Expand All @@ -48,9 +51,12 @@ public void Json_serializer_should_allow_to_setup_custom_flags()
var serializer = (NewtonSoftJsonSerializer)system.Serialization.FindSerializerForType(typeof(object));
Assert.Equal(TypeNameHandling.None, serializer.Settings.TypeNameHandling);
Assert.Equal(PreserveReferencesHandling.None, serializer.Settings.PreserveReferencesHandling);
Assert.Equal(2, serializer.Settings.Converters.Count);
Assert.Equal(5, serializer.Settings.Converters.Count);
Assert.Contains(serializer.Settings.Converters, x => x is DiscriminatedUnionConverter);
Assert.Contains(serializer.Settings.Converters, x => x is NewtonSoftJsonSerializer.DelegatedObjectConverter);
Assert.Contains(serializer.Settings.Converters, x => x is NewtonSoftJsonSerializer.SurrogateConverter);
Assert.Contains(serializer.Settings.Converters, x => x is NewtonSoftJsonSerializer.SurrogatedConverter);
Assert.Contains(serializer.Settings.Converters, x => x is NewtonSoftJsonSerializer.PrimitiveNumberConverter);
}
}

Expand All @@ -72,9 +78,12 @@ public void Json_serializer_should_allow_to_setup_custom_converters()
var serializer = (NewtonSoftJsonSerializer)system.Serialization.FindSerializerForType(typeof(object));
Assert.Equal(TypeNameHandling.All, serializer.Settings.TypeNameHandling);
Assert.Equal(PreserveReferencesHandling.Objects, serializer.Settings.PreserveReferencesHandling);
Assert.Equal(4, serializer.Settings.Converters.Count);
Assert.Equal(7, serializer.Settings.Converters.Count);
Assert.Contains(serializer.Settings.Converters, x => x is DiscriminatedUnionConverter);
Assert.Contains(serializer.Settings.Converters, x => x is NewtonSoftJsonSerializer.DelegatedObjectConverter);
Assert.Contains(serializer.Settings.Converters, x => x is NewtonSoftJsonSerializer.SurrogateConverter);
Assert.Contains(serializer.Settings.Converters, x => x is NewtonSoftJsonSerializer.SurrogatedConverter);
Assert.Contains(serializer.Settings.Converters, x => x is NewtonSoftJsonSerializer.PrimitiveNumberConverter);
Assert.Contains(serializer.Settings.Converters, x => x is DummyConverter);
Assert.Contains(serializer.Settings.Converters, x => x is DummyConverter2);
}
Expand Down Expand Up @@ -103,7 +112,7 @@ class DummyConverter2 : JsonConverter
{
public DummyConverter2(ExtendedActorSystem system)
{
if (system == null)
if (system == null)
throw new ArgumentNullException(nameof(system));
}

Expand Down
55 changes: 55 additions & 0 deletions src/core/Akka/Serialization/DelegatedObjectConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//-----------------------------------------------------------------------
// <copyright file="NewtonSoftJsonSerializer.cs" company="Akka.NET Project">
// Copyright (C) 2009-2021 Lightbend Inc. <http://www.lightbend.com>
// Copyright (C) 2013-2021 .NET Foundation <https://github.com/akkadotnet/akka.net>
// </copyright>
//-----------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;

namespace Akka.Serialization
{

public partial class NewtonSoftJsonSerializer
{
internal class DelegatedObjectConverter : JsonConverter
{
private readonly IObjectConverter[] objectConverters;

public DelegatedObjectConverter(params IObjectConverter[] objectConverters)
{
this.objectConverters = objectConverters;
}

public override bool CanConvert(Type objectType)
{
return objectType == typeof(object);
}

public override bool CanWrite => false;

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var deserializedValue = serializer.Deserialize(reader);

foreach (var objectConverter in objectConverters)
{
if (objectConverter.TryConvert(deserializedValue, out var convertedValue))
{
return convertedValue;
}
}

return deserializedValue;
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotSupportedException();
}
}
}
}
18 changes: 18 additions & 0 deletions src/core/Akka/Serialization/IObjectConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//-----------------------------------------------------------------------
// <copyright file="NewtonSoftJsonSerializer.cs" company="Akka.NET Project">
// Copyright (C) 2009-2021 Lightbend Inc. <http://www.lightbend.com>
// Copyright (C) 2013-2021 .NET Foundation <https://github.com/akkadotnet/akka.net>
// </copyright>
//-----------------------------------------------------------------------

namespace Akka.Serialization
{

public partial class NewtonSoftJsonSerializer
{
internal interface IObjectConverter
{
bool TryConvert(object deserializedValue, out object convertedValue);
}
}
}
Loading

0 comments on commit 06368e6

Please sign in to comment.