Skip to content

Commit

Permalink
Removing Newtonsoft.Json dependency from Garnet.host and .nuspec file (
Browse files Browse the repository at this point in the history
…#755)

* wip

* wip

* some cleanup

* format
  • Loading branch information
TalZaccai authored Oct 30, 2024
1 parent 5fe9e23 commit d7ad0eb
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 18 deletions.
1 change: 0 additions & 1 deletion Garnet.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
<dependency id="Microsoft.Extensions.Logging" version="8.0.0" />
<dependency id="Microsoft.Extensions.Logging.Configuration" version="8.0.0" />
<dependency id="Microsoft.Extensions.Logging.Console" version="8.0.0" />
<dependency id="Newtonsoft.Json" version="13.0.3" />
<dependency id="CommandLineParser" version="2.9.1" />
<dependency id="Microsoft.IdentityModel.Protocols.OpenIdConnect" version="8.0.1" />
<dependency id="System.IdentityModel.Tokens.Jwt" version="8.0.1" />
Expand Down
24 changes: 12 additions & 12 deletions libs/host/Configuration/ConfigProviders.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@
using System.IO;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using Garnet.common;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs;
using JsonSerializer = System.Text.Json.JsonSerializer;

namespace Garnet
{
Expand Down Expand Up @@ -86,18 +84,20 @@ public bool TryImportOptions(string path, IStreamProvider streamProvider, Option

try
{
var settings = new JsonSerializerSettings
var jsonSerializerOptions = new JsonSerializerOptions
{
MissingMemberHandling = MissingMemberHandling.Error,
Error = delegate (object _, ErrorEventArgs args)
{
logger?.LogWarning("Encountered an issue when deserializing config file (Path: {path}): {ErrorMessage}", path, args.ErrorContext.Error.Message);
args.ErrorContext.Handled = true;
}
Converters = { new PopulateObjectJsonConverter<Options>(options), new JsonStringEnumConverter() },
NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString
};
JsonConvert.PopulateObject(streamReader.ReadToEnd(), options, settings);

var json = streamReader.ReadToEnd();
var jsonReaderOptions = new JsonReaderOptions { CommentHandling = JsonCommentHandling.Skip };
var jsonReader = new Utf8JsonReader(new ReadOnlySpan<byte>(Encoding.UTF8.GetBytes(json)), jsonReaderOptions);

// No need fot the return value, as the deserializer populates the existing options instance
_ = JsonSerializer.Deserialize<Options>(ref jsonReader, jsonSerializerOptions);
}
catch (Newtonsoft.Json.JsonException je)
catch (JsonException je)
{
logger?.LogError(je, "An error occurred while parsing config file (Path: {path}).", path);
return false;
Expand Down
58 changes: 58 additions & 0 deletions libs/host/Configuration/PopulateObjectJsonConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

using System;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Garnet
{
/// <summary>
/// Deserializes a JSON-serialized stream into an existing object
/// </summary>
/// <typeparam name="T">Destination object type</typeparam>
public class PopulateObjectJsonConverter<T> : JsonConverter<T> where T : class, new()
{
private readonly T existingInstance;

/// <summary>
/// Create a new converter instance
/// </summary>
/// <param name="existingInstance">Instance to populate</param>
public PopulateObjectJsonConverter(T existingInstance)
{
this.existingInstance = existingInstance;
}

/// <inheritdoc />
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartObject)
{
throw new JsonException("Expected start of JSON object.");
}

var jsonDocument = JsonDocument.ParseValue(ref reader);

// Only override properties that are specified in the source document
foreach (var property in jsonDocument.RootElement.EnumerateObject())
{
var propertyInfo = typeof(T).GetProperty(property.Name);
if (propertyInfo != null && propertyInfo.CanWrite)
{
var propertyValue = JsonSerializer.Deserialize(property.Value.GetRawText(), propertyInfo.PropertyType, options);
propertyInfo.SetValue(existingInstance, propertyValue);
}
}

return existingInstance;
}

/// <inheritdoc />
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
JsonSerializer.Serialize(writer, value, options);
}
}

}
1 change: 0 additions & 1 deletion libs/host/Garnet.host.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
<PackageReference Include="Microsoft.Extensions.Logging.Console" />
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" />
<PackageReference Include="Microsoft.Extensions.Logging" />
<PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="System.Text.Json" />
</ItemGroup>

Expand Down
15 changes: 11 additions & 4 deletions test/Garnet.test/GarnetServerConfigTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using CommandLine;
using Garnet.common;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using NUnit.Framework;
using NUnit.Framework.Legacy;
using Tsavorite.core;
Expand Down Expand Up @@ -43,10 +44,16 @@ public void DefaultConfigurationOptionsCoverage()
}
}
// Deserialize default.conf to get all defined default options
Dictionary<string, string> jsonSettings = [];
Dictionary<string, object> jsonSettings = [];
var jsonSerializerOptions = new JsonSerializerOptions
{
ReadCommentHandling = JsonCommentHandling.Skip,
NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString
};

try
{
jsonSettings = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
jsonSettings = JsonSerializer.Deserialize<Dictionary<string, object>>(json, jsonSerializerOptions);
}
catch (Exception e)
{
Expand All @@ -57,7 +64,7 @@ public void DefaultConfigurationOptionsCoverage()
ClassicAssert.IsNotNull(jsonSettings);
foreach (var property in typeof(Options).GetProperties().Where(pi =>
pi.GetCustomAttribute<OptionAttribute>() != null &&
pi.GetCustomAttribute<System.Text.Json.Serialization.JsonIgnoreAttribute>() == null))
pi.GetCustomAttribute<JsonIgnoreAttribute>() == null))
{
ClassicAssert.Contains(property.Name, jsonSettings.Keys);
}
Expand Down

0 comments on commit d7ad0eb

Please sign in to comment.