Как использовать атрибуты для управления сериализацией свойства в C # - PullRequest
0 голосов
/ 01 июня 2019

Я хотел бы контролировать, как определенный класс сериализует свои данные ... например:

    [TimeSeriesSerialization(TimeSeriesSerializationType.Dates)]
    public TimeSeries DailyValues { get; set; }

    [TimeSeriesSerialization(TimeSeriesSerializationType.Normal)]
    public TimeSeries IntraDayValues { get; set; }

В моем примере здесь у меня есть класс TimeSeries, который имеет список DateTimes и список десятичных чисел ... когда я сериализую его, я хочу сделать две вещи ... создать строку JSON, которая представляет собой набор пары время / значение ... например:

{"2018-01-30T09:30:01":9958.2289,"2018-01-30T09:30:02":9958.2284,...}

Но я также хотел бы иметь возможность сериализовать этот класс как значения Date (без времени) ... так:

{"2018-01-30":9958.2289,"2018-01-31":9958.2284,...}

или только значения:

[9958.2289,9958.2284,...]

Любая помощь?

public enum TimeSeriesSerializationType
{
    Times,
    Dates,
    ValuesOnly,
}

public class TimeSeries
{
    public List<DateTime> Times = new List<DateTime>();
    public List<decimal> Values = new List<decimal>();

    public void Add(DateTime time, decimal value)
    {
        Times.Add(time);
        Values.Add(value);
    }

    public string Serialize(TimeSeriesSerializationType timeSeriesSerializationType)
    {
        switch (timeSeriesSerializationType)
        {
            case TimeSeriesSerializationType.Dates:
                {
                    var data = new Dictionary<DateTime, decimal>();
                    for (var i = 0; i < Times.Count; i++)
                    {
                        if (!data.ContainsKey(Times[i])) // duplicate dates???
                            data.Add(Times[i], Values[i]);
                    }
                    var isoDateTimeConverter = new IsoDateTimeConverter() { DateTimeFormat = "yyyy-MM-dd" };
                    var json = JsonConvert.SerializeObject(data, Formatting.None, isoDateTimeConverter);
                    return json;
                }
            case TimeSeriesSerializationType.ValuesOnly:
                {
                    var json = JsonConvert.SerializeObject(Values, Formatting.None);
                    return json;
                }
            default:
                {
                    var data = new Dictionary<DateTime, decimal>();
                    for (var i = 0; i < Times.Count; i++)
                    {
                        if (!data.ContainsKey(Times[i])) // duplicate times???
                            data.Add(Times[i], Values[i]);
                    }
                    var isoDateTimeConverter = new IsoDateTimeConverter() { DateTimeFormat = "yyyy-MM-ddTHH\\:mm\\:ss.fffffffzzz" };
                    var json = JsonConvert.SerializeObject(data, Formatting.None, isoDateTimeConverter);
                    return json;
                }
        }
    }

    public void Deserialize(string str)
    {
        if (string.IsNullOrEmpty(str)) return;
        var data = JsonConvert.DeserializeObject<Dictionary<DateTime, decimal>>(str);
        if (data.Count <= 0) return;
        foreach (var d in data)
            this.Add(d.Key, d.Value);
    }
}

Ответы [ 2 ]

0 голосов
/ 02 июня 2019

Более пешеходный, но эффективный подход (для json) ...

    [JsonIgnore]
    public PriceSeries DailyValues { get; set; }
    [JsonProperty(PropertyName = "DailyValues")]
    public string DailyValuesSerialized {
        get
        {
            return (DailyValues == null) ?
                null : 
                DailyValues.Serialize(TimeSeriesSerializationType.ValuesOnly);
        }
        set
        {
            DailyValues = (value == null) ? null : new PriceSeries(value);
        }
    }

    [JsonIgnore]
    public PriceSeries IntraDayValues { get; set; }
    [JsonProperty(PropertyName = "IntraDayValues")]
    public string IntraDayValuesSerialized
    {
        get
        {
            return (IntraDayValues == null) ?
                null : 
                IntraDayValues.Serialize(TimeSeriesSerializationType.Normal);
        }
        set
        {
            IntraDayValues = (value == null) ? null : new PriceSeries(value);
        }
    }

Или это (для базы данных) ...

    [NotMapped]
    public PriceSeries DailyValues { get; set; }
    [Column("DailyValues")]
    public string DailyValuesSerialized
    {
        get
        {
            return (DailyValues == null) ?
                null :
                DailyValues.Serialize(TimeSeriesSerializationType.ValuesOnly);
        }
        set
        {
            DailyValues = (value == null) ? null : new PriceSeries(value);
        }
    }

    [NotMapped]
    public PriceSeries IntraDayValues { get; set; }
    [Column("IntraDayValues")]
    public string IntraDayValuesSerialized
    {
        get
        {
            return (IntraDayValues == null) ?
                null :
                IntraDayValues.Serialize(TimeSeriesSerializationType.Normal);
        }
        set
        {
            IntraDayValues = (value == null) ? null : new PriceSeries(value);
        }
    }
0 голосов
/ 01 июня 2019

Я думаю, у вас уже есть большая часть того, что вам нужно.
Чего не хватает, так это самого атрибута.

Вы бы определили его так (вы можете прочитать больше об AttributeUsage-Attribute здесь ):

[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
class TimeSeriesSerializationAttribute : Attribute
{
    public TimeSeriesSerializationType SerializationType { get; }

    public TimeSeriesSerializationAttribute(TimeSeriesSerializationType timeSeriesSerializationType)
    {
        SerializationType = timeSeriesSerializationType;
    }
}

Теперь вы можете добавить те, что выше ваших свойств, простокак ты уже писал.

Время для оценочной части.Вы можете использовать что-то вроде этого, чтобы прочитать все свойства и использовать их для сериализации ваших TimeSeries.
Кстати, если атрибут, который вы получаете, становится нулевым, атрибут просто не устанавливается.Вы можете либо полностью пропустить свойство, либо использовать SerializationType по умолчанию, что бы вам ни понадобилось.

public static void SerializeToSomewhere(YourClassWithTimeSeries instance) {
    // get all public, instance properties 
    IEnumerable<PropertyInfo> allProps = typeof(YourClassWithTimeSeries).GetProperties(BindingFlags.Public | BindingFlags.Instance);
    // only take TimeSeries-properties
    IEnumerable<PropertyInfo> timeSeriesProps = allProps.Where(p => p.PropertyType == typeof(TimeSeries));

    foreach (PropertyInfo property in timeSeriesProps)
    {
        // get the attribute
        TimeSeriesSerializationAttribute attribute = property.GetCustomAttribute<TimeSeriesSerializationAttribute>();
        if (attribute == null) continue; // or do something else like use a default SerializationType

        // get the value
        TimeSeries value = (TimeSeries)property.GetValue(instance, null);

        // serialize using the type from the attribute
        string serializedValue = value.Serialize(attribute.SerializationType);
        // Do whatever with the serialized value (for example save it to a file or whatever)
    }
}

Надеюсь, это поможет вам.Есть также множество учебников и документов по атрибутам, если вам нужна дополнительная информация.Я также постараюсь ответить на вопросы, которые у вас могут возникнуть, поэтому не стесняйтесь задавать.

Редактировать:
Небольшая вещь, чтобы добавить.В вашем коде ваш класс TimeSeries предоставляет открытые поля Times и Values.Ты не должен этого делать.Вместо этого:

public List<DateTime> Times = new List<DateTime>();
public List<decimal> Values = new List<decimal>();

Вы должны сделать это.Это мешает вам случайно назначить его снова внутри или за пределами класса.

public List<DateTime> Times { get; } = new List<DateTime>();
public List<decimal> Values { get; } = new List<decimal>();
...