using System.Reflection; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; namespace Idoklad.Infrastructure; /// /// Makes iDoklad SDK models usable in both directions. The SDK only ever serializes what it sends /// and deserializes what it receives, so several of its converters, attached through /// , implement one direction and throw /// in the other. This service needs both directions, so this /// resolver swaps those converters for wrappers that fill in the missing half. Everything else is /// left to . /// public sealed class SdkContractResolver : DefaultContractResolver { /// /// SDK converters that cannot write, so responses carrying such a model would fail. /// private static readonly HashSet WriteUnsupportedConverterTypeNames = new(StringComparer.Ordinal) { // Attached to DateTime members of the invoice, bank account, cash register and similar models. "IdokladSdk.Serialization.DateTimeConverter", // Attached to the NotificationListGetModel type itself. "IdokladSdk.Serialization.NotificationJsonConverter", }; /// /// SDK converters that cannot read, so a request body carrying such a member would fail. /// private const string NullablePropertyConverterTypeName = "IdokladSdk.Clients.NullablePropertyJsonConverter"; protected override JsonContract CreateContract(Type objectType) { var contract = base.CreateContract(objectType); contract.Converter = Replace(contract.Converter); return contract; } protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { var property = base.CreateProperty(member, memberSerialization); property.Converter = Replace(property.Converter); // Collections carry the member converter as the item converter instead. property.ItemConverter = Replace(property.ItemConverter); return property; } private static JsonConverter? Replace(JsonConverter? converter) { var typeName = converter?.GetType().FullName; if (typeName is null) { return converter; } if (WriteUnsupportedConverterTypeNames.Contains(typeName)) { return new SdkWriteBypassJsonConverter(converter!); } // Attached to NullableProperty members of Patch models; writing works, reading throws. return typeName == NullablePropertyConverterTypeName ? new SdkNullablePropertyConverter(converter!) : converter; } }