Импорт ЕГРЮЛ ФНС средствами Apache NiFi. Шаг 2 — преобразование XML в JSON

от автора

В одном из проектов возникла необходимость перевести процессы импорта данных сторонних систем на микросервисную архитектуру. В качестве инструмента выбран Apache NiFi. В качестве первого подопытного выбран импорт ЕГРЮЛ ФНС.

В предыдущей статье было описано, как получить файлы XML с данными ЕГРЮЛ, которые требуется импортировать.

В данной статье описан способ преобразования XML в JSON.

Используемые процессоры и контроллеры

Для преобразования XML в JSON используется процессор ConvertRecord. В котором используются два контроллера: FnsEgrulXmlReader типа XMLReader для чтения данных из XML и FnsEgrulJsonWriter типа JsonRecordSetWriter для записи этих данных в JSON.

Для работы контроллерам XMLReader и JsonRecordSetWriter требуются сведения о структуре читаемых и записываемых данных, т.е. схема данных. Я использовал схему AVRO. Для ее хранения в NiFi используется контроллер AvroSchemaRegistry. В нем задается имя схемы в поле Property и ее содержимое в поле Value

AVRO схема должна описывать структуру данных, соответствующую XSD-схеме, опубликованной на сайте ФНС. Не обязательно описывать всю структуру. Достаточно лишь в части тех данных, которые требуется импортировать.

Пример исходного XML

