fix: Support for more field value types in CF payload

Added or extended support for the following types that may appear in the
"value" property in CF specifications payload:

- Integer
- Double
- String
- Boolean (true and false)
- Null

Fixes #318
pull/319/head
Robert Dailey 6 months ago
parent 99c4e1ac35
commit 64cfa97e6f

@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
### Fixed
- Custom Format: The error "CF field of type False is not supported" no longer occurs when syncing
some language-specific custom formats (#318).
## [7.2.2] - 2024-08-25 ## [7.2.2] - 2024-08-25
### Fixed ### Fixed

@ -8,7 +8,7 @@ public record CustomFormatFieldData
{ {
public string Name { get; } = nameof(Value).ToCamelCase(); public string Name { get; } = nameof(Value).ToCamelCase();
[JsonConverter(typeof(FieldValueConverter))] [JsonConverter(typeof(NondeterministicValueConverter))]
public object? Value { get; init; } public object? Value { get; init; }
} }

@ -1,22 +0,0 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Recyclarr.TrashGuide.CustomFormat;
public class FieldValueConverter : JsonConverter<object>
{
public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.TokenType switch
{
JsonTokenType.Number => reader.GetInt32(),
JsonTokenType.String => reader.GetString(),
_ => throw new JsonException($"CF field of type {reader.TokenType} is not supported")
};
}
public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
{
JsonSerializer.Serialize(writer, value, options);
}
}

@ -0,0 +1,57 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Recyclarr.TrashGuide.CustomFormat;
public class NondeterministicValueConverter : JsonConverter<object>
{
public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.TokenType switch
{
JsonTokenType.Number when reader.TryGetInt32(out var value) => value,
JsonTokenType.Number when reader.TryGetDouble(out var value) => value,
JsonTokenType.String => reader.GetString(),
JsonTokenType.True => true,
JsonTokenType.False => false,
JsonTokenType.Null => null,
_ => throw new JsonException($"CF field of type {reader.TokenType} is not supported")
};
}
public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
{
try
{
switch (value)
{
case null:
writer.WriteNullValue();
break;
case string strValue:
writer.WriteStringValue(strValue);
break;
case int intValue:
writer.WriteNumberValue(intValue);
break;
case double doubleValue:
writer.WriteNumberValue(doubleValue);
break;
case bool boolValue:
writer.WriteBooleanValue(boolValue);
break;
default:
throw new JsonException($"Serialization of type {value.GetType()} is not supported");
}
}
catch (Exception ex)
{
throw new JsonException($"Serialization failed for value of type {value?.GetType()}", ex);
}
}
}

@ -0,0 +1,78 @@
using System.Text.Json;
using Recyclarr.TrashGuide.CustomFormat;
namespace Recyclarr.Tests.TrashGuide.CustomFormat;
[Parallelizable(ParallelScope.All)]
public class NondeterministicValueConverterTest
{
private JsonSerializerOptions _options;
[SetUp]
public void Setup()
{
_options = new JsonSerializerOptions
{
Converters = {new NondeterministicValueConverter()},
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
}
[Test]
public void Deserialize_int_value()
{
const string json = "42";
var result = JsonSerializer.Deserialize<object>(json, _options);
result.Should().Be(42);
}
[Test]
public void Deserialize_double_value()
{
const string json = "42.5";
var result = JsonSerializer.Deserialize<object>(json, _options);
result.Should().Be(42.5);
}
[Test]
public void Deserialize_string_value()
{
const string json = "\"test string\"";
var result = JsonSerializer.Deserialize<object>(json, _options);
result.Should().Be("test string");
}
[Test]
public void Deserialize_boolean_value_true()
{
const string json = "true";
var result = JsonSerializer.Deserialize<object>(json, _options);
result.Should().Be(true);
}
[Test]
public void Deserialize_boolean_value_false()
{
const string json = "false";
var result = JsonSerializer.Deserialize<object>(json, _options);
result.Should().Be(false);
}
[Test]
public void Deserialize_null_value()
{
const string json = "null";
var result = JsonSerializer.Deserialize<object>(json, _options);
result.Should().BeNull();
}
[Test]
public void Deserialize_unsupported_type_should_throw()
{
const string json = "{ }";
Action act = () => JsonSerializer.Deserialize<object>(json, _options);
act.Should().Throw<JsonException>()
.WithMessage("CF field of type StartObject is not supported*")
.And.InnerException.Should().BeNull();
}
}
Loading…
Cancel
Save