При работе с внешними системами и интеграциями мы часто сталкиваемся с необходимостью обмениваться данными в формате JSON. В 1С:Предприятии для десериализации (чтения) JSON-строк в объекты мы используем функцию ПрочитатьJSON(). Однако иногда возникает специфическая проблема, когда имена свойств в JSON-объекте начинаются с цифр, например, "32730": {}. В таком случае, при попытке прочитать этот JSON в стандартный объект Структура 1С, мы получаем ошибку: "Недопустимое имя свойства: '32730' для чтения JSON в объект Структура". Давайте выясним, почему это происходит и как легко и эффективно решить эту проблему, обеспечив успешное чтение и обработку данных.
Причина возникновения ошибки кроется в фундаментальных различиях между правилами именования свойств в стандарте JSON и правилами именования полей объектов типа Структура в 1С:Предприятии. Стандарт JSON крайне гибок и позволяет использовать в качестве ключей любые строки, включая те, что начинаются с цифр или содержат специальные символы. Например, JSON-объект {"123Key": "Value", "another-key": 100} является абсолютно корректным.
Однако в 1С существуют строгие ограничения на то, как должны называться поля у объектов типа Структура. Имена полей в Структуре должны начинаться с буквы или символа подчеркивания и не могут содержать некоторые специальные символы. Это требование аналогично правилам именования переменных во многих языках программирования. Таким образом, когда функция ПрочитатьJSON() пытается преобразовать JSON-ключ "32730" в поле Структуры, она сталкивается с нарушением этого правила и выдает соответствующую ошибку. По сути, 1С не может создать поле с именем, которое начинается с цифры, в своем внутреннем представлении Структуры.
Несмотря на это ограничение, существуют надежные и эффективные методы для успешного чтения таких JSON-файлов в 1С. Мы рассмотрим несколько подходов, начиная от самых простых и рекомендованных, до более продвинутых и гибких.
Наиболее рекомендуемый, простой и эффективный способ справиться с проблемой числовых ключей в JSON — это использовать второй необязательный булев параметр функции ПрочитатьJSON(). Этот параметр, ПрочитатьВСоответствие, позволяет нам указать 1С, что JSON-объекты следует десериализовать в объекты типа Соответствие вместо стандартных Структур.
Что такое "Соответствие" в 1С и почему оно подходит?
Объект Соответствие в 1С представляет собой коллекцию пар "ключ-значение", по своей сути аналогичную словарям, хеш-таблицам или ассоциативным массивам в других языках программирования. Ключом в Соответствии может выступать строка практически любого содержания, включая те, которые начинаются с цифр или содержат специальные символы. Эта особенность делает Соответствие идеальным контейнером для данных, полученных из JSON, где имена свойств могут быть весьма разнообразными и не соответствовать жестким правилам именования полей Структуры 1С.
Когда мы передаем Истина в качестве второго параметра функции ПрочитатьJSON(), мы явно указываем системе: "Все JSON-объекты в этом файле или строке должны быть преобразованы в объекты Соответствие". Таким образом, 1С не будет пытаться создать Структуру с "недопустимыми" именами полей, а вместо этого создаст Соответствие, которое прекрасно справляется с такими ключами.
Давайте рассмотрим пошаговую инструкцию и пример кода для реализации этого решения:
ЧтениеJSON и установка в него исходной JSON-строки или открытие JSON-файла. Это стандартная процедура для любого чтения JSON в 1С.ПрочитатьJSON() с параметром Истина: Мы вызываем функцию глобального контекста ПрочитатьJSON(), передавая ей созданный объект ЧтениеJSON в качестве первого параметра, и самое главное — значение Истина в качестве второго параметра ПрочитатьВСоответствие.Соответствие. Теперь мы можем обращаться к значениям по их ключам, используя синтаксис квадратных скобок (Соответствие["Ключ"]), что является стандартным способом работы с этим типом объекта.Рассмотрим конкретный пример на основе JSON-структуры, вызвавшей проблему:
// Предположим, что мы имеем JSON-текст с проблемными ключами,
// где объект "list" содержит вложенные объекты с числовыми именами свойств
ТекстJSON = "{
\"list\": {
\"32730\": {
\"id\": \"32730\",
\"parent\": \"88\",
\"create_date\": \"2021-09-10 11:16:49\",
\"modify_date\": \"2021-11-07 14:04:39\",
\"sku\": \"KDA067-2007\"
},
\"32731\": {
\"id\": \"32731\",
\"parent\": \"89\",
\"create_date\": \"2021-09-10 11:17:00\",
\"modify_date\": \"2021-11-07 14:05:00\",
\"sku\": \"KDA068-2008\"
}
},
\"meta\": {
\"total\": 2,
\"status\": \"success\"
}
}";
// Создаем объект для чтения JSON
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(ТекстJSON); // Или ЧтениеJSON.ОткрытьФайл("путь/к/файлу.json");
Попытка
// **Ключевой момент:** передаем Истина в качестве второго параметра.
// Это указывает 1С прочитать JSON-объекты в Соответствие.
Данные = ПрочитатьJSON(ЧтениеJSON, Истина);
Сообщить("JSON успешно прочитан в Соответствие.");
// Теперь мы можем безопасно обращаться к данным, используя ключи-строки.
// Проверяем наличие ключа "list"
Если Данные.Свойство("list") Тогда
// Получаем значение по ключу "list", это будет еще одно Соответствие
СписокЭлементов = Данные["list"];
Сообщить("Тип объекта 'list': " + ТипЗнч(СписокЭлементов));
// Проверяем наличие проблемного ключа "32730"
Если СписокЭлементов.Свойство("32730") Тогда
// Получаем вложенный объект по ключу "32730", это также будет Соответствие
Элемент32730 = СписокЭлементов["32730"];
Сообщить("Тип объекта '32730': " + ТипЗнч(Элемент32730));
// Извлекаем значения его свойств
Идентификатор = Элемент32730["id"];
Родитель = Элемент32730["parent"];
ДатаСоздания = Элемент32730["create_date"];
Артикул = Элемент32730["sku"];
Сообщить("--- Детали элемента '32730' ---");
Сообщить("ID: " + Идентификатор);
Сообщить("Parent: " + Родитель);
Сообщить("Create Date: " + ДатаСоздания);
Сообщить("SKU: " + Артикул);
Иначе
Сообщить("Ключ '32730' не найден в 'list'.");
КонецЕсли;
// Можно также получить данные для "meta", которые также будут Соответствием
Если Данные.Свойство("meta") Тогда
МетаДанные = Данные["meta"];
ОбщееКоличество = МетаДанные["total"];
Статус = МетаДанные["status"];
Сообщить("--- Метаданные ---");
Сообщить("Total: " + ОбщееКоличество);
Сообщить("Status: " + Статус);
КонецЕсли;
Иначе
Сообщить("Ключ 'list' не найден в корневом объекте JSON.");
КонецЕсли;
Исключение
Сообщить("Ошибка при чтении JSON: " + ОписаниеОшибки());
КонецПопытки;
// Обязательно закрываем объект ЧтениеJSON
ЧтениеJSON.Закрыть();
Важный аспект: при использовании параметра ПрочитатьВСоответствие = Истина, все JSON-объекты во всей структуре, независимо от глубины вложенности, будут преобразованы в объекты Соответствие. Это относится как к объектам с "некорректными" числовыми ключами, так и к объектам, чьи ключи могли бы быть успешно преобразованы в поля Структуры. Такая единообразная обработка упрощает дальнейшую работу с десериализованными данными, поскольку вы всегда знаете, что имеете дело с Соответствием.
Использование JSONВЗначение из БСП: Если в вашей конфигурации используется Библиотека Стандартных Подсистем (БСП), вы, вероятно, найдете там функцию JSONВЗначение или аналогичные утилиты для работы с JSON. Эти функции часто являются обертками над стандартной ПрочитатьJSON() и по умолчанию настроены на преобразование JSON-объектов в Соответствия, тем самым автоматически обходя проблему с некорректными именами свойств Структуры. Мы рекомендуем проверять наличие таких готовых функций в БСП, так как они могут значительно упростить и стандартизировать работу с JSON.
В некоторых сценариях, если по каким-либо причинам мы абсолютно не можем использовать объекты Соответствие (например, если существует строгая архитектурная зависимость от Структур), или если количество проблемных ключей очень ограничено, статично и предсказуемо, мы можем применить метод предварительной текстовой обработки JSON-строки. Суть этого подхода заключается в том, чтобы модифицировать JSON-текст перед его передачей функции ПрочитатьJSON(), заменяя "недопустимые" имена свойств на "допустимые".
Например, мы можем принять решение, что все числовые ключи будут иметь префикс, скажем, символ подчеркивания (_). Таким образом, ключ "32730" будет преобразован в "_32730". После такой замены 1С уже сможет успешно прочитать JSON в Структуру, поскольку "_32730" является абсолютно корректным именем поля для Структуры.
Для выполнения такой замены нам потребуется прочитать весь JSON как обычную строку и использовать методы для работы со строками, такие как СтрЗаменить() или более мощный объект РегулярноеВыражение, для поиска и модификации проблемных ключей.
Давайте разберем пример использования регулярных выражений для автоматической замены таких ключей:
// Исходный JSON-текст с проблемными ключами
ТекстJSON = "{
\"list\": {
\"32730\": {
\"id\": \"32730\",
\"parent\": \"88\",
\"create_date\": \"2021-09-10 11:16:49\"
},
\"32731\": {
\"id\": \"32731\",
\"parent\": \"89\",
\"create_date\": \"2021-09-10 11:17:00\"
}
},
\"meta\": {
\"total\": 2,
\"status\": \"success\"
},
\"data\": {
\"user_id\": \"12345\",
\"category\": \"electronics\"
},
\"prices\": {
\"item_1\": 100,
\"item_2\": 200
}
}";
// Создаем объект для регулярных выражений.
// Мы ищем шаблон: кавычка, одна или более цифр (захватываем их в группу), кавычка, двоеточие.
// Например, "32730":
// (\d+) - это захватывающая группа, содержащая цифры.
// Это позволит нам использовать эти цифры в строке замены.
РегВыражение = Новый РегулярноеВыражение("\"(\d+)\":");
// Выполняем замену:
// В строке замены "$_1"
// $1 - это ссылка на первую захваченную группу (в нашем случае - сами цифры)
// Таким образом, "32730": будет заменено на "_32730":
ИзмененныйТекстJSON = РегВыражение.Заменить(ТекстJSON, "\"_$1\":");
Сообщить("--- Исходный JSON ---");
Сообщить(ТекстJSON);
Сообщить("--- Измененный JSON ---");
Сообщить(ИзмененныйТекстJSON);
Попытка
// Теперь пытаемся прочитать измененный JSON
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(ИзмененныйТекстJSON);
// Читаем JSON в Структуру (по умолчанию, т.к. второй параметр не указан или равен Ложь)
ДанныеСтруктура = ПрочитатьJSON(ЧтениеJSON);
Сообщить("Измененный JSON успешно прочитан в Структуру.");
// Доступ к данным в Структуре теперь будет осуществляться через новые имена полей
// Например, вместо "32730" используем "_32730"
Если ДанныеСтруктура.Свойство("list") Тогда
СписокЭлементовСтруктура = ДанныеСтруктура.list; // Доступ как к полю Структуры
Сообщить("Тип объекта 'list' после чтения в Структуру: " + ТипЗнч(СписокЭлементовСтруктура));
Если СписокЭлементовСтруктура.Свойство("_32730") Тогда
Элемент32730Структура = СписокЭлементовСтруктура._32730; // Доступ к полю Структуры
Сообщить("Тип объекта '_32730' после чтения в Структуру: " + ТипЗнч(Элемент32730Структура));
// Извлекаем значения свойств вложенной структуры
ИдентификаторСтруктура = Элемент32730Структура.id;
РодительСтруктура = Элемент32730Структура.parent;
Сообщить("--- Детали элемента '_32730' из Структуры ---");
Сообщить("ID: " + ИдентификаторСтруктура);
Сообщить("Parent: " + РодительСтруктура);
Иначе
Сообщить("Ключ '_32730' не найден в 'list'.");
КонецЕсли;
Иначе
Сообщить("Ключ 'list' не найден в корневой Структуре.");
КонецЕсли;
Исключение
Сообщить("Ошибка при чтении измененного JSON: " + ОписаниеОшибки());
КонецПопытки;
ЧтениеJSON.Закрыть();
Преимущества данного подхода:
Структура: Если ваша логика кода жестко завязана на работу со Структурами и вы не хотите переписывать ее под Соответствия, этот метод позволяет сохранить привычный способ доступа к данным (через точку: Объект.Поле).Недостатки и риски:
{"code": "12345"}). В таких случаях, неправильно составленное выражение может повредить данные.В целом, предварительная обработка строки является жизнеспособным, но менее универсальным решением по сравнению с чтением в Соответствие. Ее следует использовать с осторожностью и только в случаях, когда преимущества использования Структуры перевешивают потенциальные риски и сложности.
Для случаев, требующих максимально глубокого и гибкого контроля над процессом десериализации JSON, функция ПрочитатьJSON() предоставляет третий необязательный параметр — функцию восстановления (также известную как "reviver function"). Этот подход позволяет нам определить пользовательскую функцию, которая будет автоматически вызываться интерпретатором 1С для каждого объекта или массива, обнаруженного в JSON-структуре, в процессе его преобразования.
Как работает функция восстановления?
Когда мы передаем имя нашей функции восстановления в ПрочитатьJSON(), 1С будет выполнять следующие действия:
Ключ: Строковое имя текущего свойства (для объектов) или индекс (для массивов).Значение: Десериализованное значение текущего свойства.Контекст: Дополнительная информация о текущем состоянии десериализации (например, путь к текущему элементу, родительский объект и т.д.). Этот параметр позволяет принимать решения в зависимости от положения элемента в JSON-иерархии.Неопределено, то это свойство будет удалено из результирующего объекта.Мы можем использовать эту функцию для динамического преобразования или переименования ключей, которые 1С не может автоматически преобразовать в поля Структуры. В нашей функции восстановления мы можем проверять, начинается ли Ключ с цифры, и если да, то модифицировать его (например, добавив префикс, как в предыдущем решении), перед тем как вернуть измененный объект. Кроме того, этот механизм позволяет выполнять более сложную логику, такую как преобразование типов данных, фильтрация нежелательных полей или валидация данных.
Рассмотрим концептуальный пример, демонстрирующий, как можно использовать функцию восстановления для переименования проблемных ключей. Важно отметить, что в этом подходе мы можем пытаться прочитать в Структуру (передав Ложь во втором параметре ПрочитатьВСоответствие) и позволить функции восстановления подготовить имена ключей к этому:
// Это функция восстановления, которую мы определим.
// Она будет вызываться для каждого объекта в процессе десериализации JSON.
Функция МояФункцияВосстановленияJSON(Ключ, Значение, Контекст)
// Примечание: Контекст может содержать полезную информацию, но для простоты
// в данном примере мы его не используем явно.
// Эта функция вызывается как для примитивных значений, так и для объектов/массивов.
// Если Значение является Соответствием (поскольку 1С сначала пытается создать Соответствие
// или Структуру для объектов, прежде чем передать их в функцию восстановления),
// мы можем обработать его ключи.
// Если мы передаем Ложь во втором параметре ПрочитатьJSON, 1С будет стараться
// построить Структуру, и функция восстановления получит возможность
// изменить ключи, которые иначе вызвали бы ошибку.
Если ТипЗнч(Значение) = Тип("Соответствие") Тогда
// Создаем новое Соответствие, чтобы аккумулировать переименованные ключи и их значения.
НовоеЗначение = Новый Соответствие;
Для Каждого Элемент Из Значение Цикл
ПереименованныйКлюч = Элемент.Ключ;
// Проверяем, начинается ли ключ с цифры.
// Применяем проверку только к непустым ключам.
Если ПереименованныйКлюч <> "" И Лев(ПереименованныйКлюч, 1) В "0123456789" Тогда
// Если ключ начинается с цифры, добавляем префикс '_'
ПереименованныйКлюч = "_" + ПереименованныйКлюч;
КонецЕсли;
// Вставляем значение с (возможно) переименованным ключом.
// Значение, которое здесь вставляется, уже могло быть обработано
// этой же функцией восстановления, если оно было вложенным объектом.
НовоеЗначение.Вставить(ПереименованныйКлюч, Элемент.Значение);
КонецЦикла;
// Возвращаем новое Соответствие. 1С попытается преобразовать его в Структуру,
// если в ПрочитатьJSON было указано Ложь для ПрочитатьВСоответствие.
Возврат НовоеЗначение;
ИначеЕсли ТипЗнч(Значение) = Тип("Массив") Тогда
// Если это массив, обрабатываем его элементы, если необходимо.
// В данном примере оставляем массив как есть.
Возврат Значение;
Иначе
// Если это примитивное значение (строка, число, булево и т.д.),
// возвращаем его как есть.
Возврат Значение;
КонецЕсли;
КонецФункции
// ... далее в основном коде, где происходит чтение JSON ...
// Предположим, что JSON-строка уже получена
ТекстJSONСПроблемой = "{
\"list\": {
\"32730\": {
\"id\": \"32730\",
\"parent\": \"88\"
},
\"45678\": {
\"id\": \"45678\",
\"name\": \"ItemX\"
}
},
\"metadata\": {
\"count\": 2
}
}";
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(ТекстJSONСПроблемой);
Попытка
// Вызываем ПрочитатьJSON с функцией восстановления.
// Второй параметр (ПрочитатьВСоответствие) установлен в Ложь,
// чтобы 1С попыталась создать Структуру, после того как
// функция восстановления изменит имена ключей.
ДанныеПослеОбработки = ПрочитатьJSON(ЧтениеJSON, Ложь, МояФункцияВосстановленияJSON);
Сообщить("JSON успешно прочитан с использованием функции восстановления.");
// Теперь ДанныеПослеОбработки будет содержать Структуру
// с ключами типа "_32730", "_45678"
Если ТипЗнч(ДанныеПослеОбработки) = Тип("Структура") Тогда
Сообщить("Корневой объект прочитан как Структура.");
Если ДанныеПослеОбработки.Свойство("list") Тогда
СписокОбработанный = ДанныеПослеОбработки.list; // Доступ к полю Структуры
Если ТипЗнч(СписокОбработанный) = Тип("Структура") Тогда
Сообщить("Объект 'list' также прочитан как Структура.");
Если СписокОбработанный.Свойство("_32730") Тогда
ЭлементОбработанный32730 = СписокОбработанный._32730;
Сообщить("Элемент '_32730' доступен: ID=" + ЭлементОбработанный32730.id + ", Parent=" + ЭлементОбработанный32730.parent);
КонецЕсли;
Если СписокОбработанный.Свойство("_45678") Тогда
ЭлементОбработанный45678 = СписокОбработанный._45678;
Сообщить("Элемент '_45678' доступен: ID=" + ЭлементОбработанный45678.id + ", Name=" + ЭлементОбработанный45678.name);
КонецЕсли;
Иначе
Сообщить("Объект 'list' прочитан как " + ТипЗнч(СписокОбработанный) + ". Ожидалась Структура.");
КонецЕсли;
КонецЕсли;
Если ДанныеПослеОбработки.Свойство("metadata") Тогда
МетаданныеОбработанные = ДанныеПослеОбработки.metadata;
Сообщить("Метаданные: Count=" + МетаданныеОбработанные.count);
КонецЕсли;
Иначе
Сообщить("Корневой объект прочитан как " + ТипЗнч(ДанныеПослеОбработки) + ". Ожидалась Структура.");
КонецЕсли;
Исключение
Сообщить("Ошибка при чтении JSON с функцией восстановления: " + ОписаниеОшибки());
КонецПопытки;
ЧтениеJSON.Закрыть();
Преимущества этого продвинутого метода:
Недостатки:
Данный подход рекомендуется для очень специфических задач, где требуется тонкая настройка процесса десериализации, или для работы с крайне сложными и динамически меняющимися JSON-структурами, где простые методы уже не справляются.
Мы рассмотрели несколько эффективных способов решения проблемы чтения JSON-файлов в 1С, когда имена свойств начинаются с цифр. Выбор наиболее подходящего решения будет зависеть от ваших конкретных требований и контекста задачи:
Соответствие.Соответствие, которые идеально подходят для представления произвольных JSON-объектов, выбирайте это решение. Оно минимизирует риски ошибок и требует наименьшего объема кода. Помните, что все вложенные объекты также будут преобразованы в Соответствия, что обеспечивает единообразие.Структуры и проблема ограничена: Предварительная обработка JSON-строки.Структуру, и вы уверены, что проблемные ключи предсказуемы и их количество ограничено, вы можете использовать текстовую замену. Однако будьте крайне внимательны при составлении регулярных выражений, чтобы избежать непреднамеренного повреждения данных. Этот метод требует большего контроля и может быть менее надежным при изменениях в структуре JSON.В заключение, не позволяйте "недопустимым" именам свойств JSON остановить ваши интеграции. 1С предоставляет достаточно гибкие инструменты для работы с различными форматами данных. Выбрав правильный подход, мы можем эффективно обрабатывать JSON-файлы любой сложности, обеспечивая бесперебойную работу наших систем.