Как прочитать JSON в 1С, если изменился порядок свойств?

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

При организации обмена данными между базами 1С через JSON часто используется стандартный механизм сериализации объектов, например, наборов записей регистров. Однако этот подход имеет свою специфику: он чувствителен к порядку полей (измерений, реквизитов, ресурсов) в метаданных. Если в одной из баз этот порядок изменится, при загрузке данных возникнет ошибка "Ошибка преобразования данных XDTO". Давайте разберемся, почему так происходит и как решить эту проблему.

Проанализируем ситуацию. Выгрузка данных выполняется с помощью СериализаторXDTO.ЗаписатьJSON(), а загрузка — через ПрочитатьJSON(). Проблема кроется в том, как 1С формирует JSON при сериализации объектов базы данных. В итоговый файл добавляется специальное свойство "#type", которое явно указывает тип данных, например, "InformationRegisterRecordSet.ИмяВашегоРегистра". При чтении такого JSON платформа 1С видит этот тип и пытается сразу создать соответствующий объект, строго сопоставляя поля по их порядку в метаданных, а не по именам. Если порядок нарушен — происходит сбой.

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

Способ 1. Синхронизация структур (самый простой путь)

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

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

  2. Формирование данных в нужном порядке при выгрузке. Если менять метаданные в приемнике нельзя, можно "подстроиться" под них на стороне источника. Вместо прямой выгрузки набора записей, сначала получите данные запросом, перечислив поля в том порядке, в котором они ожидаются в базе-приемнике. Затем результат запроса выгрузите в таблицу значений и уже ее загрузите в набор записей для последующей сериализации.

    
    // Пример на стороне источника
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   МойРегистр.Измерение2, // Ставим в том порядке, как в базе-приемнике
        |   МойРегистр.Измерение1,
        |   МойРегистр.Ресурс1
        |ИЗ
        |   РегистрСведений.МойРегистр КАК МойРегистр";
    
    ТаблицаДляВыгрузки = Запрос.Выполнить().Выгрузить();
    
    НаборЗаписей = РегистрыСведений.МойРегистр.СоздатьНаборЗаписей();
    НаборЗаписей.Загрузить(ТаблицаДляВыгрузки);
    
    // Далее идет ваша стандартная процедура сериализации НабораЗаписей в JSON
    

Способ 2. Обработка текста JSON "на лету" (быстрый обходной путь)

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

Ключевой трюк — убрать информацию о типе. Как мы выяснили, проблема в свойстве "#type". Если его удалить, то 1С при чтении функцией ПрочитатьJSON() не будет пытаться создать строго типизированный объект базы данных. Вместо этого она создаст универсальную коллекцию — Структуру или Соответствие с вложенным Массивом структур. А с такой коллекцией уже можно работать гибко, сопоставляя поля по именам.

Посмотрим на пример:


// Получаем текст из файла JSON
ТекстJSON = ПолучитьТекстИзФайла(ПутьКФайлу);

// С помощью простой замены удаляем информацию о типе набора записей
// Имя регистра нужно подставить свое
ТекстJSON = СтрЗаменить(ТекстJSON, """", "#type"": ""InformationRegisterRecordSet.ИмяВашегоРегистра""");

// Также нужно удалить типы для каждой отдельной записи
ТекстJSON = СтрЗаменить(ТекстJSON, """", "#type"": ""InformationRegisterRecordManager.ИмяВашегоРегистра""");

// Теперь читаем "обезличенный" JSON
ПараметрыЧтенияJSON = Новый ПараметрыЧтенияJSON(,,Истина); // Истина - чтобы имена свойств были в формате CamelCase
ДанныеКакСтруктура = ПрочитатьJSON(ТекстJSON, ПараметрыЧтенияJSON);

// ДанныеКакСтруктура будет содержать массив записей, который можно обойти
НаборЗаписей = РегистрыСведений.ИмяВашегоРегистра.СоздатьНаборЗаписей();
// Устанавливаем отборы, если они есть в JSON
// ...

Для Каждого ЭлементМассива Из ДанныеКакСтруктура.value Цикл
    НоваяЗапись = НаборЗаписей.Добавить();
    // Теперь заполняем по именам, а не по порядку!
    ЗаполнитьЗначенияСвойств(НоваяЗапись, ЭлементМассива);
КонецЦикла;

НаборЗаписей.Записать();

Важный момент: использование регулярных выражений, как предлагали на форуме, — более мощный, но и более сложный вариант. Для простой задачи удаления строки с типом обычно достаточно функции СтрЗаменить(). Регулярные выражения могут понадобиться, если нужно не просто удалить тип, а переставить сами свойства местами в строке, что является гораздо более хрупким решением.

Способ 3. Переход на обмен через ТаблицуЗначений (самый надежный путь)

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

Алгоритм следующий:

  1. На стороне источника: Данные из регистра выгружаются не в НаборЗаписей, а сразу в ТаблицуЗначений.

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

  3. На стороне приемника: JSON-строка десериализуется обратно в ТаблицуЗначений.

  4. Создается пустой НаборЗаписей, и в него загружаются данные из полученной таблицы методом Загрузить(). Этот метод автоматически сопоставляет колонки таблицы с измерениями и ресурсами набора записей по их именам. Порядок колонок в таблице уже не имеет значения.

Этот подход решает проблему в корне, так как вся логика сопоставления по именам перекладывается на платформу, которая делает это эффективно и правильно.

Также в качестве альтернативы JSON можно рассмотреть встроенный механизм сериализации через ЗначениеВСтрокуВнутр() и ЗначениеИзСтрокиВнутр(). Он также работает с ТаблицейЗначений и не зависит от порядка колонок, но создает нечитаемый для человека формат данных.

Выводы

Итак, столкнувшись с ошибкой преобразования XDTO из-за разного порядка полей, у вас есть несколько вариантов действий:

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

← На главную