В этой статье я хотел бы рассказать об опыте нашей команды по созданию универсального преобразователя данных.
На первый взгляд звучит очень просто, что в этом такого сложного? Возьмите один тип данных и преобразуйте его в другой тип.
Что, если данные представляют собой структуру? Это тоже не сложно, скажете вы, надо просто нанести на карту поля.
Да, просто.
Но когда целевых структур несколько, все они сложные и преобразование требуется «на лету», да еще и с обогащением данных, то, как говорится, «надо думать».
Перед командой была поставлена следующая задача: Напишите преобразователь данных из одной структуры в несколько других целевых структур.
При этом формат хранения исходных данных и данных назначения может быть совершенно произвольным.
Преобразование должно быть основано на правилах, иметь возможность многократного использования и редактирования.
В процессе преобразования некоторые данные необходимо перекодировать, например, преобразовать строку «#ff0000» в строку «red».
Кроме того, как известно, пользователь хочет иметь возможность читать и редактировать все интеграционные преобразования, т.е.
разработанный функционал должен отображаться на UI с возможностью редактирования.
Итак, давайте начнем.
Теоретически форматы ввода и вывода могут быть любого типа (csv, json и т. д.).
Для наглядности выберем формат XML. Пример источника XML — «конвертировать ИЗ»:
Пример назначения XML — «конвертировать в»:<Car> <Color>#ff0000</Color> <Length>5296 cm<Length> <Width>1848 cm</Width> <Price>31000 USD</Price> </Car>
<Vehicle>
<Body>
<Exterior>
<Color>red</Color>
</Exterior>
<Size>
<MeasureUnit>ft</measureUnit>
<Length>17.3753</Length>
<Width>6.0630</Width>
</Size>
</Body>
<Msrp>
<Currency>RUB</Currency>
<Value>1600000</Value>
</Msrp>
</Vehicle>
Как видите, по ходу дела возникают пересчеты и преобразования, меняется не только расположение значений в структуре, но и их типы, появляются расчетные значения, а исходные данные не содержат всех необходимых данных для успешного выполнения.
конвертация в конечный формат (требуется обогащение).
Я перечислю несколько:
- Цвет автомобиля Car.Color в источнике отображается как RBG-код «#ff0000», а в целевом объекте его нужно перекодировать в словесную интерпретацию «красного» в теге Vehicle.Body.Exterior.Color. ;
- Длину автомобиля Car.Lenght нужно разобрать на несколько составляющих, величину измерения и единицу измерения и преобразовать в американские футы, полученное значение поместить в Vehicle.Size.Length;
- Цену автомобиля Car.Price также необходимо разобрать на составляющие, пересчитать по курсу ЦБ в рублях на дату перерасчета и занести в Vehicle.Msrp.
Выбор контейнера для доступа к данным
Мы не можем напрямую работать с форматом XML, потому что.во-первых, это текст, а во-вторых, есть требование не быть привязанным к формату.
В этом случае логично работать с объектами-контейнерами в памяти компьютера, которые будут иметь удобный интерфейс доступа к своим данным и иметь структурный тип для обращения к его частям.
Лучше всего для этого подходят обычные классы C#, структура которых точно соответствует сохраняемым данным.
Создание этого класса значительно упрощается, если XML набран и доступна схема XSD. С помощью утилит можно автоматически собрать класс и без дополнительных усилий использовать его в своем коде.
Ниже приведены классы для наших структур.
Исходный класс контейнера C#: public class Car
{
public string Color;
public string Length;
public string Width;
public string Price;
}
Целевой класс контейнера C#: public class Vehicle {
public Body Body;
public Msrp Msrp;
}
public class Body
{
public Exterior Exterior;
public Size Size;
}
public class Msrp
{
public string Currency;
public decimal Value;
}
public class Exterior
{
public string Color;
}
public class Size
{
public string MeasureUnit;
public decimal Length;
public decimal Width;
}
Загрузка исходных данных в контейнер
В .Net Framework есть готовые компоненты, выполняющие десериализацию XML-данных, с помощью которых мы получаем экземпляр класса, автоматически заполняемый исходными данными.
Если файл более конкретного формата, то написать собственную библиотеку загрузки данных не составит труда.
Доступ к данным контейнера
Первое, что нам нужно изучить, — это иметь единый способ доступа к данным контейнера с произвольной структурой.Те.
нам нужен доступ к метаданным контейнера.
Это решается с помощью отражения .
Net. Мы можем добраться до любого свойства или поля класса и, зная тип и расположение данных, изменить их.
Чтобы напрямую указать структурный элемент (узел), воспользуемся аналогией XPath для XML. Например, чтобы указать в исходнике нужный нам узел, достаточно указать строку «Car.Color».
Правила преобразования данных из исходного контейнера в целевой контейнер
Итак, у нас есть два контейнера, каждый из которых имеет структурированную архитектуру.Теперь нам нужно научиться конвертировать одно в другое, из исходного контейнера в целевой контейнер.
Как сказано в постановке задачи, преобразование должно выполняться на основе набора правил.
Правила должны быть универсальными, чтобы их можно было использовать неоднократно.
В коде возникает следующая схема взаимодействия (см.
схему ниже): Данные сериализуются из XML в .
Net-объект (1-2), затем при обращении к данным контейнера (2) происходит преобразование на основе списка (3) правил в контейнер назначения (2-3-4).
Более того, правила имеют возможность обогащать данные (3-3’-3).
После инициализации контейнера назначения данные сбрасываются в окончательный формат (4-5).
Схема 1. Схема взаимодействия компонентов внутри преобразователя:
Теперь разработаем механизм преобразования с использованием правил.
С их помощью мы сможем описать любое преобразование.
Писать новый язык правил и затем реализовывать для него отдельный компилятор или интерпретатор явно не нужно.
Мы решили использовать обычный код на C#, который всегда можно скомпилировать и подключить к существующему функционалу.
Было разработано несколько интерфейсов и базовых классов C#.
Сам преобразователь: public interface IConverter
{
T Convert<T>(Object source, IDictionary<string, ConversionRule> rules) where T : class, new();
.
}
, где список правил — IDictionary , в котором строковые ключи — это пути к данным целевого контейнера, например "Vehicle.Msrp".
И правило преобразования: public abstract class ConversionRule
{
public abstract object GetValue(object source);
.
}
Задача конвертера — преобразовать указанный исходный объект source в новый объект типа T в соответствии со списком правил.
При преобразовании «источника» в «назначение» преобразователь выполняет следующие действия.
- Создает экземпляр типа T (тип целевого контейнера).
- Рекурсивно перебирает каждое поле и свойство целевого объекта типа T (контейнера назначения), ищет соответствующее ему правило преобразования и выполняет инициализацию (присвоение значения, рассчитанного на основе правила).
Если правило не найдено, но узел остается пустым.
Правило должно выполнить расчет и вернуть результирующее значение.
Как видно из примера, в правилах преобразования нет строгой типизации; объект может быть передан в качестве входных данных, и мы также получим объект в качестве выходных данных.
Рассмотрим пример правила, которое: получает цену автомобиля Car.Price, разбирает ее на составляющие, пересчитывает по курсу ЦБ в рублях (на дату перерасчета) и записывает значение в Vehicle.Msrp. » целевого контейнера.
Ниже представлена таблица для настройки правила конвертации:
Целевой узел в целевом объекте | Правило преобразования (класс в сборке) | Параметры правила преобразования |
---|---|---|
Транспортное средство.
Рекомендуемая розничная цена |
КонвертироватьStringPriceToMsrp | TargetCurrency = «РУБ», SourcePath = «Автомобиль.
Цена» |
public class ConvertStringPriceToMsrp: ConvertionRule
{
public string TargetCurrency;
public string SourcePath;
public override object GetValue(object source)
{
var targetObject = new Msrp();
targetObject.Currency = TargetCurrency;
targetObject.Value = SplitAndCalc(GetFiled(source(), SourcePath, TargetCurrency);
return targetObject;
}
.
}
Перед запуском правила оно инициализируется путем перебора его полей и свойств посредством отражения и заполнения одноименных значений TargetCurrency и SourcePath из конфига (набора параметров для конкретного экземпляра правила).
При обработке этого правила объект ConvertStringPriceToMsrp принимает значение поля в исходном контейнере Car.Price, разбивает строку на составляющие: цену и валюту, и создает результирующий объект Msrp, заполняя поля Msrp.Curreny = RUB и Msrp.Value. =[цена в рублях] поля.
Как видно из описания, правилу все равно необходимо обращаться к внешнему источнику данных, чтобы получить текущий курс рубля к доллару.
Те.
Правило преобразования может подключаться к любым внешним источникам данных и выполнять пополнение данных.
Выгрузка данных назначения из контейнера
Загрузка данных из целевого объекта в XML осуществляется аналогично готовой библиотеке .Net Framework путем сериализации объекта.
Компонент аккуратно свернет данные полей и свойств класса в XML-структуру.
Если файл назначения тоже конкретный, то для этого нам нужно написать адаптер, который наш целевой контейнер сохранит в нужном формате.
Рабочий прототип, преимущества и проблемы
Для автоматической загрузки справочных библиотек сервисов (для обогащения данных, для многоразовых справочников) мы реализовали IoC Autofac. Таким образом, при преобразовании большого количества однородных данных мы решили проблему лишней нагрузки на ввод-вывод и ускорили обработку.Преобразование в целевой объект происходит за один проход без лишних циклов.
Благодаря рекурсивности есть возможность подставлять значение узла по желанию «на выбор».
Эта опция очень полезна для XML, когда структура одного тега зависит от другого (например, от типа товара заполняются разные теги — мы это активно используем при генерации XML в API Amazon).
В то же время вся работа с метаданными основана на рефлексии, и на горизонте возникает потенциальная проблема скорости.
Проблема проявится тогда, когда задержки вычислений отражения будут доминировать над нами при быстрых вычислениях внутри правил конвертера.
На данный момент такая проблема еще не заявила о себе.
Но если он все-таки появится, есть идея кэшировать типы контейнеров назначения во время пакетной обработки.
Мы вынесли все настройки правил в веб-интерфейс, чтобы пользователи могли быстро менять настройки.
Настройки конвертации изначально хранились в XML, но для удобства редактирования их решили перенести в базу данных.
При всех преимуществах и недостатках мы все же получили желанный «Универсальный преобразователь данных на платформе .
Net Framework».
Сейчас он активно работает над модулями публикации товаров на Amazon, Wallmart и других маркетплейсах, где требуется постоянное картографирование, преобразование и обогащение данных.
Теги: #конвертер данных #преобразователь данных #правила преобразования #правила преобразования #торговые площадки #amazon #amazon #Walmart #торговая площадка #программирование #.
NET
-
Красители И Окрашивание
19 Oct, 24 -
Вирусная Стеганография
19 Oct, 24 -
Live Onecare Виновата В Пропаже Писем
19 Oct, 24 -
Рисование Анимированной Сцены С Помощью Css
19 Oct, 24