为 appsettings.json 编写自定义转换器
Writing a custom converter for appsettings.json
我想创建一个 appsettings.json 转换器,将 Symbols
转换为 IReadOnlyCollection<Symbol>
。转换器应将字符串拆分为 /
,这将导致 BaseAsset/QuoteAsset。然后它应该检查 QuoteAsset
是否等于 StakeCurrency
。如果没有,则抛出异常。使用自定义转换器执行此操作的最佳方法是什么?我不想使用绑定。是否可以使用自定义 JsonConverter?
- appsettings.json
{
"BacktestConfiguration": {
"StakeCurrency": "USDT",
"Symbols": [ "TRX/USDT", "BTC/USDT", "ETH/USDT" ]
}
}
- 类
public class BacktestOptions
{
public const string Position = "BacktestConfiguration";
public string StakeCurrency { get; set; }
public IReadOnlyCollection<Symbol> Symbols { get; set; }
}
public class Symbol
{
public string BaseAsset { get; set; }
public string QuoteAsset { get; set; }
}
您可以使用自定义 JsonConverter 来处理从字符串数组到 IReadOnlyCollection<Symbol>
的转换。
用转换器类型修饰BacktestOptions
属性的Symbols
属性,并在自定义转换器的Read()
方法中处理转换,其中你拆分数组中的字符串以生成新的 Symbol
个对象。
然后 return 从生成的 Symbol
个对象列表中创建一个新的 ReadOnlyCollection<Symbol>
。
我正在使用 处理程序 class、BacktestConfigurationHandler
来包含对象并提供基本转换和反序列化功能。
调用静态 Deserialize()
方法,将 JSON 作为参数传递。
当 StakeCurrency 值与任何 Symbol[].QuoteAsset 值不匹配时,它 return 是一个 BacktestConfiguration
对象。
如果不匹配,它会抛出 JsonException
。
称其为:
var configuration = BacktestConfigurationHandler.Deserialize(json);
BacktestConfigurationHandler
class:
► 它只处理反序列化。如您所见,序列化没有实现:Write()
方法什么都不做。
public class BacktestConfigurationHandler
{
public class BacktestRoot {
public BacktestConfiguration BacktestConfiguration { get; set; }
}
public class BacktestConfiguration
{
public const string Position = "BacktestConfiguration";
public string StakeCurrency { get; set; }
[JsonConverter(typeof(SymbolConverter))]
public IReadOnlyCollection<Symbol> Symbols { get; set; }
}
public class Symbol
{
public Symbol() : this("", "") { }
public Symbol(string baseAsset, string quoteAsset) {
BaseAsset = baseAsset;
QuoteAsset = quoteAsset;
}
public string BaseAsset { get; set; }
public string QuoteAsset { get; set; }
}
public static BacktestConfiguration Deserialize(string json)
{
var root = JsonSerializer.Deserialize<BacktestRoot>(json);
var stakeCurrency = root.BacktestConfiguration.StakeCurrency;
if (root.BacktestConfiguration.Symbols.Any(s => s.QuoteAsset != stakeCurrency)) {
throw new JsonException("StakeCurrency mismatch");
}
return root.BacktestConfiguration;
}
public class SymbolConverter : JsonConverter<IReadOnlyCollection<Symbol>>
{
public override IReadOnlyCollection<Symbol> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.StartArray) {
var symbols = new List<Symbol>();
var values = JsonSerializer.Deserialize<string[]>(ref reader, options);
foreach (string value in values) {
var parts = value.Split('/');
symbols.Add(new Symbol(parts[0], parts[1]));
}
return new ReadOnlyCollection<Symbol>(symbols);
}
return null;
public override void Write(Utf8JsonWriter writer, IReadOnlyCollection<Symbol> value, JsonSerializerOptions options)
=> throw new NotImplementedException();
}
}
编辑:
尝试 class 模型和改编的自定义转换器的这些变体,一个 JSON 转换器和一个分配给符号 属性.
的类型转换器
我在基本场景中测试了转换器,直接从 JSON 转换为 class 模型,并使用 TypeConverter 调用的隐式运算符访问/转换对象。
public class BacktestOptionsRoot
{
public BacktestOptions BacktestConfiguration { get; set; }
}
public class BacktestOptions
{
public const string Position = "BacktestConfiguration";
public string StakeCurrency { get; set; }
[JsonConverter(typeof(JsonSymbolConverter))]
[TypeConverter(typeof(BacktestSymbolsConverter))]
public BacktestSymbols Symbols { get; set; }
}
public class Symbol
{
public Symbol() : this("", "") { }
public Symbol(string baseAsset, string quoteAsset)
{
BaseAsset = baseAsset;
QuoteAsset = quoteAsset;
}
public string BaseAsset { get; set; }
public string QuoteAsset { get; set; }
}
public class BacktestSymbolsConverter : TypeConverter
{
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string[])) {
return (BacktestSymbols)(value as string[]);
}
return base.ConvertTo(context, culture, value, destinationType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is BacktestSymbols) {
return (string[])(value as BacktestSymbols);
}
return base.ConvertFrom(context, culture, value);
}
}
public class JsonSymbolConverter : JsonConverter<BacktestSymbols>
{
public override bool CanConvert(Type typeToConvert)
{
return typeToConvert == typeof(BacktestSymbols);
}
public override BacktestSymbols Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.StartArray) {
return JsonSerializer.Deserialize<string[]>(ref reader, options);
}
return null;
}
public override void Write(Utf8JsonWriter writer, BacktestSymbols value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
public class BacktestSymbols
{
public ReadOnlyCollection<Symbol> Value { get; }
public string[] Symbols => Value.Select(v => $"{v.BaseAsset}/{v.QuoteAsset}").ToArray();
public BacktestSymbols(string[] source)
{
var symbols = new List<Symbol>();
if (source != null && source.Length > 0) {
foreach (string value in source) {
var parts = value.Split('/');
symbols.Add(new Symbol(parts[0], parts[1]));
}
}
Value = new ReadOnlyCollection<Symbol>(symbols);
}
public static implicit operator BacktestSymbols(string[] source) => new BacktestSymbols(source);
public static implicit operator string[](BacktestSymbols instance) => instance.Symbols;
public override string ToString() => $"[ {string.Join(",", Symbols)} ]";
}
我想创建一个 appsettings.json 转换器,将 Symbols
转换为 IReadOnlyCollection<Symbol>
。转换器应将字符串拆分为 /
,这将导致 BaseAsset/QuoteAsset。然后它应该检查 QuoteAsset
是否等于 StakeCurrency
。如果没有,则抛出异常。使用自定义转换器执行此操作的最佳方法是什么?我不想使用绑定。是否可以使用自定义 JsonConverter?
- appsettings.json
{
"BacktestConfiguration": {
"StakeCurrency": "USDT",
"Symbols": [ "TRX/USDT", "BTC/USDT", "ETH/USDT" ]
}
}
- 类
public class BacktestOptions
{
public const string Position = "BacktestConfiguration";
public string StakeCurrency { get; set; }
public IReadOnlyCollection<Symbol> Symbols { get; set; }
}
public class Symbol
{
public string BaseAsset { get; set; }
public string QuoteAsset { get; set; }
}
您可以使用自定义 JsonConverter 来处理从字符串数组到 IReadOnlyCollection<Symbol>
的转换。
用转换器类型修饰BacktestOptions
属性的Symbols
属性,并在自定义转换器的Read()
方法中处理转换,其中你拆分数组中的字符串以生成新的 Symbol
个对象。
然后 return 从生成的 Symbol
个对象列表中创建一个新的 ReadOnlyCollection<Symbol>
。
我正在使用 处理程序 class、BacktestConfigurationHandler
来包含对象并提供基本转换和反序列化功能。
调用静态 Deserialize()
方法,将 JSON 作为参数传递。
当 StakeCurrency 值与任何 Symbol[].QuoteAsset 值不匹配时,它 return 是一个 BacktestConfiguration
对象。
如果不匹配,它会抛出 JsonException
。
称其为:
var configuration = BacktestConfigurationHandler.Deserialize(json);
BacktestConfigurationHandler
class:
► 它只处理反序列化。如您所见,序列化没有实现:Write()
方法什么都不做。
public class BacktestConfigurationHandler
{
public class BacktestRoot {
public BacktestConfiguration BacktestConfiguration { get; set; }
}
public class BacktestConfiguration
{
public const string Position = "BacktestConfiguration";
public string StakeCurrency { get; set; }
[JsonConverter(typeof(SymbolConverter))]
public IReadOnlyCollection<Symbol> Symbols { get; set; }
}
public class Symbol
{
public Symbol() : this("", "") { }
public Symbol(string baseAsset, string quoteAsset) {
BaseAsset = baseAsset;
QuoteAsset = quoteAsset;
}
public string BaseAsset { get; set; }
public string QuoteAsset { get; set; }
}
public static BacktestConfiguration Deserialize(string json)
{
var root = JsonSerializer.Deserialize<BacktestRoot>(json);
var stakeCurrency = root.BacktestConfiguration.StakeCurrency;
if (root.BacktestConfiguration.Symbols.Any(s => s.QuoteAsset != stakeCurrency)) {
throw new JsonException("StakeCurrency mismatch");
}
return root.BacktestConfiguration;
}
public class SymbolConverter : JsonConverter<IReadOnlyCollection<Symbol>>
{
public override IReadOnlyCollection<Symbol> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.StartArray) {
var symbols = new List<Symbol>();
var values = JsonSerializer.Deserialize<string[]>(ref reader, options);
foreach (string value in values) {
var parts = value.Split('/');
symbols.Add(new Symbol(parts[0], parts[1]));
}
return new ReadOnlyCollection<Symbol>(symbols);
}
return null;
public override void Write(Utf8JsonWriter writer, IReadOnlyCollection<Symbol> value, JsonSerializerOptions options)
=> throw new NotImplementedException();
}
}
编辑:
尝试 class 模型和改编的自定义转换器的这些变体,一个 JSON 转换器和一个分配给符号 属性.
我在基本场景中测试了转换器,直接从 JSON 转换为 class 模型,并使用 TypeConverter 调用的隐式运算符访问/转换对象。
public class BacktestOptionsRoot
{
public BacktestOptions BacktestConfiguration { get; set; }
}
public class BacktestOptions
{
public const string Position = "BacktestConfiguration";
public string StakeCurrency { get; set; }
[JsonConverter(typeof(JsonSymbolConverter))]
[TypeConverter(typeof(BacktestSymbolsConverter))]
public BacktestSymbols Symbols { get; set; }
}
public class Symbol
{
public Symbol() : this("", "") { }
public Symbol(string baseAsset, string quoteAsset)
{
BaseAsset = baseAsset;
QuoteAsset = quoteAsset;
}
public string BaseAsset { get; set; }
public string QuoteAsset { get; set; }
}
public class BacktestSymbolsConverter : TypeConverter
{
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string[])) {
return (BacktestSymbols)(value as string[]);
}
return base.ConvertTo(context, culture, value, destinationType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is BacktestSymbols) {
return (string[])(value as BacktestSymbols);
}
return base.ConvertFrom(context, culture, value);
}
}
public class JsonSymbolConverter : JsonConverter<BacktestSymbols>
{
public override bool CanConvert(Type typeToConvert)
{
return typeToConvert == typeof(BacktestSymbols);
}
public override BacktestSymbols Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.StartArray) {
return JsonSerializer.Deserialize<string[]>(ref reader, options);
}
return null;
}
public override void Write(Utf8JsonWriter writer, BacktestSymbols value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
public class BacktestSymbols
{
public ReadOnlyCollection<Symbol> Value { get; }
public string[] Symbols => Value.Select(v => $"{v.BaseAsset}/{v.QuoteAsset}").ToArray();
public BacktestSymbols(string[] source)
{
var symbols = new List<Symbol>();
if (source != null && source.Length > 0) {
foreach (string value in source) {
var parts = value.Split('/');
symbols.Add(new Symbol(parts[0], parts[1]));
}
}
Value = new ReadOnlyCollection<Symbol>(symbols);
}
public static implicit operator BacktestSymbols(string[] source) => new BacktestSymbols(source);
public static implicit operator string[](BacktestSymbols instance) => instance.Symbols;
public override string ToString() => $"[ {string.Join(",", Symbols)} ]";
}