diff options
Diffstat (limited to 'src/Cepis/GeoConverter.cs')
| -rw-r--r-- | src/Cepis/GeoConverter.cs | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/src/Cepis/GeoConverter.cs b/src/Cepis/GeoConverter.cs new file mode 100644 index 0000000..f5c78de --- /dev/null +++ b/src/Cepis/GeoConverter.cs @@ -0,0 +1,89 @@ +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Cepis; + +/// <summary> +/// Reads the <c>geo</c> block, yielding <see langword="null"/> for anything it +/// cannot make sense of. +/// </summary> +/// <remarks> +/// A malformed coordinate is not worth losing the whole address over: the rest +/// of the response is still useful, and an address with no coordinate is a +/// shape callers already handle, since about 43% of CEPs have none. +/// </remarks> +internal sealed class GeoConverter : JsonConverter<Geo?> +{ + public override Geo? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var document = JsonDocument.ParseValue(ref reader); + var root = document.RootElement; + if (root.ValueKind != JsonValueKind.Object) + { + return null; + } + + var lat = ReadDouble(root, "lat"); + var lon = ReadDouble(root, "lon"); + var raio = ReadDouble(root, "raio_m"); + var precisao = ReadString(root, "precisao"); + + if (lat is null || lon is null || raio is null || precisao is null) + { + return null; + } + + return new Geo + { + Lat = lat.Value, + Lon = lon.Value, + RaioM = (int)Math.Round(raio.Value), + Precisao = precisao, + }; + } + + public override void Write(Utf8JsonWriter writer, Geo? value, JsonSerializerOptions options) + { + ArgumentNullException.ThrowIfNull(writer); + + if (value is null) + { + writer.WriteNullValue(); + return; + } + + writer.WriteStartObject(); + writer.WriteNumber("lat", value.Lat); + writer.WriteNumber("lon", value.Lon); + writer.WriteNumber("raio_m", value.RaioM); + writer.WriteString("precisao", value.Precisao); + writer.WriteEndObject(); + } + + private static double? ReadDouble(JsonElement owner, string name) + { + if (!owner.TryGetProperty(name, out var element)) + { + return null; + } + + return element.ValueKind switch + { + JsonValueKind.Number => element.TryGetDouble(out var number) ? number : null, + JsonValueKind.String => double.TryParse( + element.GetString(), + NumberStyles.Float, + CultureInfo.InvariantCulture, + out var parsed) + ? parsed + : null, + _ => null, + }; + } + + private static string? ReadString(JsonElement owner, string name) => + owner.TryGetProperty(name, out var element) && element.ValueKind == JsonValueKind.String + ? element.GetString() + : null; +} |