Как исправить ошибку «Неверный порядок записи JSON» в 1С и получить чистый JSON без служебных метаданных

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

При интеграции 1С:Предприятия со сторонними ИТ-системами (веб-сайтами, мобильными приложениями, маркетплейсами) разработчики часто сталкиваются со специфическими трудностями при выгрузке документов. Самая частая проблема — возникновение системной ошибки Неверный порядок записи JSON. Вторая по популярности проблема — появление в итоговом файле избыточных служебных тегов типа #type, #value, jv8:Structure, которые генерируются стандартным XDTO-сериализатором 1С. Внешние системы такие теги не понимают и не могут корректно распарсить файл.

В этой статье мы подробно разберем, почему возникают эти ошибки, и научимся формировать чистый, красивый и легковесный JSON, полностью готовый для интеграции с любым веб-сайтом.

Выясняем причину ошибки «Неверный порядок записи JSON»

Для начала проанализируем ситуацию и поймем, почему платформа 1С выбрасывает исключение при записи. В платформе 1С реализовано два принципиально разных подхода к работе с JSON:

  1. Потоковый метод (низкоуровневый) — когда вы последовательно вызываете команды записи элементов в поток: ЗаписатьНачалоОбъекта(), ЗаписатьИмяСвойства(), ЗаписатьЗначение(), ЗаписатьКонецОбъекта(). Этот метод незаменим при записи гигантских объемов данных, так как он не нагружает оперативную память.
  2. Объектный метод (высокоуровневый) — когда вы передаете готовый объект 1С (например, Структура или Массив) напрямую в процедуру ЗаписатьJSON(), и платформа самостоятельно конвертирует его в текстовый формат целиком.

Ошибка Неверный порядок записи JSON возникает тогда, когда разработчик пытается смешать оба этих подхода в одном цикле. Посмотрим на некорректный пример кода:


ЗаписиJSON.ЗаписатьНачалоОбъекта(); // 1. Открыли объект вручную
СтруктураДокумента = ПолучитьСтруктуру(СтрСписокДокументов.Ссылка);
ЗаписатьJSON(ЗаписиJSON, СтруктураДокумента, НастройкиСериал); // 2. Пытаемся записать готовый объект внутрь
ЗаписиJSON.ЗаписатьКонецОбъекта(); // 3. Пробуем закрыть объект вручную

Процедура ЗаписатьJSON() при вызове автоматически генерирует открывающие фигурные скобки { и закрывающие скобки } для переданной структуры. Когда мы вызываем этот метод внутри уже открытого потока (после явного вызова ЗаписатьНачалоОбъекта()), мы нарушаем иерархию структуры (стейт-машину парсера платформы). В результате платформа завершает работу аварийно.

Почему XDTO-сериализатор засоряет итоговый JSON

Когда разработчики сталкиваются с тем, что сложные типы данных (ссылки на справочники, документы, перечисления или уникальные идентификаторы) не сериализуются напрямую методом ЗаписатьJSON(), они начинают использовать СериализаторXDTO.ЗаписатьJSON().

Этот способ действительно решает проблему типизации, но на выходе получается тяжелый и нечитаемый для внешних систем JSON-код:


"#value": [
    {
        "#type": "jv8:Structure",
        "#value": [
            {
                "name": {
                    "#type": "jxs:string",
                    "#value": "Ссылка"
                },
                "Value": {
                    "#type": "jxs:string",
                    "#value": "Поступление на расчетный счет АГ000000008 от 04.06.2020"
                }
            }
        ]
    }
]

XDTO-сериализатор разработан для сценариев обмена «1С-в-1С». Служебные метаданные с префиксом # нужны для того, чтобы принимающая база 1С могла точно восстановить исходные типы метаданных. Веб-сайту эти подробности не нужны — ему требуется плоская, лаконичная структура ключ-значение:


{
    "Ссылка": "Поступление на расчетный счет АГ000000008 от 04.06.2020",
    "Проведен": true,
    "Сумма": 150000
}

Разбираем по шагам правильное решение

Для получения чистого JSON без служебного мусора мы откажемся от XDTO-сериализации и используем стандартный метод ЗаписатьJSON() с подключением специальной функции преобразования типов.

