Fixed: Error deserializing preferred words with dapper

Fixes Sentry LIDARR-106
Fixes Sentry LIDARR-10B
This commit is contained in:
ta264 2020-08-20 21:31:45 +01:00 committed by Qstick
parent 2a4b3d79b8
commit 0f5531af4d
3 changed files with 197 additions and 0 deletions

View File

@ -0,0 +1,47 @@
using System.Collections.Generic;
using System.Data.SQLite;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Datastore.Converters
{
[TestFixture]
public class KeyValuePairConverterFixture : CoreTest<EmbeddedDocumentConverter<List<KeyValuePair<string, int>>>>
{
private SQLiteParameter _param;
[SetUp]
public void Setup()
{
_param = new SQLiteParameter();
}
[Test]
public void should_serialize_in_camel_case()
{
var items = new List<KeyValuePair<string, int>>
{
new KeyValuePair<string, int>("word", 1)
};
Subject.SetValue(_param, items);
var result = (string)_param.Value;
result.Should().Be(@"[
{
""key"": ""word"",
""value"": 1
}
]");
}
[TestCase(@"[{""key"": ""deluxe"", ""value"": 10 }]")]
[TestCase(@"[{""Key"": ""deluxe"", ""Value"": 10 }]")]
public void should_deserialize_case_insensitive(string input)
{
Subject.Parse(input).Should().BeEquivalentTo(new List<KeyValuePair<string, int>> { new KeyValuePair<string, int>("deluxe", 10) });
}
}
}

View File

@ -22,6 +22,7 @@ namespace NzbDrone.Core.Datastore.Converters
};
serializerSettings.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase, true));
serializerSettings.Converters.Add(new KeyValuePairConverter()); /* Remove in .NET 5 */
serializerSettings.Converters.Add(new TimeSpanConverter());
serializerSettings.Converters.Add(new UtcConverter());

View File

@ -0,0 +1,149 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace NzbDrone.Core.Datastore.Converters
{
/* See https://github.com/dotnet/runtime/issues/1197
Can be removed once we switch to .NET 5
Based on https://github.com/layomia/dotnet_runtime/blob/master/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/KeyValuePairConverter.cs
and https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to
*/
public class KeyValuePairConverter : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert)
{
if (!typeToConvert.IsGenericType)
{
return false;
}
if (typeToConvert.GetGenericTypeDefinition() != typeof(KeyValuePair<,>))
{
return false;
}
return true;
}
public override JsonConverter CreateConverter(
Type type,
JsonSerializerOptions options)
{
var keyType = type.GetGenericArguments()[0];
var valueType = type.GetGenericArguments()[1];
var converter = (JsonConverter)Activator.CreateInstance(
typeof(KeyValuePairConverterInner<,>).MakeGenericType(
new Type[] { keyType, valueType }),
BindingFlags.Instance | BindingFlags.Public,
binder: null,
args: null,
culture: null);
return converter;
}
private class KeyValuePairConverterInner<TKey, TValue> :
JsonConverter<KeyValuePair<TKey, TValue>>
{
public KeyValuePairConverterInner()
{
}
public override KeyValuePair<TKey, TValue> Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartObject)
{
throw new JsonException();
}
TKey k = default;
var keySet = false;
TValue v = default;
var valueSet = false;
reader.Read();
// Get the first property.
if (reader.TokenType != JsonTokenType.PropertyName)
{
throw new JsonException();
}
var propertyName = reader.GetString();
if (string.Equals(propertyName, "Key", StringComparison.OrdinalIgnoreCase))
{
reader.Read();
k = JsonSerializer.Deserialize<TKey>(ref reader, options);
keySet = true;
}
else if (string.Equals(propertyName, "Value", StringComparison.OrdinalIgnoreCase))
{
reader.Read();
v = JsonSerializer.Deserialize<TValue>(ref reader, options);
valueSet = true;
}
else
{
throw new JsonException();
}
// Get the second property.
reader.Read();
if (reader.TokenType != JsonTokenType.PropertyName)
{
throw new JsonException();
}
propertyName = reader.GetString();
if (!keySet && string.Equals(propertyName, "Key", StringComparison.OrdinalIgnoreCase))
{
reader.Read();
k = JsonSerializer.Deserialize<TKey>(ref reader, options);
}
else if (!valueSet && string.Equals(propertyName, "Value", StringComparison.OrdinalIgnoreCase))
{
reader.Read();
v = JsonSerializer.Deserialize<TValue>(ref reader, options);
}
else
{
throw new JsonException();
}
reader.Read();
if (reader.TokenType != JsonTokenType.EndObject)
{
throw new JsonException();
}
return new KeyValuePair<TKey, TValue>(k, v);
}
public override void Write(
Utf8JsonWriter writer,
KeyValuePair<TKey, TValue> kvp,
JsonSerializerOptions options)
{
writer.WriteStartObject();
writer.WritePropertyName("key");
JsonSerializer.Serialize(writer, kvp.Key, options);
writer.WritePropertyName("value");
JsonSerializer.Serialize(writer, kvp.Value, options);
writer.WriteEndObject();
}
}
}
}