From b41ed722836c7918c1185b7b46aeeedaefc6bf32 Mon Sep 17 00:00:00 2001 From: Robert Dailey Date: Thu, 16 Jun 2022 17:25:29 -0500 Subject: [PATCH] fix: Do not parse NULL into empty collections When you specify an empty object in YAML, like: ``` quality_profiles: ``` This causes that respective object/collection to be assigned `null`. YamlDotNet feature request covering this behavior can be found [here][1]. Fixes #89. [1]: https://github.com/aaubry/YamlDotNet/issues/443 --- CHANGELOG.md | 1 + src/Common/YamlDotNet/ForceEmptySequences.cs | 55 ++++++++++++++++++++ src/TrashLib/Config/YamlSerializerFactory.cs | 1 + 3 files changed, 57 insertions(+) create mode 100644 src/Common/YamlDotNet/ForceEmptySequences.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 355c790e..bff3bc3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Do not exit when a YAML config has no sonarr or radarr section. - Sonarr: Invalid release profile JSON files no longer cause the program to exit. Instead, it just skips them and prints a warning to the user. (#87) +- Radarr: Do not crash when `quality_profiles` is empty. (#89) ## [2.2.0] - 2022-06-03 diff --git a/src/Common/YamlDotNet/ForceEmptySequences.cs b/src/Common/YamlDotNet/ForceEmptySequences.cs new file mode 100644 index 00000000..2947519e --- /dev/null +++ b/src/Common/YamlDotNet/ForceEmptySequences.cs @@ -0,0 +1,55 @@ +using System.Collections; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; + +namespace Common.YamlDotNet; + +// Borrowed from: https://github.com/aaubry/YamlDotNet/issues/443#issuecomment-544449498 +public sealed class ForceEmptySequences : INodeDeserializer +{ + private readonly IObjectFactory _objectFactory; + + public ForceEmptySequences(IObjectFactory objectFactory) + { + _objectFactory = objectFactory; + } + + public bool Deserialize(IParser reader, Type expectedType, Func nestedObjectDeserializer, + out object? value) + { + value = null; + + if (!IsEnumerable(expectedType) || !reader.Accept(out var evt) || !NodeIsNull(evt)) + { + return false; + } + + reader.SkipThisAndNestedEvents(); + value = _objectFactory.Create(expectedType); + return true; + } + + private static bool NodeIsNull(NodeEvent nodeEvent) + { + // http://yaml.org/type/null.html + + if (nodeEvent.Tag == "tag:yaml.org,2002:null") + { + return true; + } + + if (nodeEvent is not Scalar {Style: ScalarStyle.Plain} scalar) + { + return false; + } + + var value = scalar.Value; + return value is "" or "~" or "null" or "Null" or "NULL"; + } + + private static bool IsEnumerable(Type type) + { + return typeof(IEnumerable).IsAssignableFrom(type); + } +} diff --git a/src/TrashLib/Config/YamlSerializerFactory.cs b/src/TrashLib/Config/YamlSerializerFactory.cs index fa5645bd..1c2daf1e 100644 --- a/src/TrashLib/Config/YamlSerializerFactory.cs +++ b/src/TrashLib/Config/YamlSerializerFactory.cs @@ -19,6 +19,7 @@ public class YamlSerializerFactory : IYamlSerializerFactory .WithNamingConvention(UnderscoredNamingConvention.Instance) .WithTypeConverter(new YamlNullableEnumTypeConverter()) .WithNodeTypeResolver(new ReadOnlyCollectionNodeTypeResolver()) + .WithNodeDeserializer(new ForceEmptySequences(_objectFactory)) .WithObjectFactory(_objectFactory) .Build(); }