Импорт ЕГРЮЛ ФНС средствами 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/


Интересные статьи

Интересные статьи

Введение Во время работы над задачами машинного обучения с онлайн-данными есть необходимость собирать различные сущности в одну для дальнейшего анализа и оценки. Процесс сбора должен быт...
Не так давно мы рассказывали о том, что Китай активно занимается развитием собственной инфраструктуры производства чипов, чтобы меньше зависеть от других стран. Поднебесная хочет об...
GeoJSON — это стандартизованный формат представления географических структур данных, основанный на JSON. Существует множество замечательных инструментов для визуализации GeoJSON-данных. При этом ...
Сериализация и десериализация — типичные операции, к которым современный разработчик относится как к тривиальным. Мы общаемся с базами данных, формируем HTTP-запросы, получаем данные через RE...
Всем привет! Сегодня мы разберем очень короткий, но полезный лайфхак о том, что нужно сделать, чтобы не вводить, к примеру, «import pandas as pd» по 10 раз в день. Для этого нужно: Перейт...