2

我想创建一个 appsettings.json 转换器,它可以转换SymbolsIReadOnlyCollection<Symbol>. 转换器应将字​​符串拆分为/,这将生成 BaseAsset/QuoteAsset。然后它应该检查是否QuoteAsset等于StakeCurrency。如果没有,则抛出异常。使用自定义转换器做到这一点的最佳方法是什么?我不想使用绑定。是否可以使用自定义 JsonConverter?

  • 应用设置.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; }
}
4

1 回答 1

3

您可以使用自定义 JsonConverter 来处理从字符串数组到IReadOnlyCollection<Symbol>.

使用转换器类型装饰类的Symbols属性BacktestOptions并在自定义转换器的Read()方法中处理转换,您可以在其中拆分数组中的字符串以生成Symbol带有部件的新对象。然后从生成的对象列表中
返回一个新的。ReadOnlyCollection<Symbol>Symbol

我正在使用处理程序类 ,BacktestConfigurationHandler来包含对象并提供基本转换和反序列化功能。

调用静态Deserialize()方法,将 JSON 作为参数传递。BacktestConfiguration当 StakeCurrency 值与任何 Symbol[].QuoteAsset 值不匹配时,它会返回一个对象。
如果不匹配,它会抛出一个JsonException

称它为:

var configuration = BacktestConfigurationHandler.Deserialize(json);

BacktestConfigurationHandler

► 它只处理反序列化。如您所见,序列化没有实现:该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();
    }
}

编辑:
尝试模型类的这些变体和适应的自定义转换器,一个 JSON 转换器和一个分配给 Symbols 属性的 TypeConverter。

我已经在基本场景中测试了转换器,直接从 JSON 转换为类模型,并使用 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)} ]";
}
于 2021-03-09T11:13:33.940 回答