Error while decoding the request сбис

Znuny and ((OTRS)) Community Edition OTRS 5 TicketUpdate via WebServices JSON : Error while decoding request content.. Moderator: crythias OTRS 5 TicketUpdate via WebServices JSON : Error while decoding request content.. Post by LXsysadmin » 25 Mar 2019, 14:51 We are trying to update multiple tickets via OTRS 5 web services using JSON. All […]

Содержание

  1. Znuny and ((OTRS)) Community Edition
  2. OTRS 5 TicketUpdate via WebServices JSON : Error while decoding request content..
  3. OTRS 5 TicketUpdate via WebServices JSON : Error while decoding request content..
  4. Re: OTRS 5 TicketUpdate via WebServices JSON : Error while decoding request content..
  5. Обработать ошибки в API СБИС
  6. Общие рекомендации для обработки ошибок
  7. Появилась HTTP-ошибка 401 «Not authorized»
  8. Причина №1. Не указан идентификатор сессии (X-SBISSessionID) в HTTP-заголовке запроса
  9. Причина №2. Идентификатор сессии (X-SBISSessionID) в HTTP-заголовке запроса устарел
  10. Причина №3. В запросе указан неверный адрес
  11. Структура данных API-интерфейса
  12. Заголовок запроса
  13. Объект запроса
  14. Объект ответа
  15. Объект ошибки
  16. Справочник кодов ошибок API СБИС

Znuny and ((OTRS)) Community Edition

OTRS 5 TicketUpdate via WebServices JSON : Error while decoding request content..

Moderator: crythias

OTRS 5 TicketUpdate via WebServices JSON : Error while decoding request content..

Post by LXsysadmin » 25 Mar 2019, 14:51

We are trying to update multiple tickets via OTRS 5 web services using JSON. All is working fine except for one ticket. JSON looks the same as for other tickets.
We get error like:

Message: Decoding the JSON string failed:
RemoteAddress:
RequestURI: /nph-genericinterface.pl/Webservice/Robot/TicketUpdate/106589?SessionID=H3uAMCCcPNIj1Dem7o30PeIk4zBxnK3K
Traceback (32704):
Module: Kernel::System::JSON::Decode Line: 139
Module: Kernel::GenericInterface::Transport::HTTP::REST::ProviderProcessRequest Line: 290
Module: Kernel::GenericInterface::Transport::ProviderProcessRequest Line: 108
Module: Kernel::GenericInterface::Provider::Run Line: 180
Module: ModPerl::ROOT::ModPerl::Registry::opt_otrs_bin_cgi_2dbin_nph_2dgenericinterface_2epl::handler Line: 38
Module: (eval) (v1.99) Line: 204
Module: ModPerl::RegistryCooker::run (v1.99) Line: 204
Module: ModPerl::RegistryCooker::default_handler (v1.99) Line: 170
Module: ModPerl::Registry::handler (v1.99) Line: 31

ERROR: GenericInterfaceProvider-10 Perl: 5.10.1 OS: linux Time: Mon Mar 25 14:32:26 2019
Message: DebugLog error: Summary: Error while decoding request content. Data : No data provided.
Message: DebugLog error: Summary: Request could not be processed Data : Error while decoding request content..
Message: DebugLog error: Summary: Returning provider data to remote system (HTTP Code: 500) Data : Error while decoding request content..

Can we somehow get more information about what is wrong? JSON validators say, that JSON is correct. How to get output what and where something goes wrong? Is there some value incorrect or symbol or.

Re: OTRS 5 TicketUpdate via WebServices JSON : Error while decoding request content..

Post by root » 25 Mar 2019, 15:18

Show your JSON and HTTP method.
Any details in the web service log (switch to debug first)

Znuny and Znuny LTS running on CentOS / RHEL / Debian / SLES / MySQL / PostgreSQL / Oracle / OpenLDAP / Active Directory / SSO

Use a test system — always.

Do you need professional services? Check out https://www.znuny.com/

Do you want to contribute or want to know where it goes ?

Источник

Обработать ошибки в API СБИС

