В одном из проектов возникла необходимость перевести процессы импорта данных сторонних систем на микросервисную архитектуру. В качестве инструмента выбран Apache NiFi. В качестве первого подопытного выбран импорт ЕГРЮЛ ФНС.
В предыдущей статье было описано, как получить файлы XML с данными ЕГРЮЛ, которые требуется импортировать.
В данной статье описан способ преобразования XML в JSON.
Используемые процессоры и контроллеры
Для преобразования XML в JSON используется процессор ConvertRecord. В котором используются два контроллера: FnsEgrulXmlReader типа XMLReader для чтения данных из XML и FnsEgrulJsonWriter типа JsonRecordSetWriter для записи этих данных в JSON.
Для работы контроллерам XMLReader и JsonRecordSetWriter требуются сведения о структуре читаемых и записываемых данных, т.е. схема данных. Я использовал схему AVRO. Для ее хранения в NiFi используется контроллер AvroSchemaRegistry. В нем задается имя схемы в поле Property и ее содержимое в поле Value
AVRO схема должна описывать структуру данных, соответствующую XSD-схеме, опубликованной на сайте ФНС. Не обязательно описывать всю структуру. Достаточно лишь в части тех данных, которые требуется импортировать.
<?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>
[ {
"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
}
} ]
{ "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 спецификации — в следующей статье.