Как исправить ошибку "JSON parse error" и убрать UTF-8 BOM при отправке POST-запроса из 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Часто при интеграции 1С со сторонними веб-сервисами (особенно на базе современных фреймворков, таких как Python Django) разработчики сталкиваются с ошибками парсинга входящих данных. Рассмотрим типичную ситуацию: при попытке отправить файл через HTTPСоединение POST-запросом сервер возвращает ошибку {"detail":"JSON parse error - Unexpected UTF-8 BOM (decode using utf-8-sig): line 1 column 1 (char 0)"} с кодом состояния 400. Дальнейшие корректировки кода могут привести к другой ошибке: {"detail":"JSON parse error - Expecting value: line 1 column 1 (char 0)"}.

Проанализируем ситуацию по шагам, выясним причину такого поведения сервера и разберем способы решения этих проблем. Для быстрой отладки отправляемых данных и тестирования REST-запросов прямо внутри платформы подойдёт инструмент для тестирования HTTP-запросов.

Разберем проблему невидимого маркера BOM

Первая ошибка (Unexpected UTF-8 BOM) напрямую указывает на наличие маркера последовательности байтов (Byte Order Mark) в начале отправляемого сообщения. Когда мы в 1С используем методы записи в файл, такие как ЗаписьXML.ОткрытьФайл() или ЗаписьТекста.Открыть(), платформа автоматически добавляет в начало файла с кодировкой UTF-8 три скрытых байта (EF BB BF). 1С делает это для точной идентификации кодировки файла в ОС Windows.

Проблема заключается в том, что многие современные веб-серверы (в том числе REST API на Django) воспринимают BOM как посторонние текстовые символы перед началом полезных данных. Парсер просто не может прочитать структуру и выдает ошибку.

Чтобы избежать появления BOM при формировании запроса, откажемся от сохранения данных во временный файл и использования метода HTTPЗапрос.УстановитьИмяФайлаТела(). Вместо этого сформируем строку и передадим ее в запрос, явно отключив BOM:


// Пример формирования тела запроса без BOM
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку("UTF-8");
// ... здесь записываем элементы XML ...
СтрокаXML = ЗаписьXML.Закрыть();

// Передаем строку в запрос с явным указанием не использовать BOM
HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаXML, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);

Если специфика API строго требует отправки файла через multipart/form-data, перед отправкой файла его необходимо прочитать как ДвоичныеДанные, проверить первые три байта и при наличии удалить их программно.

Проанализируем несоответствие формата данных и заголовков

Даже решив проблему с BOM, вы можете столкнуться со второй ошибкой: "JSON parse error - Expecting value". Посмотрим на код инициализации запроса:


ЗаголовокHTTP = Новый Соответствие();
ЗаголовокHTTP.Вставить("Content-Type", "application/json;charset=utf-8");

Здесь кроется главная логическая ошибка. Заголовок Content-Type сообщает принимающему серверу, как именно нужно интерпретировать входящий поток байтов. Указывая application/json, мы заставляем сервер передать наше тело запроса JSON-парсеру. Но фактически в коде формируется XML-документ. Как только JSON-парсер встречает первый символ XML-тега (<), он не находит ожидаемой открывающей скобки JSON ({ или [) и генерирует ошибку валидации ("Expecting value").

Рассмотрим два варианта решения этой проблемы в зависимости от требований целевого API.

Решение 1: Сервер принимает только JSON

Судя по тексту ошибки от Django Rest Framework, сервер ожидает именно JSON. Если у вас нет доступа к настройкам API на стороне сайта (чтобы включить поддержку XML), необходимо переписать процедуру подготовки данных в 1С. Заменим ЗаписьXML на ЗаписьJSON:


ЗаписьJSON = Новый ЗаписьJSON;
ПараметрыJSON = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Нет);
ЗаписьJSON.УстановитьСтроку(ПараметрыJSON);

ЗаписьJSON.ЗаписатьНачалоОбъекта();
ЗаписьJSON.ЗаписатьИмяСвойства("Products");
ЗаписьJSON.ЗаписатьНачалоМассива();

Для Каждого нмк Из хмл Цикл
    ЗаписьJSON.ЗаписатьНачалоОбъекта();
    ЗаписьJSON.ЗаписатьИмяСвойства("Price");
    ЗаписьJSON.ЗаписатьЗначение(нмк.Значение.Цена);
    ЗаписьJSON.ЗаписатьИмяСвойства("Quantity");
    ЗаписьJSON.ЗаписатьЗначение(нмк.Значение.Количество);
    ЗаписьJSON.ЗаписатьИмяСвойства("Name");
    ЗаписьJSON.ЗаписатьЗначение(нмк.Значение.Наименование);
    ЗаписьJSON.ЗаписатьКонецОбъекта();
КонецЦикла;

ЗаписьJSON.ЗаписатьКонецМассива();
ЗаписьJSON.ЗаписатьКонецОбъекта();

СтрокаJSON = ЗаписьJSON.Закрыть();
HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаJSON, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);

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

Решение 2: Если сервер поддерживает XML

Если API сайта умеет принимать XML-пакеты, то нам достаточно исправить заголовки запроса, чтобы они соответствовали реальному содержимому тела:


ЗаголовокHTTP.Вставить("Content-Type", "application/xml; charset=utf-8");
// или "text/xml; charset=utf-8" в зависимости от требований спецификации API

При этом, формируя XML, обращайте внимание на логическую структуру. В исходном примере была допущена ошибка дублирования имен элементов корневого и вложенных узлов:


// ОШИБОЧНО:
ЗаписьXML.ЗаписатьНачалоЭлемента("Product"); // Корневой элемент
Для Каждого нмк Из хмл Цикл
    ЗаписьXML.ЗаписатьНачалоЭлемента("Product"); // Вложенный элемент с тем же именем
    // ...

Обычно XML-схема предполагает, что список содержит вложенные элементы. Правильно будет назвать корневой элемент во множественном числе (например, Products), а элементы цикла — в единственном (Product). Это исключит ошибки валидации структуры на стороне приемника.

← На главную