简体   繁体   English

为 appsettings.json 编写自定义转换器

[英]Writing a custom converter for appsettings.json

I want to create an appsettings.json converter which converts Symbols to IReadOnlyCollection<Symbol> .我想创建一个 appsettings.json 转换器,它将Symbols转换为IReadOnlyCollection<Symbol> The converter should split the string by / , which will result into a BaseAsset/QuoteAsset.转换器应该用/分割字符串,这将产生一个 BaseAsset/QuoteAsset。 It should then check whether QuoteAsset equals to StakeCurrency or not.然后它应该检查QuoteAsset是否等于StakeCurrency If it doesn't, throw an exception.如果没有,则抛出异常。 What is the best way to do that with a custom converter?使用自定义转换器做到这一点的最佳方法是什么? I don't want to use binding.我不想使用绑定。 Is it possible to use a custom JsonConverter?是否可以使用自定义 JsonConverter?

  • appsettings.json appsettings.json
{
  "BacktestConfiguration": {
    "StakeCurrency": "USDT",
    "Symbols": [ "TRX/USDT", "BTC/USDT", "ETH/USDT" ]
  }
}
  • Classes课程
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; }
}

You can use a custom JsonConverter to handle the transformation from the Array of strings to a IReadOnlyCollection<Symbol> .您可以使用自定义 JsonConverter 来处理从字符串数组到IReadOnlyCollection<Symbol>的转换。

Decorate the Symbols property of the BacktestOptions class with the converter Type and handle the conversion in the custom converter's Read() method, where you split the strings in the array to generate new Symbol objects with the parts.使用转换器 Type 装饰BacktestOptions class 的Symbols属性,并在自定义转换器的Read()方法中处理转换,您可以在其中拆分数组中的字符串以生成带有部件的新Symbol对象。
Then return a new ReadOnlyCollection<Symbol> from the generated list of Symbol objects.然后从生成的Symbol对象列表中返回一个新的ReadOnlyCollection<Symbol>

I'm using a handler class, BacktestConfigurationHandler , to contain the objects and provide the base conversion and deserialization functionality.我正在使用处理程序class BacktestConfigurationHandler来包含对象并提供基本转换和反序列化功能。

Call the static Deserialize() method, passing the JSON as argument.调用 static Deserialize()方法,将 JSON 作为参数传递。 It returns a BacktestConfiguration object when there's no mismatch between the StakeCurrency value and any of the Symbol[].QuoteAsset values.当 StakeCurrency 值与任何 Symbol[].QuoteAsset 值不匹配时,它会返回一个BacktestConfiguration object。
It throws a JsonException in case there's a mismatch.如果不匹配,它会抛出JsonException

Call it as:称它为:

var configuration = BacktestConfigurationHandler.Deserialize(json);

BacktestConfigurationHandler class : BacktestConfigurationHandler配置处理程序 class

► It handles deserialization only. ► 它只处理反序列化。 The serialization, as you can see, is not implemented: the Write() method does nothing.如您所见,序列化没有实现: 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();
    }
}

EDIT:编辑:
Try these variations of the class Model and the adapted custom converters, both a JSON converter and a TypeConverter assigned to the Symbols Property.尝试 class Model 的这些变体和适配的自定义转换器,包括 JSON 转换器和分配给 Symbols 属性的 TypeConverter。

I've tested the converters in a base scenario, converting from JSON to a class model directly and accessing / casting the objects using the implicit operators called by the TypeConverter.我已经在基本场景中测试了转换器,从 JSON 转换为 class model 直接使用 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)} ]";
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM