Импорт ЕГРЮЛ ФНС средствами 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 спецификации — в следующей статье.

    Средняя зарплата в IT

    113 000 ₽/мес.
    Средняя зарплата по всем IT-специализациям на основании 10 037 анкет, за 2-ое пол. 2020 года Узнать свою зарплату
    Реклама
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее

    Комментарии 1

      0

      Спасибо за статью. Очень надеюсь, что следующая часть будет не через полгода, тема jolt трансформаций была бы очень интересна! :)

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое