Добавление параметра к свойству
Суть вопроса в том, что я получаю набор шестнадцатеричных данных в формате string. пример:
string dataLine = "63EDF9AC00028702F60303040800000203096000210102805000010012650309000";
Данных много, dataLine представлена не полностью.
Для начала, я перевожу dataLine в List, группируя по 2 символа:
var data = new List<string>();
for (int i = 2; i < dataLine.Length / 2; i += 2)
{
data.Add(dataLine.Substring(0, 2));
dataLine = dataLine.Substring(2);
}
Мне нужно эти данные привести к свойствам модели. Но каждое свойство имеет разную длину в байтах. Например первые 3 из них:
/// <summary>
/// UNIX-штамп обновления пакета в секундах (с первого января 1970г.)
/// Тип: 8 Целое число, беззнаковое, 4 байта
/// Позиция: 0
/// Количество символов: 8
/// </summary>
public int PackTime { get; set; }
/// <summary>
/// Номер пакета (0 - данные телеметрии)
/// Тип: Целое число, беззнаковое, 1 байт
/// Позиция: 8
/// Количество символов: 2
/// </summary>
public int PackNo { get; set; }
/// <summary>
/// Позиция: 10
/// Количество символов: 4
/// </summary>
public int SW { get; set; }
Есть мысль получать все свойства модели через:
var properties = this.GetType().GetProperties();
И в цикле перебирая свойства присваивать им значения, на основании количества символов. и каждый раз удалять элементы из data,опять же согласно (количества символов)/2.
Можно ли как то добавить что то типа параметра к свойству?
Использовал вариант с кастомным атрибутом, но я не знаю на сколько это костыльно:
internal class SymbolCountAttribute : Attribute
{
private int SymbolCount;
public SymbolCountAttribute(int symbolCount)
{
SymbolCount = symbolCount;
}
}
/// <summary>
/// UNIX-штамп обновления пакета в секундах (с первого января 1970г.)
/// Тип: 8 Целое число, беззнаковое, 4 байта
/// Позиция: 0
/// Количество символов: 8
/// </summary>
[SymbolCount(8)]
public int PackTime { get; set; }
Данные по атрибуту получаю следующим образом:
foreach (var prop in properties)
{
var value = prop.CustomAttributes.First().ConstructorArguments.First().Value;
}
Ответы (1 шт):
Я вам уже давал ответ на вопрос про сериализацию.
Не надо никаких аттрибутов, надо сделать модель с типами, которые соответствуют данным, вот например так.
class MyModel
{
/// <summary>
/// UNIX-штамп обновления пакета в секундах (с первого января 1970г.)
/// Тип: 8 Целое число, беззнаковое, 4 байта
/// Позиция: 0
/// Количество символов: 8
/// </summary>
public uint PackTime { get; set; }
/// <summary>
/// Номер пакета (0 - данные телеметрии)
/// Тип: Целое число, беззнаковое, 1 байт
/// Позиция: 8
/// Количество символов: 2
/// </summary>
public byte PackNo { get; set; }
/// <summary>
/// Позиция: 10
/// Количество символов: 4
/// </summary>
public ushort SW { get; set; }
}
Не надо выдумывать своих преобразователей 16-ричной записи, они уже есть встроенные в .NET. Возьму к примеру только данные из начала строки, соответствующие суммарной длине ваших свойств.
string dataLine = "63EDF9AC000287";
Span<byte> data = Convert.FromHexString(dataLine);
И далее просто распарсить например используя рефлексию.
int index = 0;
MyModel model = new MyModel();
foreach (PropertyInfo prop in typeof(MyModel).GetProperties())
{
switch (prop.GetValue(model))
{
case uint:
prop.SetValue(model, BinaryPrimitives.ReadUInt32BigEndian(data[index..]));
index += sizeof(uint); // 4
break;
case byte:
prop.SetValue(model, data[index]);
index++;
break;
case ushort:
prop.SetValue(model, BinaryPrimitives.ReadUInt16BigEndian(data[index..]));
index += sizeof(ushort); // 2
break;
}
}
Console.WriteLine(JsonSerializer.Serialize(model));
Console.WriteLine(DateTimeOffset.FromUnixTimeSeconds(model.PackTime));
Вывод в консоль
{"PackTime":1676540332,"PackNo":0,"SW":647}
16.02.2023 09:38:52 +00:00