2 minutes
Własny JsonConverter
Wstęp
Ostatnio w pracy integrowałem nasz system z zewnętrznym API. Musiałem zintegorać zarządzanie zadaniami klienta. W wyniku czego powstał w kodzie taki enum:
internal enum CustomerTaskStatus
{
Pending = 1,
Completed = 2,
Cancelled = 3,
}
Nie trzeba tłumaczyć, wiadomo o co chodzi 🙂
Z racji tego, że ten status dostaję z zewnętrznego API, nie mam wpływu jakie wartości zwróci. A zwraca takie wartości:
PENDING
COMPLETED
CANCELLED
Domyślny deserializer ASP.NET Core nie poradzi sobie z tym, więc trzeba napisać własny.
Przykład z domyślnym deserializerem
Kod uproszczony na maksa, aby tylko pokazać problem z deserializerem.
using System.Text.Json;
using System.Text.Json.Serialization;
string json = """
{
"Status": "PENDING"
}
""";
var customerTask = JsonSerializer.Deserialize<CustomerTask>(json);
Console.WriteLine($"Status: {customerTask!.Status}");
// Obiekt do którego deserializujemy
internal sealed class CustomerTask
{
public CustomerTaskStatus Status { get; set; }
}
internal enum CustomerTaskStatus
{
Pending = 1,
Completed = 2,
Cancelled = 3
}
W takim przypadku dostajemy błąd:
System.Text.Json.JsonException: The JSON value could not be converted to CustomerTaskStatus. Path: $.Status | LineNumber: 1 | BytePositionInLine: 21
ponieważ nie może sobie poradzić z zamianą PENDING
na Pending
.
Właśny JsonConverter
Dodajemy własny JsonConverter
:
internal sealed class CustomerTaskStatusJsonConverter : JsonConverter<CustomerTaskStatus>
{
/// <inheritdoc />
public override CustomerTaskStatus Read(ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
return Enum.Parse<CustomerTaskStatus>(reader.GetString()!, true);
}
/// <inheritdoc />
public override void Write(Utf8JsonWriter writer,
CustomerTaskStatus value,
JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString().ToUpper());
}
}
Nasz konwerter musi dziedziczyć po generecznym JsonConverter<CustomerTaskStatus>
, gdzie przekazujemy naszego enuma.
Musimy napisać implementację 2 metoda:
Read
- która jest używana przy deserializacji. W naszym przypadku używamEnum.Parse
, gdzie ostatni parametr jesttrue
. A ten parametr oznacza właśnie, aby zignorował wielkość liter.Write
- które jest używana przy serializji
Jeszcze tylko musimy oznaczyć naszą właściwość, aby użyć tego convertera
internal sealed class CustomerTask
{
[JsonConverter(typeof(CustomerTaskStatusJsonConverter))]
public CustomerTaskStatus Status { get; set; }
}
I to tyle 😄 Wszystko działa. Można wrócić do dalszej integracji zewnętrznego API 🙂
Cały kod tego przykładu jest dostępny na GitHubie: https://gist.github.com/tomaszprasolek/930f600f9d414045c0088c6c2d718fe7
Linki
333 Words
2024-06-09 15:48