Удаленные методы программного интерфейса СБИС (API) вызываются по протоколу HTTPS, данные предаются в формате JSON. При этом могут возникать ошибки:

  • в адресации сервера;
  • протоколе HTTP/HTTPS;
  • теле JSON-запроса;
  • параметрах аутентификации на online.sbis.ru — неверный логин/пароль, недопустимый сертификат ключа ЭП, отсутствие прав на доступ к методу;
  • функциях внешнего интерфейса — отсутствует обязательный параметр;
  • процессе выполнения команды — нет запрашиваемого документа.

Если в результате выполнения команды пришел ответ, отличный от «HTTP/1.1 200 OK», значит произошла ошибка. Проанализируйте результат, начиная с проверки HTTP-кода ответа. Например:

  • «HTTP/1.1 401 Unauthorized» — необходимо аутентифицироваться;
  • «HTTP/1.1 307 Temporary Redirect» — код аутентификации устарел. Необходимо произвести повторную аутентификацию и повторить запрос с новым идентификатором сессии.
  • Справочник кодов ошибок API СБИС

Общие рекомендации для обработки ошибок

Ошибки при работе с API СБИС делятся на два класса:

  • нефатальные — исправляются при повторении запроса через некоторое время без изменения параметров или дополнительных действий. Чтобы избавиться от ошибки, повторите запрос через 5 секунд;
  • фатальные — невозможно исправить при повторном вызове без изменения параметров запроса или дополнительных действий.

Источник

При вызове метода API вернулась фатальная HTTP-ошибка 401 «Unauthorized» с сообщением «Not authorized».

Причина №1. Не указан идентификатор сессии (X-SBISSessionID) в HTTP-заголовке запроса

  1. Пройдите аутентификацию для получения нового идентификатора.
  2. Укажите правильный идентификатор сессии как значение заголовка HTTP-пакета «X-SBISSessionID».

Пример неверного запроса (идентификатор сессии отсутствует)

Пример ответа

Причина №2. Идентификатор сессии (X-SBISSessionID) в HTTP-заголовке запроса устарел

Идентификатор может устареть из-за большого периода неактивности (отсутствия вызовов) или после окончания регламентных работ на сайте СБИС.

  1. Пройдите аутентификацию для получения нового идентификатора.
  2. Укажите правильный идентификатор сессии как значение заголовка HTTP-пакета «X-SBISSessionID».

Пример неверного запроса

Ошибка — в указании кодировки UTF-8. При этом, в JSON части запроса используется кодировка Win-1251. По тексту запроса это невозможно определить.

Пример ответа

Причина №3. В запросе указан неверный адрес

Такое случается, если при выполнении аутентификации указан адрес https://online.sbis.ru/service/?srv=1 вместо https://online.sbis.ru/auth/service/. Часто происходит при выполнении команд «СБИС.Аутентифицировать», «СБИС.АутентифицироватьПоСертификату», «СБИС.Выход», «СБИС.СписокСертификатовДляАутентификации» и других.

Пример ответа на неправильный запрос

Источник

Структура данных API-интерфейса

Для обмена данными программный интерфейс СБИС API использует формат JSON. Обмен с online.sbis.ru происходит с помощью HTTP-запросов/ответов.

  • POST — запросы на выполнение команд;
  • GET — запросы для получения содержимого указанного ресурса, данных вложений или электронных подписей (ЭП).

Заголовок запроса

В заголовке запроса обязательно указывается:

  • адрес сервера:
    1. для команд аутентификации — https://online.sbis.ru/auth/service/;
    2. для прочих команд — https://online.sbis.ru/service/?srv=1 HTTP/1.1;
  • идентификатор сессии «X-SBISSessionID»;
  • кодировка символов в поле «Content-Type»:
    1. application/json-rpc;charset=utf-8 — рекомендуем «utf-8»;
    2. application/json-rpc;charset=windows-1251 — «windows-1251».
  • размер запроса в байтах в «Content-Length»;
  • название и версия информационной системы в заголовке «User-Agent».

Объект запроса

Объект запроса содержит:

  • значение и версию спецификации JSON-RPC — «jsonrpc»:«2.0»;
  • параметр «method» с названием команды;
  • параметр «params» с объектом параметров команды;
  • параметр «id»с целочисленным значением.

Объект ответа

В ответ на запрос с сервера возвращается JSON-структура:

  • значение и версия спецификации JSON-RPC — «jsonrpc»:«2.0»;
  • параметр «id» со значением из объекта запроса;
  • значение объекта «result», содержащее ответ на команду.

Если произошла ошибка, в ответе на запрос вернется «error».

Объект ошибки

Если вернулась ошибка, в объекте ответа в поле «error» будут указаны:

  • «code» — код ошибки;
  • «message» — текст ошибки;
  • «details» — подробное описание;
  • «data» — тип ошибки.

Источник

Справочник кодов ошибок API СБИС

Расширенные коды ошибок внешнего API-интерфейса СБИС имеют вид:

  • 00000000-0000-0000-0000-1XXXXXXXXXXX, где X — шестнадцатиричное значение (0..9, А..F).
  • 00000000-0000-0000-0000-100000000000 — ошибка общего вида, не имеющая классификации. Как правило, фатальная.

Фатальные ошибки — не подразумевают исправление. При повторной отправке запроса без изменения его тела имеют вид — 00000000-0000-0000-0000-1FAXXXXXXXXX.Символы «FA» в коде означают «fatal».

  • 00000000-0000-0000-0000-1FA00001XXXX — фатальные ошибки входных параметров.
  • 00000000-0000-0000-0000-1FA00002XXXX — ошибки, которые связаны с криптографией.
  • 00000000-0000-0000-0000-1FA00003XXXX — ошибки модуля работы с формализованными документами.
  • 00000000-0000-0000-0000-1FA00004XXXX — ошибки запуска в документооборот.
  • 00000000-0000-0000-0000-1FA0000FXXXX — ошибки форматно-логического контроля.
  • 00000000-0000-0000-0000-1FA000001000 — код фатальной ошибки при аутентификации.
  • 00000000-0000-0000-0000-1FA000001001 — ошибка аутентификации. Возникает если данные в полях «Логин»/«Пароль» не указаны или указаны неверно.
  • 00000000-0000-0000-0000-1FA000001002 — код фатальной ошибки аутентификации. Возникает, когда для дальнейшей работы требуется подтверждение действий от пользователя.
  • 00000000-0000-0000-0000-1FA0000A0001 — «невозможно выполнить преобразование». Возникает при попытке получить архив,PDF-файл, PDF или HTML-представление документа, имеющее вложение с испорченным или недопустимым содержимым.

Нефатальные ошибки — подразумевают исправление. При повторной отправке запроса изменения тела запроса не требуется.

  • 00000000-0000-0000-0000-1AAXXXXXXXX — общая маска ошибок.
  • 00000000-0000-0000-0000-1AA00001XXXX — ошибки внутренних сервисов.
  • 00000000-0000-0000-0000-1AA0000F1002 — предупреждение о необходимости повторить запрос через 3-5 секунд. Повтор нужен для получения результата асинхронного запроса.
  • 00000000-0000-0000-0000-1AA000001000 — код нефатальной ошибки аутентификации.

Источник

Введение

Наша команда занимается разработкой приложения Tardis (MCOB. Управление бухгалтерией). Приложение используется компаниями уполномоченных бухгалтерий для управления и комплексного взаимодействия c базами 1С, в которых ведется бухгалтерский учет по обслуживаемым организациям.

Приложение предоставляет пользователям единый интерфейс, в котором отображаются актуальные сведения о состоянии ведения учета по организациям во множестве баз 1С.

Кроме этого возникла потребность отображать в этом интерфейсе данные организаций, по которым отчетность и взаимодействие с контролирующими органами выполняется в таких сервисах как СБИС.Онлайн и Контур.Экстерн.

Своевременное информирование о статусах отчетности или получении Требований от контролирующих органов является очень важным для бухгалтеров. Особенно когда бухгалтер ведет несколько организаций в разных программах, или каждая организация имеет собственный аккаунт в какой-то системе.

О недокументированных особенностях получения данных из СБИС.Онлайн и пойдет речь в этой статье.

Вся документация для разработки взаимодействия изучалась на официальном сайте СБИС.
В соответствии с ней выполнялась первоначальная авторизация для получения токена, выполнялись запросы с этим токеном, и после получения данных закрывался сеанс.

1. Получение регламентированных отчетов

Первой задачей было получить статусы регламентированных отчетов.

Сначала попробовали метод API отчетности СБИС.СписокИзменений
https://sbis.ru/help/integration/api/reporting/commands/list_change

Но этот метод возвращал только документы ЭДО (счета, реализации, акты). Даже если установить параметр запроса ТолькоОтчетность=Да, как рекомендовала техподдержка СБИС, регламентированные отчеты получить не удавалось.

Поэтому попробовали использовать метод СБИС.СписокДокументов https://sbis.ru/help/integration/api/all_methods/list_doc , хотя техподдержка СБИС не всегда признает существование этого метода для отчетности

article-image

Для получения именно отчетов нашли способ — указывать в параметре Тип=ОтчетФНС или ОтчетПФР, хотя в документации данные типы документов не указаны https://sbis.ru/help/integration/catalog/guide#1

И техподдержка СБИС считает, что данные типы использовать нельзя

article-image

Тем не менее, таким способом нам удалось получать статусы по всем видам отчетов.

2. Получение Требований ФНС

В АПИ ЭДО СБИС мы не нашли метода, возвращающего список документов такого типа.

Изначально, мы пробовали получить Требования методом СБИС.СписокДокументов, но документы такого типа данным методом не возвращаются.

article-image

Однако, удалось получить новые Требования методом СБИС.СписокСлужебныхЭтапов. https://sbis.ru/help/integration/api/all_methods/stage_doc

и получить данные документа методом СБИС.ПрочитатьДокумент. https://sbis.ru/help/integration/api/all_methods/read_doc

article-image

Выяснилась неприятная особенность. Метод СБИС.СписокСлужебныхЭтапов с фильтром ТолькоОтчетность=Да получает только первичное появление статуса по Требованию.

Но если пользователь в кабинете СБИС откроет данный документ, то информация о появлении Требования перестает попадать в ответ запроса этим методом.

Поэтому для получения, уже прочитанных пользователями Требований, обратились за помощью в поддержку СБИС, которая сначала ответила, что Требования получить никак нельзя.

article-image

Но позже прислала письмо с разъяснениями, что нужно использовать метод СБИС.СписокИзменений с параметром Тип=ИстребованиеФНС.

Данный способ полностью решил нашу проблему.

Следует отметить, при тестах выполнения запросов в Postman легко не заметить, что запрос возвращает несколько объектов внутри массива “Документ”

article-image

Хотя при чтении ответа средствами 1С содержимое массива отображается корректно и полностью.

article-image

Запрос СБИС.СписокИзменений выдает постраничный ответ. И если параметр “ЕстьЕще” равен “Да”, то для получения следующей страницы в запросе необходимо передать идентификатор последнего документа из массива ”Документ” предыдущего запроса.

article-image

3. Получатели и Доверители

К сожалению, скрины к данному описанию нет возможности разместить из-за требований конфиденциальности.

Учетная запись СБИСоформляется на одну организацию по ИНН/КПП. При этом в учетной записи документооборот может происходить по нескольким организациям (если позволяет тариф).

Когда мы пытались получить Требования по организациям одного из аккаунтов СБИС , обнаружили, что по некоторым организациям Требования есть в личном кабинете, но отсутствуют в ответах API.

Метод СБИС.СписокИзменений изначально выполняли по каждой организации аккаунта, подставляя в запрос их ИНН/КПП. В ответе по некоторым организациям был всегда пустой список документов.

В чем заключалась проблема? Оказывается, в СБИС есть такие понятия, как:

  1. Доверитель — это организация, которая поручает другой организации получать и отправлять свои документы по ЭДО.
  2. Получатель — организация, которая получила такую доверенность от Доверителя.

В личном кабинете СБИС такие Требования в отборах по Доверителюне отображаются, но они находятся среди документов Получателя

Аналогично в ответах API мы получали Требования других организаций как входящие документы, адресованные главной организации аккаунта.

При этом в ответе нет явного реквизита, позволяющего определить фактическую организацию этого документа.

После изучения проблемы было определено, что у документа Требование в реквизитах есть список Событий.

У каждого События список Вложений. При участии Требования в цикле обмена у одного из вложений имя файла содержит ИНН фактической организации.

Таким непростым способом получилось идентифицировать Требования организаций Доверителей и корректно отобразить их в нашей программе.

4.Получение сверок с ФНС по задолженности

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

Выполнять запросы сверок с ФНС через API СБИС нет возможности.

Но в ЛК СБИС есть возможность настроить автоматическое формирование и отправку запросов сверок.

Т.е. для нас оставалось разработать поиск и парсинг уже полученных в ЛК СБИС сверок.

Изначально данные сверок с ФНС получали методомСБИС.СписокДокументов с параметром Тип=ЗапросФНС и последующим поиском в выборке документов, где

Направление = «Входящий» и Вложение.Подтип = «1160080» и чтением данных найденных сверок методом СБИС.ПрочитатьДокумент

article-image

Оказалось, что таким способом можно получить только документы “Справка о состоянии расчетов с ФНС” по аккаунтам, у которых включена услуга «Супер-сверка»

article-image

Чтобы получать данные сверок из документов “Выписка расчетов с ФНС” без покупки дополнительных лицензий клиентами, мы разработали поиск выписок и получение данных из их содержимого.

Для этого массива документов со Справками (Тип=ЗапросФНС) мы добавили поиск Выписок. Фильтр: Документ.Примечание = НСтр(«ru=’Выписка операций по расчетам с бюджетом'», «ru») и Документ.Состояние.Код = «7»

Коды состояний документов: https://sbis.ru/help/integration/api/reporting/guides#code_con

article-image

Из найденных и прочитанных документов мы получаем ссылки на сами файлы сверок.

Скачиваем их во временный каталог и парсим для получения данных о задолженности, которые потом отображаем в нашей программе по всем организациям аккаунта.

5. Проверка организации в аккаунте

При выполнении всех запросов к API СБИС в параметрах всегда передаются ИНН и КПП.

Иногда КПП организации меняется или организация переводится в другой аккаунт СБИС

Поэтому для проверки актуальности организации в аккаунте и соответствия ИНН и КПП организации в нашей программе мы используем запрос методом СБИС.СписокНашихОрганизаций.

https://sbis.ru/help/integration/api/all_methods/company

6. Ограничение по количеству запросов в минуту

API СБИС имеет лимит по количеству запросов к сервису равный 300 запросов в минуту.

Эту проблему решили просто. Сделали паузу 0,5 секунд перед выполнением каждого запроса. Таким образом запросов в минуту никогда не будет больше 120.

7. Формат результата разный

В процессе отладки взаимодействия с API СБИС обнаружили, что в случае ошибки, ответ не всегда приходит в виде описанного в документации объекта (Структуры).

article-image

Иногда объект Error является строкой!

article-image

Это тоже необходимо учитывать при обработке ответов API СБИС.

Заключение

API СБИС имеет довольно обширный функционал, которым удобно пользоваться при разработке интеграций. И несмотря на пробелы в документации и поддержке, из него можно получить практические всё, что необходимо.

Here I have created simple example for you by using Alamofire which is good practice for API calls in app.

I have created Codable struct for you from your JSON response from here.

Which is look like below:

import Foundation

struct DeclarationInfoElement: Codable {
    let id, createdDate, lastmodifiedDate: String
    let data: DataClass

    enum CodingKeys: String, CodingKey {
        case id
        case createdDate = "created_date"
        case lastmodifiedDate = "lastmodified_date"
        case data
    }
}

struct DataClass: Codable {
    let step0: Step0
    let step1: Step1
    let step2: [JSONAny]
    let step3, step4, step5, step6: Step
    let step7, step8, step9, step10: Step
    let step11: Step11
    let step12, step13, step14, step15: Step
    let step16: Step

    enum CodingKeys: String, CodingKey {
        case step0 = "step_0"
        case step1 = "step_1"
        case step2 = "step_2"
        case step3 = "step_3"
        case step4 = "step_4"
        case step5 = "step_5"
        case step6 = "step_6"
        case step7 = "step_7"
        case step8 = "step_8"
        case step9 = "step_9"
        case step10 = "step_10"
        case step11 = "step_11"
        case step12 = "step_12"
        case step13 = "step_13"
        case step14 = "step_14"
        case step15 = "step_15"
        case step16 = "step_16"
    }
}

struct Step0: Codable {
    let declarationType, declarationYearTo, declarationYearFrom: String
}

struct Step1: Codable {
    let city, region, street, country: String
    let cityPath, cityType, district, lastname: String
    let postCode, postType, workPost, firstname: String
    let workPlace, middlename, streetType: String
    let changedName: Bool
    let countryPath, engPostCode, postCategory, actualStreet: String
    let actualCountry, actualCityType, actualPostCode, actualStreetType: String
    let engActualAddress, previousLastname, ukrActualAddress, corruptionAffected: String
    let engActualPostCode, previousFirstname, previousMiddlename, responsiblePosition: String
    let sameRegLivingAddress, postTypeExtendedstatus, engSameRegLivingAddress, housePartNumExtendedstatus: String
    let postCategoryExtendedstatus, apartmentsNumExtendedstatus: String

    enum CodingKeys: String, CodingKey {
        case city, region, street, country, cityPath, cityType, district, lastname, postCode, postType, workPost, firstname, workPlace, middlename, streetType, changedName, countryPath
        case engPostCode = "eng_postCode"
        case postCategory
        case actualStreet = "actual_street"
        case actualCountry = "actual_country"
        case actualCityType = "actual_cityType"
        case actualPostCode = "actual_postCode"
        case actualStreetType = "actual_streetType"
        case engActualAddress = "eng_actualAddress"
        case previousLastname = "previous_lastname"
        case ukrActualAddress = "ukr_actualAddress"
        case corruptionAffected
        case engActualPostCode = "eng_actualPostCode"
        case previousFirstname = "previous_firstname"
        case previousMiddlename = "previous_middlename"
        case responsiblePosition, sameRegLivingAddress
        case postTypeExtendedstatus = "postType_extendedstatus"
        case engSameRegLivingAddress = "eng_sameRegLivingAddress"
        case housePartNumExtendedstatus = "housePartNum_extendedstatus"
        case postCategoryExtendedstatus = "postCategory_extendedstatus"
        case apartmentsNumExtendedstatus = "apartmentsNum_extendedstatus"
    }
}

struct Step: Codable {
    let empty: String
}

struct Step11: Codable {
    let the1530711324488: The1530711324488

    enum CodingKeys: String, CodingKey {
        case the1530711324488 = "1530711324488"
    }
}

struct The1530711324488: Codable {
    let person: String
    let rights: Rights
    let iteration, objectType, sizeIncome, incomeSource: String
    let sourceCitizen, otherObjectType, sourceUaLastname, sourceEngFullname: String
    let sourceUaFirstname, sourceUkrFullname, sourceUaMiddlename, sourceUaCompanyCode: String
    let sourceUaCompanyName, sourceEngCompanyCode, sourceEngCompanyName, sourceUkrCompanyName: String
    let objectTypeExtendedstatus, sizeIncomeExtendedstatus, sourceEngCompanyAddress, sourceUkrCompanyAddress: String
    let incomeSourceExtendedstatus, sourceCitizenExtendedstatus, otherObjectTypeExtendedstatus, sourceUaSameRegLivingAddress: String
    let sourceUaCompanyCodeExtendedstatus, sourceUaCompanyNameExtendedstatus: String

    enum CodingKeys: String, CodingKey {
        case person, rights, iteration, objectType, sizeIncome, incomeSource
        case sourceCitizen = "source_citizen"
        case otherObjectType
        case sourceUaLastname = "source_ua_lastname"
        case sourceEngFullname = "source_eng_fullname"
        case sourceUaFirstname = "source_ua_firstname"
        case sourceUkrFullname = "source_ukr_fullname"
        case sourceUaMiddlename = "source_ua_middlename"
        case sourceUaCompanyCode = "source_ua_company_code"
        case sourceUaCompanyName = "source_ua_company_name"
        case sourceEngCompanyCode = "source_eng_company_code"
        case sourceEngCompanyName = "source_eng_company_name"
        case sourceUkrCompanyName = "source_ukr_company_name"
        case objectTypeExtendedstatus = "objectType_extendedstatus"
        case sizeIncomeExtendedstatus = "sizeIncome_extendedstatus"
        case sourceEngCompanyAddress = "source_eng_company_address"
        case sourceUkrCompanyAddress = "source_ukr_company_address"
        case incomeSourceExtendedstatus = "incomeSource_extendedstatus"
        case sourceCitizenExtendedstatus = "source_citizen_extendedstatus"
        case otherObjectTypeExtendedstatus = "otherObjectType_extendedstatus"
        case sourceUaSameRegLivingAddress = "source_ua_sameRegLivingAddress"
        case sourceUaCompanyCodeExtendedstatus = "source_ua_company_code_extendedstatus"
        case sourceUaCompanyNameExtendedstatus = "source_ua_company_name_extendedstatus"
    }
}

struct Rights: Codable {
    let the1: [String: String]

    enum CodingKeys: String, CodingKey {
        case the1 = "1"
    }
}

// MARK: Encode/decode helpers

class JSONNull: Codable {
    public init() {}

    public required init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if !container.decodeNil() {
            throw DecodingError.typeMismatch(JSONNull.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for JSONNull"))
        }
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encodeNil()
    }
}

class JSONCodingKey: CodingKey {
    let key: String

    required init?(intValue: Int) {
        return nil
    }

    required init?(stringValue: String) {
        key = stringValue
    }

    var intValue: Int? {
        return nil
    }

    var stringValue: String {
        return key
    }
}

class JSONAny: Codable {
    let value: Any

    static func decodingError(forCodingPath codingPath: [CodingKey]) -> DecodingError {
        let context = DecodingError.Context(codingPath: codingPath, debugDescription: "Cannot decode JSONAny")
        return DecodingError.typeMismatch(JSONAny.self, context)
    }

    static func encodingError(forValue value: Any, codingPath: [CodingKey]) -> EncodingError {
        let context = EncodingError.Context(codingPath: codingPath, debugDescription: "Cannot encode JSONAny")
        return EncodingError.invalidValue(value, context)
    }

    static func decode(from container: SingleValueDecodingContainer) throws -> Any {
        if let value = try? container.decode(Bool.self) {
            return value
        }
        if let value = try? container.decode(Int64.self) {
            return value
        }
        if let value = try? container.decode(Double.self) {
            return value
        }
        if let value = try? container.decode(String.self) {
            return value
        }
        if container.decodeNil() {
            return JSONNull()
        }
        throw decodingError(forCodingPath: container.codingPath)
    }

    static func decode(from container: inout UnkeyedDecodingContainer) throws -> Any {
        if let value = try? container.decode(Bool.self) {
            return value
        }
        if let value = try? container.decode(Int64.self) {
            return value
        }
        if let value = try? container.decode(Double.self) {
            return value
        }
        if let value = try? container.decode(String.self) {
            return value
        }
        if let value = try? container.decodeNil() {
            if value {
                return JSONNull()
            }
        }
        if var container = try? container.nestedUnkeyedContainer() {
            return try decodeArray(from: &container)
        }
        if var container = try? container.nestedContainer(keyedBy: JSONCodingKey.self) {
            return try decodeDictionary(from: &container)
        }
        throw decodingError(forCodingPath: container.codingPath)
    }

    static func decode(from container: inout KeyedDecodingContainer<JSONCodingKey>, forKey key: JSONCodingKey) throws -> Any {
        if let value = try? container.decode(Bool.self, forKey: key) {
            return value
        }
        if let value = try? container.decode(Int64.self, forKey: key) {
            return value
        }
        if let value = try? container.decode(Double.self, forKey: key) {
            return value
        }
        if let value = try? container.decode(String.self, forKey: key) {
            return value
        }
        if let value = try? container.decodeNil(forKey: key) {
            if value {
                return JSONNull()
            }
        }
        if var container = try? container.nestedUnkeyedContainer(forKey: key) {
            return try decodeArray(from: &container)
        }
        if var container = try? container.nestedContainer(keyedBy: JSONCodingKey.self, forKey: key) {
            return try decodeDictionary(from: &container)
        }
        throw decodingError(forCodingPath: container.codingPath)
    }

    static func decodeArray(from container: inout UnkeyedDecodingContainer) throws -> [Any] {
        var arr: [Any] = []
        while !container.isAtEnd {
            let value = try decode(from: &container)
            arr.append(value)
        }
        return arr
    }

    static func decodeDictionary(from container: inout KeyedDecodingContainer<JSONCodingKey>) throws -> [String: Any] {
        var dict = [String: Any]()
        for key in container.allKeys {
            let value = try decode(from: &container, forKey: key)
            dict[key.stringValue] = value
        }
        return dict
    }

    static func encode(to container: inout UnkeyedEncodingContainer, array: [Any]) throws {
        for value in array {
            if let value = value as? Bool {
                try container.encode(value)
            } else if let value = value as? Int64 {
                try container.encode(value)
            } else if let value = value as? Double {
                try container.encode(value)
            } else if let value = value as? String {
                try container.encode(value)
            } else if value is JSONNull {
                try container.encodeNil()
            } else if let value = value as? [Any] {
                var container = container.nestedUnkeyedContainer()
                try encode(to: &container, array: value)
            } else if let value = value as? [String: Any] {
                var container = container.nestedContainer(keyedBy: JSONCodingKey.self)
                try encode(to: &container, dictionary: value)
            } else {
                throw encodingError(forValue: value, codingPath: container.codingPath)
            }
        }
    }

    static func encode(to container: inout KeyedEncodingContainer<JSONCodingKey>, dictionary: [String: Any]) throws {
        for (key, value) in dictionary {
            let key = JSONCodingKey(stringValue: key)!
            if let value = value as? Bool {
                try container.encode(value, forKey: key)
            } else if let value = value as? Int64 {
                try container.encode(value, forKey: key)
            } else if let value = value as? Double {
                try container.encode(value, forKey: key)
            } else if let value = value as? String {
                try container.encode(value, forKey: key)
            } else if value is JSONNull {
                try container.encodeNil(forKey: key)
            } else if let value = value as? [Any] {
                var container = container.nestedUnkeyedContainer(forKey: key)
                try encode(to: &container, array: value)
            } else if let value = value as? [String: Any] {
                var container = container.nestedContainer(keyedBy: JSONCodingKey.self, forKey: key)
                try encode(to: &container, dictionary: value)
            } else {
                throw encodingError(forValue: value, codingPath: container.codingPath)
            }
        }
    }

    static func encode(to container: inout SingleValueEncodingContainer, value: Any) throws {
        if let value = value as? Bool {
            try container.encode(value)
        } else if let value = value as? Int64 {
            try container.encode(value)
        } else if let value = value as? Double {
            try container.encode(value)
        } else if let value = value as? String {
            try container.encode(value)
        } else if value is JSONNull {
            try container.encodeNil()
        } else {
            throw encodingError(forValue: value, codingPath: container.codingPath)
        }
    }

    public required init(from decoder: Decoder) throws {
        if var arrayContainer = try? decoder.unkeyedContainer() {
            self.value = try JSONAny.decodeArray(from: &arrayContainer)
        } else if var container = try? decoder.container(keyedBy: JSONCodingKey.self) {
            self.value = try JSONAny.decodeDictionary(from: &container)
        } else {
            let container = try decoder.singleValueContainer()
            self.value = try JSONAny.decode(from: container)
        }
    }

    public func encode(to encoder: Encoder) throws {
        if let arr = self.value as? [Any] {
            var container = encoder.unkeyedContainer()
            try JSONAny.encode(to: &container, array: arr)
        } else if let dict = self.value as? [String: Any] {
            var container = encoder.container(keyedBy: JSONCodingKey.self)
            try JSONAny.encode(to: &container, dictionary: dict)
        } else {
            var container = encoder.singleValueContainer()
            try JSONAny.encode(to: &container, value: self.value)
        }
    }
}

And your API call will be:

Alamofire.request("https://public-api.nazk.gov.ua/v1/declaration/4647cd5d-5877-4606-8e61-5ac5869b71e0").responseData { response in
        switch response.result {
        case .success(let data):
            let declarationInfoElement = try? JSONDecoder().decode(DeclarationInfoElement.self, from: data)
            print(declarationInfoElement)
        case .failure:
            print("fail")
        }
    }

And here is demo project. Hope this will help.

Понравилась статья? Поделить с друзьями:
  • Error while creating lua virtual machine uopilot как исправить
  • Error while creating license dat opcom
  • Error while assembling tarkov
  • Error while ajax request
  • Error while accessing the asio driver