<?xml version="1.0" encoding="windows-1251" ?><EGRUL ДатаВыг="2020-05-20"><СвЮЛ ДатаВып="2020-05-20" ОГРН="1234567890123" ДатаОГРН="2002-12-30" ИНН="1234567890" КПП="123456789" СпрОПФ="ОКОПФ" КодОПФ="12300" ПолнНаимОПФ="Общества с ограниченной ответственностью">   <СвНаимЮЛ НаимЮЛПолн="ОБЩЕСТВО С ОГРАНИЧЕННОЙ ОТВЕТСТВЕННОСТЬЮ" НаимЮЛСокр="ООО">     <ГРНДата ГРН="1234567890123" ДатаЗаписи="2002-12-30" />   </СвНаимЮЛ>   <СвАдресЮЛ>     <АдресРФ Индекс="143500" КодРегион="50" КодАдрКладр="500000570000011">       <Регион ТипРегион="ОБЛАСТЬ" НаимРегион="МОСКОВСКАЯ" />       <Город ТипГород="ГОРОД" НаимГород="ИСТРА" />       <Улица ТипУлица="ПЕРЕУЛОК" НаимУлица="ВОЛОКОЛАМСКИЙ" />       <ГРНДата ГРН="1234567890123" ДатаЗаписи="2016-02-22" />       <ГРНДатаИспр ГРН="1234567890123" ДатаЗаписи="2019-03-08" />     </АдресРФ>   </СвАдресЮЛ>   <СвОбрЮЛ ОГРН="1234567890123" ДатаОГРН="2002-12-30" РегНом="12:12:12345" ДатаРег="1997-12-24" НаимРО="Московская областная регистрационная палата">     <СпОбрЮЛ КодСпОбрЮЛ="01" НаимСпОбрЮЛ="Создание юридического лица до 01.07.2002" />     <ГРНДата ГРН="1234567890123" ДатаЗаписи="2002-12-30" />   </СвОбрЮЛ>   <СвРегОрг КодНО="5081" НаимНО="Межрайонная инспекция Федеральной налоговой службы" АдрРО="144000,РОССИЯ,МОСКОВСКАЯ ОБЛ,,ЭЛЕКТРОСТАЛЬ Г,,СОВЕТСКАЯ УЛ,26А,,">     <ГРНДата ГРН="1234567890123" ДатаЗаписи="2019-01-31" />   </СвРегОрг>   <СвСтатус>     <СвСтатус КодСтатусЮЛ="105" НаимСтатусЮЛ="Регистрирующим органом принято решение о предстоящем исключении юридического лица из ЕГРЮЛ (недействующее юридическое лицо)" />     <СвРешИсклЮЛ ДатаРеш="2020-05-18" НомерРеш="12345" ДатаПубликации="2020-05-20" НомерЖурнала="1" />     <ГРНДата ГРН="1234567890123" ДатаЗаписи="2020-05-20" />   </СвСтатус>   <СвУчетНО ИНН="1234567890" КПП="123456789" ДатаПостУч="1998-01-20">     <СвНО КодНО="5017" НаимНО="Инспекция Федеральной налоговой службы" />     <ГРНДата ГРН="1234567890123" ДатаЗаписи="2007-11-01" />   </СвУчетНО>   <СвРегПФ РегНомПФ="123456789012" ДатаРег="1998-01-15">     <СвОргПФ КодПФ="060010" НаимПФ="Государственное учреждение - Управление Пенсионного фонда РФ" />     <ГРНДата ГРН="1234567890123" ДатаЗаписи="2006-05-05" />   </СвРегПФ>   <СвРегФСС РегНомФСС="123456789012345" ДатаРег="1998-01-15">     <СвОргФСС КодФСС="5023" НаимФСС="Филиал №23 Государственного учреждения - Московского областного регионального отделения Фонда социального страхования Российской Федерации" />     <ГРНДата ГРН="1234567890123" ДатаЗаписи="2016-11-01" />   </СвРегФСС>   <СведДолжнФЛ>     <ГРНДатаПерв ГРН="1234567890123" ДатаЗаписи="2005-07-20" />     <СвФЛ Фамилия="ИВАНОВ" Имя="ИВАН" Отчество="ИВАНОВИЧ" ИННФЛ="123456789012">       <ГРНДата ГРН="1234567890123" ДатаЗаписи="2020-03-18" />     </СвФЛ>     <СвДолжн ВидДолжн="02" НаимВидДолжн="Руководитель юридического лица" НаимДолжн="ГЕНЕРАЛЬНЫЙ ДИРЕКТОР">       <ГРНДата ГРН="1234567890123" ДатаЗаписи="2020-03-18" />     </СвДолжн>   </СведДолжнФЛ>   <СвУчредит>     <УчрФЛ>       <ГРНДатаПерв ГРН="1234567890123" ДатаЗаписи="2005-07-20" />       <СвФЛ Фамилия="ИВАНОВ" Имя="ИВАН" Отчество="ИВАНОВИЧ" ИННФЛ="123456789012">         <ГРНДата ГРН="1234567890123" ДатаЗаписи="2020-03-18" />       </СвФЛ>       <ДоляУстКап НоминСтоим="20000">         <РазмерДоли>           <Процент>50</Процент>         </РазмерДоли>         <ГРНДата ГРН="1234567890123" ДатаЗаписи="2005-07-20" />         <ГРНДатаИспр ГРН="1234567890123" ДатаЗаписи="2018-08-30" />       </ДоляУстКап>     </УчрФЛ> 	<УчрФЛ>       <ГРНДатаПерв ГРН="1234567890123" ДатаЗаписи="2005-07-20" />       <СвФЛ Фамилия="ПЕТРОВ" Имя="ПЕТР" Отчество="ПЕТРОВИЧ" ИННФЛ="123456789021">         <ГРНДата ГРН="1234567890123" ДатаЗаписи="2020-03-18" />       </СвФЛ>       <ДоляУстКап НоминСтоим="20000">         <РазмерДоли>           <Процент>50</Процент>         </РазмерДоли>         <ГРНДата ГРН="1234567890123" ДатаЗаписи="2005-07-20" />         <ГРНДатаИспр ГРН="1234567890123" ДатаЗаписи="2018-08-30" />       </ДоляУстКап>     </УчрФЛ>   </СвУчредит>   <СвОКВЭД>     <СвОКВЭДОсн КодОКВЭД="47.11" НаимОКВЭД="Торговля розничная преимущественно пищевыми продуктами, включая напитки, и табачными изделиями в неспециализированных магазинах" ПрВерсОКВЭД="2014">       <ГРНДата ГРН="1234567890123" ДатаЗаписи="2005-07-20" />       <ГРНДатаИспр ГРН="1234567890123" ДатаЗаписи="2018-08-30" />     </СвОКВЭДОсн>   </СвОКВЭД>   <СвЗапЕГРЮЛ ИдЗап="1234567890" ГРН="1234567890123" ДатаЗап="2002-12-30">     <ВидЗап КодСПВЗ="11101" НаимВидЗап="Внесение в Единый государственный реестр юридических лиц сведений о юридическом лице, зарегистрированном до 1 июля 2002 года" />     <СвРегОрг КодНО="5017" НаимНО="Инспекция МНС России по г.Истре Московской области" />     <СвСвид Серия="12" Номер="123456789" ДатаВыдСвид="2002-12-30" />   </СвЗапЕГРЮЛ>   <СвЗапЕГРЮЛ ИдЗап="1234567891" ГРН="1234567890123" ДатаЗап="2005-07-20">     <ВидЗап КодСПВЗ="12101" НаимВидЗап="Государственная регистрация изменений, внесенных в учредительные документы юридического лица, связанных с внесением изменений в сведения о юридическом лице, содержащиеся в Едином государственном реестре юридических лиц, на основании заявления" />     <СвРегОрг КодНО="5017" НаимНО="Инспекция Федеральной налоговой службы по г.Истре Московской области" />     <СведПредДок>       <НаимДок>ЗАЯВЛЕНИЕ О ГОСУДАРСТВЕННОЙ РЕГИСТРАЦИИ ИЗМЕНЕНИЙ, ВНОСИМЫХ В УЧРЕДИТЕЛЬНЫЕ ДОКУМЕНТЫ  ЮРИДИЧЕСКОГО ЛИЦА</НаимДок>       <НомДок>1</НомДок>       <ДатаДок>2005-07-14</ДатаДок>     </СведПредДок>     <СведПредДок>       <НаимДок>УСТАВ</НаимДок>       <НомДок>2</НомДок>       <ДатаДок>2005-07-14</ДатаДок>     </СведПредДок>     <СведПредДок>       <НаимДок>РЕШЕНИЕ</НаимДок>       <НомДок>3</НомДок>       <ДатаДок>2005-07-14</ДатаДок>     </СведПредДок>     <СведПредДок>       <НаимДок>КВИТАНЦИЯ</НаимДок>       <НомДок>4</НомДок>       <ДатаДок>2005-07-14</ДатаДок>     </СведПредДок>     <СвСвид Серия="12" Номер="123456789" ДатаВыдСвид="2005-07-20" />   </СвЗапЕГРЮЛ> </СвЮЛ></EGRUL> 

Пример получаемого JSON

[ {   "reportDate" : "2020-05-20",   "ogrn" : "1234567890123",   "ogrnDate" : "2002-12-30",   "inn" : "1234567890",   "kpp" : "123456789",   "opfCode" : "12300",   "opfName" : "Общества с ограниченной ответственностью",   "name" : {     "fullName" : "ОБЩЕСТВО С ОГРАНИЧЕННОЙ ОТВЕТСТВЕННОСТЬЮ",     "shortName" : "ООО"   },   "address" : {     "addressRF" : {       "region" : {         "type" : "ОБЛАСТЬ",         "name" : "МОСКОВСКАЯ"       },       "district" : null,       "town" : {         "type" : "ГОРОД",         "name" : "ИСТРА"       },       "settlement" : null,       "street" : {         "type" : "ПЕРЕУЛОК",         "name" : "ВОЛОКОЛАМСКИЙ"       },       "index" : "143500",       "regionCode" : "50",       "kladr" : "500000570000011",       "house" : null,       "building" : null,       "apartment" : null     }   },   "termination" : null,   "capital" : null,   "manageOrg" : null,   "director" : [ {     "fl" : {       "lastName" : "ИВАНОВ",       "firstName" : "ИВАН",       "patronymic" : "ИВАНОВИЧ",       "inn" : "123456789012"     },     "position" : {       "ogrnip" : null,       "typeCode" : "02",       "typeName" : "Руководитель юридического лица",       "name" : "ГЕНЕРАЛЬНЫЙ ДИРЕКТОР"     },     "disqualification" : null   } ],   "founders" : {     "founderULRF" : null,     "founderULForeign" : null,     "founderFL" : [ {       "fl" : {         "lastName" : "ИВАНОВ",         "firstName" : "ИВАН",         "patronymic" : "ИВАНОВИЧ",         "inn" : "123456789012"       },       "capitalPart" : {         "nominal" : 20000.0,         "size" : {           "percent" : 50.0,           "decimalPart" : null,           "simplePart" : null         }       }     }, {       "fl" : {         "lastName" : "ПЕТРОВ",         "firstName" : "ПЕТР",         "patronymic" : "ПЕТРОВИЧ",         "inn" : "123456789021"       },       "capitalPart" : {         "nominal" : 20000.0,         "size" : {           "percent" : 50.0,           "decimalPart" : null,           "simplePart" : null         }       }     } ],     "founderGov" : null,     "founderPIF" : null   },   "capitalPart" : null,   "holderReestrAO" : null,   "okved" : {     "mainOkved" : {       "code" : "47.11",       "name" : "Торговля розничная преимущественно пищевыми продуктами, включая напитки, и табачными изделиями в неспециализированных магазинах"     },     "addOkved" : null   } } ] 

AVRO схема

{ "type": "record", "name": "СвЮЛ",   "fields": [     { "name": "reportDate", "aliases": [ "ДатаВып" ], "type": "string" },     { "name": "ogrn", "aliases": [ "ОГРН" ], "type": "string" },     { "name": "ogrnDate", "aliases": [ "ДатаОГРН" ], "type": "string" },     { "name": "inn", "aliases": [ "ИНН" ], "type": "string" },     { "name": "kpp", "aliases": [ "КПП" ], "type": "string" },     { "name": "opfCode", "aliases": [ "КодОПФ" ], "type": "string" },     { "name": "opfName", "aliases": [ "ПолнНаимОПФ" ], "type": "string" },     { "name": "name", "aliases": [ "СвНаимЮЛ" ], "namespace": "СвЮЛ",       "type": { "type": "record", "name": "СвНаимЮЛ.СвЮЛ",         "fields": [           { "name": "fullName", "aliases": [ "НаимЮЛПолн" ], "type": "string" },           { "name": "shortName", "aliases": [ "НаимЮЛСокр" ], "type": "string" }         ]       }     },     { "name": "address", "aliases": [ "СвАдресЮЛ" ], "namespace": "СвЮЛ",       "type": { "type": "record", "name": "СвАдресЮЛ.СвЮЛ",         "fields": [           { "name": "addressRF", "aliases": [ "АдресРФ" ], "namespace": "СвАдресЮЛ.СвЮЛ",             "type": { "type": "record", "name": "АдресРФ.СвАдресЮЛ.СвЮЛ",               "fields": [                 { "name": "region", "aliases": [ "Регион" ], "namespace": "АдресРФ.СвАдресЮЛ.СвЮЛ",                   "type": { "type": "record", "name": "Регион.АдресРФ.СвАдресЮЛ.СвЮЛ",                     "fields": [                       { "name": "type", "aliases": [ "ТипРегион" ], "type": "string" },                       { "name": "name", "aliases": [ "НаимРегион" ], "type": "string" }                     ]                   }                 },                 { "name": "district", "aliases": [ "Район" ], "namespace": "АдресРФ.СвАдресЮЛ.СвЮЛ",                   "type": { "type": "record", "name": "Район.АдресРФ.СвАдресЮЛ.СвЮЛ",                     "fields": [                       { "name": "type", "aliases": [ "ТипРайон" ], "type": "string" },                       { "name": "name", "aliases": [ "НаимРайон" ], "type": "string" }                     ]                   }                 },                 { "name": "town", "aliases": [ "Город" ], "namespace": "АдресРФ.СвАдресЮЛ.СвЮЛ",                   "type": { "type": "record", "name": "Город.АдресРФ.СвАдресЮЛ.СвЮЛ",                     "fields": [                       { "name": "type", "aliases": [ "ТипГород" ], "type": "string" },                       { "name": "name", "aliases": [ "НаимГород" ], "type": "string" }                     ]                   }                 },                 { "name": "settlement", "aliases": [ "НаселПункт" ], "namespace": "АдресРФ.СвАдресЮЛ.СвЮЛ",                   "type": { "type": "record", "name": "НаселПункт.АдресРФ.СвАдресЮЛ.СвЮЛ",                     "fields": [                       { "name": "type", "aliases": [ "ТипНаселПункт" ], "type": "string" },                       { "name": "name", "aliases": [ "НаимНаселПункт" ], "type": "string" }                     ]                   }                 },                 { "name": "street", "aliases": [ "Улица" ], "namespace": "АдресРФ.СвАдресЮЛ.СвЮЛ",                   "type": { "type": "record", "name": "Улица.АдресРФ.СвАдресЮЛ.СвЮЛ",                     "fields": [                       { "name": "type", "aliases": [ "ТипУлица" ], "type": "string" },                       { "name": "name", "aliases": [ "НаимУлица" ], "type": "string" }                     ]                   }                 },                 { "name": "index", "aliases": [ "Индекс" ], "type": "string" },                 { "name": "regionCode", "aliases": [ "КодРегион" ], "type": "string" },                 { "name": "kladr", "aliases": [ "КодАдрКладр" ], "type": "string" },                 { "name": "house", "aliases": [ "Дом" ], "type": "string" },                 { "name": "building", "aliases": [ "Корпус" ], "type": "string" },                 { "name": "apartment", "aliases": [ "Кварт" ], "type": "string" }               ]             }           }         ]       }     },     { "name": "termination", "aliases": [ "СвПрекрЮЛ" ], "namespace": "СвЮЛ",       "type": { "type": "record", "name": "СвПрекрЮЛ.СвЮЛ",         "fields": [           { "name": "method", "aliases": [ "СпПрекрЮЛ" ], "namespace": "СвПрекрЮЛ.СвЮЛ",             "type": { "type": "record", "name": "СпПрекрЮЛ.СвПрекрЮЛ.СвЮЛ",               "fields": [                 { "name": "code", "aliases": [ "КодСпПрекрЮЛ" ], "type": "string" },                 { "name": "name", "aliases": [ "НаимСпПрекрЮЛ" ], "type": "string" }               ]             }           },           { "name": "date", "aliases": [ "ДатаПрекрЮЛ" ], "type": "string" }         ]       }     },     { "name": "capital", "aliases": [ "СвУстКап" ], "namespace": "СвЮЛ",       "type": { "type": "record", "name": "СвУстКап.СвЮЛ",         "fields": [           { "name": "type", "aliases": [ "НаимВидКап" ], "type": "string" },           { "name": "amount", "aliases": [ "СумКап" ], "type": "double" },           { "name": "partRUR", "aliases": [ "ДоляРубля" ], "namespace": "СвУстКап.СвЮЛ",             "type": { "type": "record", "name": "ДоляРубля.СвУстКап.СвЮЛ",               "fields": [                 { "name": "num", "aliases": [ "Числит" ], "type": "long" },                 { "name": "denom", "aliases": [ "Знаменат" ], "type": "long" }               ]             }           }         ]       }     },     { "name": "manageOrg", "aliases": [ "СвУпрОрг" ], "namespace": "СвЮЛ",       "type": { "type": "record", "name": "СвУпрОрг.СвЮЛ",         "fields": [           { "name": "egrulData", "aliases": [ "НаимИННЮЛ" ], "namespace": "СвУпрОрг.СвЮЛ",             "type": { "type": "record", "name": "НаимИННЮЛ.СвУпрОрг.СвЮЛ",               "fields": [                 { "name": "ogrn", "aliases": [ "ОГРН" ], "type": "string" },                 { "name": "inn", "aliases": [ "ИНН" ], "type": "string" },                 { "name": "fullName", "aliases": [ "НаимЮЛПолн" ], "type": "string" }               ]             }           }         ]       }     },     { "name": "director", "aliases": [ "СведДолжнФЛ" ], "namespace": "СвЮЛ",       "type": { "type": "array",         "items": [           { "name": "СведДолжнФЛ.СвЮЛ", "type": "record",             "fields": [               { "name": "fl", "aliases": [ "СвФЛ" ], "namespace": "СведДолжнФЛ.СвЮЛ",                 "type": { "type": "record", "name": "СвФЛ.СведДолжнФЛ.СвЮЛ",                   "fields": [                     { "name": "lastName", "aliases": [ "Фамилия" ], "type": "string" },                     { "name": "firstName", "aliases": [ "Имя" ], "type": "string" },                     { "name": "patronymic", "aliases": [ "Отчество" ], "type": "string" },                     { "name": "inn", "aliases": [ "ИННФЛ" ], "type": "string" }                   ]                 }               },               { "name": "position", "aliases": [ "СвДолжн" ], "namespace": "СведДолжнФЛ.СвЮЛ",                 "type": { "type": "record", "name": "СвДолжн.СведДолжнФЛ.СвЮЛ",                   "fields": [                     { "name": "ogrnip", "aliases": [ "ОГРНИП" ], "type": "string" },                     { "name": "typeCode", "aliases": [ "ВидДолжн" ], "type": "string" },                     { "name": "typeName", "aliases": [ "НаимВидДолжн" ], "type": "string" },                     { "name": "name", "aliases": [ "НаимДолжн" ], "type": "string" }                   ]                 }               },               { "name": "disqualification", "aliases": [ "СвДискв" ], "namespace": "СведДолжнФЛ.СвЮЛ",                 "type": { "type": "record", "name": "СвДискв.СведДолжнФЛ.СвЮЛ",                   "fields": [                     { "name": "startDate", "aliases": [ "ДатаНачДискв" ], "type": "string" },                     { "name": "endDate", "aliases": [ "ДатаОкончДискв" ], "type": "string" },                     { "name": "decisionDate", "aliases": [ "ДатаРеш" ], "type": "string" }                   ]                 }               }             ]           }         ]       }     },     { "name": "founders", "aliases": [ "СвУчредит" ], "namespace": "СвЮЛ",       "type": { "type": "record", "name": "СвУчредит.СвЮЛ",         "fields": [           { "name": "founderULRF", "aliases": [ "УчрЮЛРос" ], "namespace": "СвУчредит.СвЮЛ",             "type": { "type": "array",               "items": [                 { "name": "УчрЮЛРос.СвУчредит.СвЮЛ", "type": "record",                   "fields": [                     { "name": "egrulData", "aliases": [ "НаимИННЮЛ" ], "namespace": "УчрЮЛРос.СвУчредит.СвЮЛ",                       "type": { "type": "record", "name": "НаимИННЮЛ.УчрЮЛРос.СвУчредит.СвЮЛ",                         "fields": [                           { "name": "ogrn", "aliases": [ "ОГРН" ], "type": "string" },                           { "name": "inn", "aliases": [ "ИНН" ], "type": "string" },                           { "name": "fullName", "aliases": [ "НаимЮЛПолн" ], "type": "string" }                         ]                       }                     },                     { "name": "oldRegData", "aliases": [ "СвРегСтарые" ], "namespace": "УчрЮЛРос.СвУчредит.СвЮЛ",                       "type": { "type": "record", "name": "СвРегСтарые.УчрЮЛРос.СвУчредит.СвЮЛ",                         "fields": [                           { "name": "regNumber", "aliases": [ "РегНом" ], "type": "string" },                           { "name": "regDate", "aliases": [ "ДатаРег" ], "type": "string" },                           { "name": "regOrg", "aliases": [ "НаимРО" ], "type": "string" }                         ]                       }                     },                     { "name": "capitalPart", "aliases": [ "ДоляУстКап" ], "namespace": "УчрЮЛРос.СвУчредит.СвЮЛ",                       "type": { "type": "record", "name": "ДоляУстКап.УчрЮЛРос.СвУчредит.СвЮЛ",                         "fields": [                           { "name": "nominal", "aliases": [ "НоминСтоим" ], "type": "double" },                           { "name": "size", "aliases": [ "РазмерДоли" ], "namespace": "ДоляУстКап.УчрЮЛРос.СвУчредит.СвЮЛ",                             "type": { "type": "record", "name": "РазмерДоли.ДоляУстКап.УчрЮЛРос.СвУчредит.СвЮЛ",                               "fields": [                                 { "name": "percent", "aliases": [ "Процент" ], "type": "double" },                                 { "name": "decimalPart", "aliases": [ "ДробДесят" ], "type": "double" },                                 { "name": "simplePart", "aliases": [ "ДробПрост" ], "namespace": "РазмерДоли.ДоляУстКап.УчрЮЛРос.СвУчредит.СвЮЛ",                                   "type": { "type": "record", "name": "ДробПрост.РазмерДоли.ДоляУстКап.УчрЮЛРос.СвУчредит.СвЮЛ",                                     "fields": [                                       { "name": "num", "aliases": [ "Числит" ], "type": "long" },                                       { "name": "denom", "aliases": [ "Знаменат" ], "type": "long" }                                     ]                                   }                                 }                               ]                             }                           }                         ]                       }                     }                   ]                 }               ]             }           },           { "name": "founderULForeign", "aliases": [ "УчрЮЛИн" ], "namespace": "СвУчредит.СвЮЛ",             "type": { "type": "array",               "items": [                 { "name": "УчрЮЛИн.СвУчредит.СвЮЛ", "type": "record",                   "fields": [                     { "name": "egrulData", "aliases": [ "НаимИННЮЛ" ], "namespace": "УчрЮЛИн.СвУчредит.СвЮЛ",                       "type": { "type": "record", "name": "НаимИННЮЛ.УчрЮЛИн.СвУчредит.СвЮЛ",                         "fields": [                           { "name": "ogrn", "aliases": [ "ОГРН" ], "type": "string" },                           { "name": "inn", "aliases": [ "ИНН" ], "type": "string" },                           { "name": "fullName", "aliases": [ "НаимЮЛПолн" ], "type": "string" }                         ]                       }                     },                     { "name": "foreignReg", "aliases": [ "СвРегИн" ], "namespace": "УчрЮЛИн.СвУчредит.СвЮЛ",                       "type": { "type": "record", "name": "СвРегИн.УчрЮЛИн.СвУчредит.СвЮЛ",                         "fields": [                           { "name": "oksm", "aliases": [ "ОКСМ" ], "type": "string" },                           { "name": "country", "aliases": [ "НаимСтран" ], "type": "string" },                           { "name": "regDate", "aliases": [ "ДатаРег" ], "type": "string" },                           { "name": "regNumber", "aliases": [ "РегНомер" ], "type": "string" },                           { "name": "regOrg", "aliases": [ "НаимРегОрг" ], "type": "string" },                           { "name": "address", "aliases": [ "АдрСтр" ], "type": "string" }                         ]                       }                     },                     { "name": "capitalPart", "aliases": [ "ДоляУстКап" ], "namespace": "УчрЮЛИн.СвУчредит.СвЮЛ",                       "type": { "type": "record", "name": "ДоляУстКап.УчрЮЛИн.СвУчредит.СвЮЛ",                         "fields": [                           { "name": "nominal", "aliases": [ "НоминСтоим" ], "type": "double" },                           { "name": "size", "aliases": [ "РазмерДоли" ], "namespace": "ДоляУстКап.УчрЮЛИн.СвУчредит.СвЮЛ",                             "type": { "type": "record", "name": "РазмерДоли.ДоляУстКап.УчрЮЛИн.СвУчредит.СвЮЛ",                               "fields": [                                 { "name": "percent", "aliases": [ "Процент" ], "type": "double" },                                 { "name": "decimalPart", "aliases": [ "ДробДесят" ], "type": "double" },                                 { "name": "simplePart", "aliases": [ "ДробПрост" ], "namespace": "РазмерДоли.ДоляУстКап.УчрЮЛИн.СвУчредит.СвЮЛ",                                   "type": { "type": "record", "name": "ДробПрост.РазмерДоли.ДоляУстКап.УчрЮЛИн.СвУчредит.СвЮЛ",                                     "fields": [                                       { "name": "num", "aliases": [ "Числит" ], "type": "long" },                                       { "name": "denom", "aliases": [ "Знаменат" ], "type": "long" }                                     ]                                   }                                 }                               ]                             }                           }                         ]                       }                     }                   ]                 }               ]             }           },           { "name": "founderFL", "aliases": [ "УчрФЛ" ], "namespace": "СвУчредит.СвЮЛ",             "type": { "type": "array",               "items": [                 { "name": "УчрФЛ.СвУчредит.СвЮЛ", "type": "record",                   "fields": [                     { "name": "fl", "aliases": [ "СвФЛ" ], "namespace": "УчрФЛ.СвУчредит.СвЮЛ",                       "type": { "type": "record", "name": "СвФЛ.УчрФЛ.СвУчредит.СвЮЛ",                         "fields": [                           { "name": "lastName", "aliases": [ "Фамилия" ], "type": "string" },                           { "name": "firstName", "aliases": [ "Имя" ], "type": "string" },                           { "name": "patronymic", "aliases": [ "Отчество" ], "type": "string" },                           { "name": "inn", "aliases": [ "ИННФЛ" ], "type": "string" }                         ]                       }                     },                     { "name": "capitalPart", "aliases": [ "ДоляУстКап" ], "namespace": "УчрФЛ.СвУчредит.СвЮЛ",                       "type": { "type": "record", "name": "ДоляУстКап.УчрФЛ.СвУчредит.СвЮЛ",                         "fields": [                           { "name": "nominal", "aliases": [ "НоминСтоим" ], "type": "double" },                           { "name": "size", "aliases": [ "РазмерДоли" ], "namespace": "ДоляУстКап.УчрФЛ.СвУчредит.СвЮЛ",                             "type": { "type": "record", "name": "РазмерДоли.ДоляУстКап.УчрФЛ.СвУчредит.СвЮЛ",                               "fields": [                                 { "name": "percent", "aliases": [ "Процент" ], "type": "double" },                                 { "name": "decimalPart", "aliases": [ "ДробДесят" ], "type": "double" },                                 { "name": "simplePart", "aliases": [ "ДробПрост" ], "namespace": "РазмерДоли.ДоляУстКап.УчрФЛ.СвУчредит.СвЮЛ",                                   "type": { "type": "record", "name": "ДробПрост.РазмерДоли.ДоляУстКап.УчрФЛ.СвУчредит.СвЮЛ",                                     "fields": [                                       { "name": "num", "aliases": [ "Числит" ], "type": "long" },                                       { "name": "denom", "aliases": [ "Знаменат" ], "type": "long" }                                     ]                                   }                                 }                               ]                             }                           }                         ]                       }                     }                   ]                 }               ]             }           },           { "name": "founderGov", "aliases": [ "УчрРФСубМО" ], "namespace": "СвУчредит.СвЮЛ",             "type": { "type": "array",               "items": [                 { "name": "УчрРФСубМО.СвУчредит.СвЮЛ", "type": "record",                   "fields": [                     { "name": "govOrg", "aliases": [ "ВидНаимУчр" ], "namespace": "УчрРФСубМО.СвУчредит.СвЮЛ",                       "type": { "type": "record", "name": "ВидНаимУчр.УчрРФСубМО.СвУчредит.СвЮЛ",                         "fields": [                           { "name": "code", "aliases": [ "КодУчрРФСубМО" ], "type": "string" },                           { "name": "name", "aliases": [ "НаимМО" ], "type": "string" },                           { "name": "regionCode", "aliases": [ "КодРегион" ], "type": "string" },                           { "name": "regionName", "aliases": [ "НаимРегион" ], "type": "string" }                         ]                       }                     },                     { "name": "capitalPart", "aliases": [ "ДоляУстКап" ], "namespace": "УчрРФСубМО.СвУчредит.СвЮЛ",                       "type": { "type": "record", "name": "ДоляУстКап.УчрРФСубМО.СвУчредит.СвЮЛ",                         "fields": [                           { "name": "nominal", "aliases": [ "НоминСтоим" ], "type": "double" },                           { "name": "size", "aliases": [ "РазмерДоли" ], "namespace": "ДоляУстКап.УчрРФСубМО.СвУчредит.СвЮЛ",                             "type": { "type": "record", "name": "РазмерДоли.ДоляУстКап.УчрРФСубМО.СвУчредит.СвЮЛ",                               "fields": [                                 { "name": "percent", "aliases": [ "Процент" ], "type": "double" },                                 { "name": "decimalPart", "aliases": [ "ДробДесят" ], "type": "double" },                                 { "name": "simplePart", "aliases": [ "ДробПрост" ], "namespace": "РазмерДоли.ДоляУстКап.УчрРФСубМО.СвУчредит.СвЮЛ",                                   "type": { "type": "record", "name": "ДробПрост.РазмерДоли.ДоляУстКап.УчрРФСубМО.СвУчредит.СвЮЛ",                                     "fields": [                                       { "name": "num", "aliases": [ "Числит" ], "type": "long" },                                       { "name": "denom", "aliases": [ "Знаменат" ], "type": "long" }                                     ]                                   }                                 }                               ]                             }                           }                         ]                       }                     },                     { "name": "founderImplUL", "aliases": [ "СвОргОсущПр" ], "namespace": "УчрРФСубМО.СвУчредит.СвЮЛ",                       "type": { "type": "array",                         "items": [                           { "name": "СвОргОсущПр.УчрРФСубМО.СвУчредит.СвЮЛ", "type": "record",                             "fields": [                               { "name": "egrulData", "aliases": [ "НаимИННЮЛ" ], "namespace": "СвОргОсущПр.УчрРФСубМО.СвУчредит.СвЮЛ",                                 "type": { "type": "record", "name": "НаимИННЮЛ.СвОргОсущПр.УчрРФСубМО.СвУчредит.СвЮЛ",                                   "fields": [                                     { "name": "ogrn", "aliases": [ "ОГРН" ], "type": "string" },                                     { "name": "inn", "aliases": [ "ИНН" ], "type": "string" },                                     { "name": "fullName", "aliases": [ "НаимЮЛПолн" ], "type": "string" }                                   ]                                 }                               }                             ]                           }                         ]                       }                     },                     { "name": "founderImplFL", "aliases": [ "СвФЛОсущПр" ], "namespace": "УчрРФСубМО.СвУчредит.СвЮЛ",                       "type": { "type": "array",                         "items": [                           { "name": "СвФЛОсущПр.УчрРФСубМО.СвУчредит.СвЮЛ", "type": "record",                             "fields": [                               { "name": "fl", "aliases": [ "СвФЛ" ], "namespace": "СвФЛОсущПр.УчрРФСубМО.СвУчредит.СвЮЛ",                                 "type": { "type": "record", "name": "СвФЛ.СвФЛОсущПр.УчрРФСубМО.СвУчредит.СвЮЛ",                                   "fields": [                                     { "name": "lastName", "aliases": [ "Фамилия" ], "type": "string" },                                     { "name": "firstName", "aliases": [ "Имя" ], "type": "string" },                                     { "name": "patronymic", "aliases": [ "Отчество" ], "type": "string" },                                     { "name": "inn", "aliases": [ "ИННФЛ" ], "type": "string" }                                   ]                                 }                               }                             ]                           }                         ]                       }                     }                   ]                 }               ]             }           },           { "name": "founderPIF", "aliases": [ "УчрПИФ" ], "namespace": "СвУчредит.СвЮЛ",             "type": { "type": "array",               "items": [                 { "name": "УчрПИФ.СвУчредит.СвЮЛ", "type": "record",                   "fields": [                     { "name": "PIFName", "aliases": [ "СвНаимПИФ" ], "namespace": "УчрПИФ.СвУчредит.СвЮЛ",                       "type": { "type": "record", "name": "СвНаимПИФ.УчрПИФ.СвУчредит.СвЮЛ",                         "fields": [                           { "name": "name", "aliases": [ "НаимПИФ" ], "type": "string" }                         ]                       }                     },                     { "name": "manageOrg", "aliases": [ "СвУпрКомпПИФ" ], "namespace": "УчрПИФ.СвУчредит.СвЮЛ",                       "type": { "type": "record", "name": "СвУпрКомпПИФ.УчрПИФ.СвУчредит.СвЮЛ",                         "fields": [                           { "name": "egrulData", "aliases": [ "УпрКомпПиф" ], "namespace": "УчрПИФ.СвУчредит.СвЮЛ",                             "type": { "type": "record", "name": "УпрКомпПиф.УчрПИФ.СвУчредит.СвЮЛ",                               "fields": [                                 { "name": "ogrn", "aliases": [ "ОГРН" ], "type": "string" },                                 { "name": "inn", "aliases": [ "ИНН" ], "type": "string" },                                 { "name": "fullName", "aliases": [ "НаимЮЛПолн" ], "type": "string" }                               ]                             }                           }                         ]                       }                     },                     { "name": "capitalPart", "aliases": [ "ДоляУстКап" ], "namespace": "УчрПИФ.СвУчредит.СвЮЛ",                       "type": { "type": "record", "name": "ДоляУстКап.УчрПИФ.СвУчредит.СвЮЛ",                         "fields": [                           { "name": "nominal", "aliases": [ "НоминСтоим" ], "type": "double" },                           { "name": "size", "aliases": [ "РазмерДоли" ], "namespace": "ДоляУстКап.УчрПИФ.СвУчредит.СвЮЛ",                             "type": { "type": "record", "name": "РазмерДоли.ДоляУстКап.УчрПИФ.СвУчредит.СвЮЛ",                               "fields": [                                 { "name": "percent", "aliases": [ "Процент" ], "type": "double" },                                 { "name": "decimalPart", "aliases": [ "ДробДесят" ], "type": "double" },                                 { "name": "simplePart", "aliases": [ "ДробПрост" ], "namespace": "РазмерДоли.ДоляУстКап.УчрПИФ.СвУчредит.СвЮЛ",                                   "type": { "type": "record", "name": "ДробПрост.РазмерДоли.ДоляУстКап.УчрПИФ.СвУчредит.СвЮЛ",                                     "fields": [                                       { "name": "num", "aliases": [ "Числит" ], "type": "long" },                                       { "name": "denom", "aliases": [ "Знаменат" ], "type": "long" }                                     ]                                   }                                 }                               ]                             }                           }                         ]                       }                     }                   ]                 }               ]             }           }         ]       }     },     { "name": "capitalPart", "aliases": [ "СвДоляООО" ], "namespace": "СвЮЛ",       "type": { "type": "record", "name": "СвДоляООО.СвЮЛ",         "fields": [           { "name": "nominal", "aliases": [ "НоминСтоим" ], "type": "double" },           { "name": "size", "aliases": [ "РазмерДоли" ], "namespace": "СвДоляООО.СвЮЛ",             "type": { "type": "record", "name": "РазмерДоли.СвДоляООО.СвЮЛ",               "fields": [                 { "name": "percent", "aliases": [ "Процент" ], "type": "double" },                 { "name": "decimalPart", "aliases": [ "ДробДесят" ], "type": "double" },                 { "name": "simplePart", "aliases": [ "ДробПрост" ], "namespace": "РазмерДоли.СвДоляООО.СвЮЛ",                   "type": { "type": "record", "name": "ДробПрост.РазмерДоли.СвДоляООО.СвЮЛ",                     "fields": [                       { "name": "num", "aliases": [ "Числит" ], "type": "long" },                       { "name": "denom", "aliases": [ "Знаменат" ], "type": "long" }                     ]                   }                 }               ]             }           }         ]       }     },     { "name": "holderReestrAO", "aliases": [ "СвДержРеестрАО" ], "namespace": "СвЮЛ",       "type": { "type": "record", "name": "СвДержРеестрАО.СвЮЛ",         "fields": [           { "name": "egrulData", "aliases": [ "ДержРеестрАО" ], "namespace": "СвДержРеестрАО.СвЮЛ",             "type": { "type": "record", "name": "ДержРеестрАО.СвДержРеестрАО.СвЮЛ",               "fields": [                 { "name": "ogrn", "aliases": [ "ОГРН" ], "type": "string" },                 { "name": "inn", "aliases": [ "ИНН" ], "type": "string" },                 { "name": "fullName", "aliases": [ "НаимЮЛПолн" ], "type": "string" }               ]             }           }         ]       }     },     { "name": "okved", "aliases": [ "СвОКВЭД" ], "namespace": "СвЮЛ",       "type": { "type": "record", "name": "СвОКВЭД.СвЮЛ",         "fields": [           { "name": "mainOkved", "aliases": [ "СвОКВЭДОсн" ], "namespace": "СвОКВЭД.СвЮЛ",             "type": { "type": "record", "name": "СвОКВЭДОсн.СвОКВЭД.СвЮЛ",               "fields": [                 { "name": "code", "aliases": [ "КодОКВЭД" ], "type": "string" },                 { "name": "name", "aliases": [ "НаимОКВЭД" ], "type": "string" }               ]             }           },           { "name": "addOkved", "aliases": [ "СвОКВЭДДоп" ], "namespace": "СвОКВЭД.СвЮЛ",             "type": { "type": "array",               "items": [                 { "name": "СвОКВЭДДоп.СвОКВЭД.СвЮЛ", "type": "record",                   "fields": [                     { "name": "code", "aliases": [ "КодОКВЭД" ], "type": "string" },                     { "name": "name", "aliases": [ "НаимОКВЭД" ], "type": "string" }                   ]                 }               ]             }           }         ]       }     }   ] }

Разработка схемы AVRO

Схема должна начинаться с того элемента, содержимое которого должно попасть в JSON. Но не сам этот элемент. В данном случае — это «СвЮЛ».

Если в исходном XML этот элемент встречается несколько раз, то результирующий JSON будет представлять массив. Каждый элемент этого массива соответствует содержимому корневого элемента схемы AVRO

Необходимо указать тип элемента — record, и описать составляющие его элементы в блоке fields.

{ "type": "record", "name": "СвЮЛ",   "fields": []}

Примитивные элементы

Примитивные элементы — это атрибуты элемента и вложенные элементы без атрибутов. Для них указывается наименование name, псевдоним aliases и тип type.

{ "name": "reportDate", "aliases": [ "ДатаВып" ], "type": "string" }

Наименование элемента указывает его имя в результирующем JSON. Наименование долно состоять только из букв. Псевдоним — его имя в исходном XML. Псевдоним можно не использовать, тогда имена элементов в XML и JSON будут совпадать. Тип элемента в схеме AVRO может быть примитивным или логическим. Однако с ходу добиться работы логических типов в NiFi мне не удалось. Валидатор не пропускал такую схему.

Сложные элементы

Сложные элементы описываются типом record. Описание типа должно быть вложенным.

{ "name": "name", "aliases": [ "СвНаимЮЛ" ], "namespace": "СвЮЛ",   "type": { "type": "record", "name": "СвНаимЮЛ.СвЮЛ",     "fields": [       { "name": "fullName", "aliases": [ "НаимЮЛПолн" ], "type": "string" },       { "name": "shortName", "aliases": [ "НаимЮЛСокр" ], "type": "string" }     ]   } }

В описании сложного элемента добавляется пространство имен namespace. Оно должно содержать цепочку наименовании элементов, в которые вложен описываемый элемент, в обратной последовательности через точку.

В описании типа указывается его имя name. Я в качестве имени типа использовал, скажем так, пространство имен. Этим обеспечивается уникальность наименования типа.

Блок fields содержит описание атрибутов и вложенных элементов. Они в свою очередь могут быть как примитивного так и составного типа.

Массивы

Массивы описываются типом array. Описание типа должно быть вложенным. Блок items должен содержать описание элемента массива. В name указывается путь к элементу XML, содержащему элемент массива.

{ "name": "addOkved", "aliases": [ "СвОКВЭДДоп" ], "namespace": "СвОКВЭД.СвЮЛ",   "type": { "type": "array",     "items": [       { "name": "СвОКВЭДДоп.СвОКВЭД.СвЮЛ", "type": "record",         "fields": [           { "name": "code", "aliases": [ "КодОКВЭД" ], "type": "string" },           { "name": "name", "aliases": [ "НаимОКВЭД" ], "type": "string" }         ]       }     ]   } } 

Стоит обратить внимание, что здесь описание типа record не должно быть вложенным.

Настройка процессоров NiFi

В остальном схема AVRO представляет собой различные комбинации примитивных и сложных элементов. Схему необходимо зарегистрировать в контроллере AvroSchemaRegistry.

