我的一項服務有一個相當大且復雜的 json 輸入模式。因此,該解決方案涉及大約 50 個自定義 JsonConverters 來處理不同的型別、多型性等。這一切都有效,但每個轉換器中都有大量重復的代碼,唯一的區別是設定區域變數,處理預期的屬性在 EndObject 處理程式中鍵入并構造它:
public class Message
{
public int Id { get; set; }
public DateTime SentAt { get; set; }
public string MessageText { get; set; }
public Message(int id, DateTime sentAt, string messageText)
{
Id = id;
SentAt = sentAt;
MessageText = messageText;
}
}
internal class ExampleConverter : JsonConverter<Message>
{
public override Message Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartObject)
throw new JsonException("Expected StartObject token");
var id = Option.None<int>();
var sentAt = Option.None<DateTime>();
var messageText = Option.None<string>();
try
{
while (reader.Read())
{
switch (reader.TokenType)
{
case JsonTokenType.EndObject:
return new Message(id, sentAt, messageText);
case JsonTokenType.PropertyName:
var propName = reader.GetString();
reader.Read();
switch(propName)
{
case nameof(Message.Id):
id = Option.Some(int.Parse(reader.GetString() ?? string.Empty));
break;
case nameof(Message.SentAt):
sentAt = Option.Some(reader.GetDateTime());
break;
case nameof(Message.MessageText):
messageText = Option.Some(reader.GetString()?.ToUpper());
break;
}
break;
}
}
}
catch (OptionValueMissingException e)
{
throw new Exception(
$"Missing property of type {e.OptionValueType}", e);
}
catch (JsonException e)
{
throw new Exception(
$"Corrupted json", e);
}
catch (InvalidOperationException e)
{
throw new Exception(
$"Unexpected type found", e);
}
throw new JsonException("Expected EndObject token");
}
public override void Write(Utf8JsonWriter writer, Message value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
我希望我可以創建JsonConverterEx<T>派生JsonConverter<T>并提供我自己的ReadTypeFromJson方法,該方法采用函式來處理 EndObject 和 PropertyName 型別,如下所示:
public abstract class JsonConverterEx<T> : JsonConverter<T>
{
protected T ReadTypeFromJson(Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, Func<T> handleJsonEndObject, Action<string> handleJsonPropertyName)
{
if (reader.TokenType != JsonTokenType.StartObject)
throw new JsonException("Expected StartObject token");
try
{
while (reader.Read())
{
switch (reader.TokenType)
{
case JsonTokenType.EndObject:
return handleJsonEndObject();
case JsonTokenType.PropertyName:
var propName = reader.GetString();
reader.Read();
handleJsonPropertyName(propName);
break;
}
}
}
catch (OptionValueMissingException e)
{
throw new Exception(
$"Missing property of type {e.OptionValueType}", e);
}
catch (JsonException e)
{
throw new Exception(
$"Corrupted json", e);
}
catch (InvalidOperationException e)
{
throw new Exception(
$"Unexpected type found", e);
}
throw new JsonException("Expected EndObject token");
}
}
然后像這樣使用它:
internal class ExampleConverter : JsonConverterEx<Message>
{
public override Message Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartObject)
throw new JsonException("Expected StartObject token");
var id = Option.None<int>();
var sentAt = Option.None<DateTime>();
var messageText = Option.None<string>();
return ReadTypeFromJson(reader, typeToConvert, options,
() => new Message(id, sentAt, messageText),
(propertyName) =>
{
switch (propertyName)
{
case nameof(Message.Id):
id = Option.Some(int.Parse(reader.GetString() ?? string.Empty));
break;
case nameof(Message.SentAt):
sentAt = Option.Some(reader.GetDateTime());
break;
case nameof(Message.MessageText):
messageText = Option.Some(reader.GetString()?.ToUpper());
break;
}
});
}
public override void Write(Utf8JsonWriter writer, Message value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
這將減少我所有轉換器中重復代碼的數量,但我不能這樣做,因為我無法reader在我的 lambda 中參考 ByRef Utf8JsonReader,在handleJsonPropertyNamelambda 中的呼叫reader給了我Cannot use 'ref' parameter 'reader' inside lambda expression,我不能將它作為lambda 的一個引數,因為它是ByRef并且我得到Cannot use 'System.Text.Json.Utf8JsonReader' as a type argument.
我已經閱讀了 ByRef 結構并理解為什么我不能這樣做(所以只包括上面以顯示“我嘗試過的東西”)但是我還能做些什么來避免所有這些重復的樣板檔案?我需要使用轉換器,因為我的架構對于 System.Text.Json 來說太復雜而無法在沒有的情況下解決。
TIA
富有的
uj5u.com熱心網友回復:
您不能ref struct在 lambda 中捕獲 a。在這種情況下,無論如何您都不應該這樣做,因為ReadTypeFromJson可以訪問閱讀器。您應該將其從ReadTypeFromJson函式傳遞給 lambda。
case JsonTokenType.PropertyName:
var propName = reader.GetString();
reader.Read();
handleJsonPropertyName(reader, propName);
break;
并將 lambda 更改為不捕獲:
return ReadTypeFromJson(reader, typeToConvert, options,
() => new Message(id, sentAt, messageText),
(reader2, propertyName) =>
{
switch (propertyName)
{
case nameof(Message.Id):
id = Option.Some<int>(int.Parse(reader2.GetString() ?? string.Empty));
break;
case nameof(Message.SentAt):
sentAt = Option.Some<DateTime>(reader2.GetDateTime());
break;
case nameof(Message.MessageText):
messageText = Option.Some<string>(reader2.GetString()?.ToUpper());
break;
}
});
但是,您也不能將委托宣告為Action<Utf8JsonReader, string>,因此您需要創建自定義委托型別
public delegate void HandleJsonPropertyNameDelegate(Utf8JsonReader reader, string name);
然后將宣告更改為ReadTypeFromJson
protected T ReadTypeFromJson(
Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options,
Func<T> handleJsonEndObject, HandleJsonPropertyNameDelegate handleJsonPropertyName)
dotnetfiddle
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/504929.html
上一篇:C#桌面應用程式多影像選擇
下一篇:C#正則運算式匹配
