diff --git a/src/Yllibed.TenantCloudClient/HttpMessages/JsonFlexibleStringConverter.cs b/src/Yllibed.TenantCloudClient/HttpMessages/JsonFlexibleStringConverter.cs new file mode 100644 index 0000000..88402da --- /dev/null +++ b/src/Yllibed.TenantCloudClient/HttpMessages/JsonFlexibleStringConverter.cs @@ -0,0 +1,44 @@ +using System.Globalization; + +namespace Yllibed.TenantCloudClient.HttpMessages; + +/// +/// Reads a JSON value that TenantCloud sometimes serializes as a string and +/// sometimes as a number (observed on property_status) into a string. +/// Writes back out as a string. +/// +public class JsonFlexibleStringConverter : JsonConverter +{ + public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.String: + return reader.GetString(); + case JsonTokenType.Number: + return reader.TryGetInt64(out var l) + ? l.ToString(CultureInfo.InvariantCulture) + : reader.GetDouble().ToString(CultureInfo.InvariantCulture); + case JsonTokenType.True: + return "true"; + case JsonTokenType.False: + return "false"; + case JsonTokenType.Null: + return null; + default: + throw new NotSupportedException($"Type {reader.TokenType} not supported for flexible string"); + } + } + + public override void Write(Utf8JsonWriter writer, string? value, JsonSerializerOptions options) + { + if (value is null) + { + writer.WriteNullValue(); + } + else + { + writer.WriteStringValue(value); + } + } +} diff --git a/src/Yllibed.TenantCloudClient/HttpMessages/JsonNullableDecimalConverter.cs b/src/Yllibed.TenantCloudClient/HttpMessages/JsonNullableDecimalConverter.cs new file mode 100644 index 0000000..582419f --- /dev/null +++ b/src/Yllibed.TenantCloudClient/HttpMessages/JsonNullableDecimalConverter.cs @@ -0,0 +1,39 @@ +using System.Globalization; + +namespace Yllibed.TenantCloudClient.HttpMessages; + +/// +/// Reads a JSON decimal value that may also legitimately be null or a +/// numeric string (observed on unit price). Returns null for a +/// JSON null and parses strings tolerantly. +/// +public class JsonNullableDecimalConverter : JsonConverter +{ + public override decimal? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.Null: + return null; + case JsonTokenType.Number: + return reader.GetDecimal(); + case JsonTokenType.String: + var str = reader.GetString(); + return string.IsNullOrWhiteSpace(str) ? null : decimal.Parse(str, CultureInfo.InvariantCulture); + default: + throw new NotSupportedException($"Type {reader.TokenType} not supported for nullable decimal"); + } + } + + public override void Write(Utf8JsonWriter writer, decimal? value, JsonSerializerOptions options) + { + if (value is null) + { + writer.WriteNullValue(); + } + else + { + writer.WriteNumberValue(value.Value); + } + } +} diff --git a/src/Yllibed.TenantCloudClient/HttpMessages/TcProperty.cs b/src/Yllibed.TenantCloudClient/HttpMessages/TcProperty.cs index 33e0374..08ecd2a 100644 --- a/src/Yllibed.TenantCloudClient/HttpMessages/TcProperty.cs +++ b/src/Yllibed.TenantCloudClient/HttpMessages/TcProperty.cs @@ -16,6 +16,7 @@ public class TcProperty : IHasId public string? CityAddress { get; set; } [JsonPropertyName("property_status")] + [JsonConverter(typeof(JsonFlexibleStringConverter))] public string? Status { get; set; } public string Address => string.Format(CultureInfo.InvariantCulture, "{0} {1}", Address1, CityAddress); diff --git a/src/Yllibed.TenantCloudClient/HttpMessages/TcUnit.cs b/src/Yllibed.TenantCloudClient/HttpMessages/TcUnit.cs index 569396f..25efd4e 100644 --- a/src/Yllibed.TenantCloudClient/HttpMessages/TcUnit.cs +++ b/src/Yllibed.TenantCloudClient/HttpMessages/TcUnit.cs @@ -11,7 +11,8 @@ public class TcUnit : IHasId public string? Description { get; set; } - public decimal Price { get; set; } + [JsonConverter(typeof(JsonNullableDecimalConverter))] + public decimal? Price { get; set; } [JsonPropertyName("is_rented")] public bool IsRented { get; set; }