В процессоре ConvertRecord необходимо указать контроллеры для чтения данных из XML и записи данных в JSON.

Настройки XMLReader

  • Schema Access Strategy = Use ‘Schema Name’ Property — поиск схемы по наименованию
  • Schema Registry — контроллер AvroSchemaRegistry, в котором зарегистрирована схема AVRO
  • Schema Name — атрибут в FlowFile, который содержит наименование схемы. Я использовал атрибут scheme.name. Установка атрибута в FlowFile выполняется процессором UpdateAttribute. Хотя можно использовать и другой атрибут, который присутствует в FlowFile и позволяет точно выбрать схему AVRO
  • Expect Records as Array = true — в XML ожидается массив искомых элементов
  • Field Name for Content = EGRUL — имя элемента в XML, внутри которого содержится структура описываемая в схеме AVRO

Настройки JsonRecordSetWriter

  • Schema Write Strategy — записывать ли схему AVRO в FlowFile
  • Schema Access Strategy = Use ‘Schema Name’ Property — поиск схемы по наименованию
  • Schema Registry — контроллер AvroSchemaRegistry, в котором зарегистрирована схема AVRO
  • Schema Name — атрибут в FlowFile, который содержит наименование схемы
  • Pretty Print JSON — выполнять ли форматирование JSON
  • Suppress Null Values — что делать с пустыми элементами
  • Output Grouping — способ группировки записей в JSON

На выходе процессор ConvertRecord создаст FlowFile, который содержит результирующий JSON.

Далее…

Далее каждый JSON можно разбить по организациям и, навести красоту — убрать избыточные уровни иерархии, объединить некоторые поля (например ФИО, поля адреса).

О преобразовании JSON с использованием JOLT спецификации — в следующей статье.

ссылка на оригинал статьи https://habr.com/ru/post/503122/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *