【问题标题】:Writing a custom converter for appsettings.json为 appsettings.json 编写自定义转换器
【发布时间】:2021-03-09 10:00:41
【问题描述】:

我想创建一个 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; }
}

【问题讨论】:

    标签: c# appsettings jsonconverter


    【解决方案1】:

    您可以使用自定义 JsonConverter 来处理从字符串数组到 IReadOnlyCollection&lt;Symbol&gt; 的转换。

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

    我正在使用 handlerBacktestConfigurationHandler 来包含对象并提供基本转换和反序列化功能。

    调用静态 Deserialize() 方法,将 JSON 作为参数传递。 当 StakeCurrency 值与任何 Symbol[].QuoteAsset 值不匹配时,它会返回一个 BacktestConfiguration 对象。
    它会抛出一个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();
        }
    }
    

    编辑:
    试试这些 Model 类的变体和适配的自定义转换器,包括 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)} ]";
    }
    

    【讨论】:

    • 感谢您的回答!这意味着services.Configure&lt;BacktestOptions&gt;(hostingContext.Configuration.GetSection(BacktestOptions.Backtest)) 无法自动生成?
    • 是的,stackoverflow.com/questions/62820147/…。我想要像public static implicit operator string(UppercaseString upper) =&gt; upper.Value; 这样的东西,它会自动转换它。基本上,一旦我 DI Options&lt;T&gt;,它应该会自动解决它。
    • 我添加了一个修改过的类模型和 JSON / 类型转换器。给它看看。我不确定它是否可以在您描述的场景中工作。
    • 感谢您付出的努力!在那段时间我提出了一个更具体的问题,我解决了:stackoverflow.com/questions/66553181/… - 你可以看看。 TypeConverter 本身在没有implicit 转换器的情况下也能正常工作,但它的属性应该放在 Symbol 类之上,而不是放在 BacktestOptions 中。我现在接受答案,再次感谢
    猜你喜欢
    • 2011-04-06
    • 2021-03-25
    • 1970-01-01
    • 2017-06-22
    • 2011-05-13
    • 1970-01-01
    • 2010-10-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多