Платформа 1С умеет автоматически переводить в JSON только примитивные типы данных (Строка, Число, Булево, Дата). Если в структуру данных попадает сложный тип (ссылка ДокументСсылка, СправочникСсылка или УникальныйИдентификатор), платформа не знает, как его представить, и вызывает ошибку. Мы укажем платформе собственную экспортную функцию, которая будет перехватывать сложные типы и возвращать их понятное текстовое представление.

Реализация кода выгрузки

Рассмотрим подробнее программный код. Нам понадобятся две составляющие: вспомогательная функция преобразования типов и основная процедура экспорта.

1. Функция преобразования типов (обязательно должна быть определена как Экспорт):


&НаСервере
Функция ФункцияПреобразованияТипов(Свойство, Значение, ДополнительныеПараметры, Отказ) Экспорт
    
    ТипЗначения = ТипЗнч(Значение);
    
    // Проверяем, является ли значение ссылочным типом данных
    Если Справочники.ТипВсеСсылки().СодержитТип(ТипЗначения) 
        ИЛИ Документы.ТипВсеСсылки().СодержитТип(ТипЗначения) Тогда
        
        // Для внешних веб-систем лучшим решением будет передача GUID ссылки в виде строки
        Возврат Строка(Значение.УникальныйИдентификатор());
        
        // Если принимающей системе нужно простое текстовое представление, можно использовать:
        // Возврат Строка(Значение);
    КонецЕсли;
    
    // Обрабатываем уникальные идентификаторы напрямую
    Если ТипЗначения = Тип("УникальныйИдентификатор") Тогда
        Возврат Строка(Значение);
    КонецЕсли;
    
    // Обрабатываем перечисления
    Если Перечисления.ТипВсеСсылки().СодержитТип(ТипЗначения) Тогда
        Возврат Строка(Значение); // Вернет строковое представление перечисления
    КонецЕсли;
    
    // Если тип не предопределен нами, сообщаем платформе о невозможности сериализации
    Отказ = Истина;
    
КонецФункции

2. Основная процедура выгрузки документов:


&НаСервере
Процедура ВыгрузитьДокументыВЧистыйJSON()
    
    ИмяВременногоФайла = ПолучитьИмяВременногоФайла("json");
    
    // Настраиваем правила сериализации стандартных типов (например, дат)
    НастройкиСериализации = Новый НастройкиСериализацииJSON;
    НастройкиСериализации.ВариантЗаписиДаты = ВариантЗаписиДатыJSON.ЛокальнаяДата;
    НастройкиСериализации.ФорматСериализацииДаты = ФорматДатыJSON.ISO;
    
    // Создаем массив для накопления объектов
    МассивОбъектов = Новый Массив;
    
    Для Каждого СтрСписокДокументов Из СписокДокументов Цикл 
        
        Если Не СтрСписокДокументов.Обрабатывать Тогда 
            Продолжить;
        КонецЕсли;
        
        // Получаем плоскую структуру реквизитов документа (только примитивные типы + ссылки)
        СтруктураДокумента = ПолучитьСтруктуру(СтрСписокДокументов.Ссылка);
        
        // Добавляем в структуру саму ссылку. Функция преобразования сама сделает из нее понятный GUID
        СтруктураДокумента.Вставить("Идентификатор", СтрСписокДокументов.Ссылка);
        
        МассивОбъектов.Добавить(СтруктураДокумента);
        
    КонецЦикла;
    
    // Инициализируем объект записи
    ЗаписиJSON = Новый ЗаписьJSON;
    ПараметрыЗаписи = Новый ПараметрыЗаписиJSON(, Символы.Таб); // Для красивого форматирования отступами
    ЗаписиJSON.ОткрытьФайл(ИмяВременногоФайла, "UTF-8",, ПараметрыЗаписи);
    
    // Важнейший шаг: записываем массив структур одной командой, передав имя функции-преобразователя
    ЗаписатьJSON(ЗаписиJSON, МассивОбъектов, НастройкиСериализации, "ФункцияПреобразованияТипов", ЭтотОбъект);
    
    ЗаписиJSON.Закрыть();
    
    // Помещаем результат во временное хранилище и очищаем временный файл на сервере
    АдресДанных = ПоместитьВоВременноеХранилище(Новый ДвоичныеДанные(ИмяВременногоФайла));
    УдалитьФайлы(ИмяВременногоФайла);
    
КонецПроцедуры

В чем преимущества этого подхода?

Применяя этот подход, вы навсегда избавитесь от ошибок сериализации и сможете легко настраивать интеграцию любой сложности с внешними веб-ресурсами.

← На главную