В практике разработки на платформе 1С:Предприятие часто возникает задача сравнения двух объектов данных, имеющих сложную иерархическую структуру. Это могут быть Структура, Соответствие или Массив, внутри которых находятся другие коллекции или примитивные типы. Типичный пример — обмен данными с внешним сайтом через JSON, когда необходимо понять, изменился ли объект с момента последней выгрузки, чтобы не передавать лишнюю информацию. Простое сравнение ссылок здесь не поможет, так как объекты создаются заново при каждом обмене. Разберем подробно, как реализовать эффективное и точное сравнение таких данных.
Начнем с того, почему классические операторы сравнения не подходят для сложных типов. В 1С выражение Структура1 = Структура2 сравнивает не содержимое объектов, а их ссылки в оперативной памяти. Даже если внутри структур находятся абсолютно идентичные наборы ключей и значений, платформа вернет Ложь, если это разные экземпляры объектов.
Рассмотрим также идею использования ЗначениеВСтрокуВнутр() или сериализации в JSON с последующим расчетом хеш-суммы. Проанализируем ситуацию: если порядок ключей в структуре или порядок элементов в массиве изменится (например, пришел массив [1, 2] вместо [2, 1]), строковые представления будут разными, хотя по смыслу данные идентичны. Следовательно, перед нами стоит задача не просто технического сравнения строк, а реализации алгоритма «глубокого равенства» (deep equal).
Если ваша конфигурация построена на базе БСП, то задача решается максимально быстро. Разработчики фирмы 1С уже предусмотрели стандартную функцию для рекурсивного сравнения коллекций. Проанализируем метод ДанныеСовпадают в общем модуле ОбщегоНазначения. Для быстрого поиска и изучения готовых функций подсистем можно использовать справочник по методам БСП с примерами.
Этот метод последовательно обходит все вложенные элементы. Если встречается структура, он проверяет наличие одинаковых ключей и рекурсивно вызывает сравнение для каждого значения. Если встречается массив — сравниваются его элементы. Рассмотрим, как это вызвать в коде:
// Пример использования БСП
РезультатСравнения = ОбщегоНазначения.ДанныеСовпадают(СтруктураОбъекта1, СтруктураОбъекта2);
Если РезультатСравнения Тогда
Сообщить("Данные идентичны");
Иначе
Сообщить("Найдены различия");
КонецЕсли;
Важный нюанс: стандартный метод БСП чувствителен к порядку элементов в массивах. Если для вашей логики массивы [1, 2] и [2, 1] должны считаться одинаковыми, этот метод потребует предварительной подготовки данных (сортировки).
В случаях, когда БСП не используется или требуется специфическая логика (например, игнорирование определенных ключей), напишем собственную функцию. Разберем по шагам логику рекурсивного обхода:
Посмотрим на пример реализации упрощенной версии такого сравнения:
Функция ЗначенияРавны(Знач Значение1, Знач Значение2) Экспорт
Тип1 = ТипЗнч(Значение1);
Тип2 = ТипЗнч(Значение2);
Если Тип1 <> Тип2 Тогда
Возврат Ложь;
КонецЕсли;
Если Тип1 = Тип("Структура") Тогда
Если Значение1.Количество() <> Значение2.Количество() Тогда
Возврат Ложь;
КонецЕсли;
Для каждого Элемент Из Значение1 Цикл
ЗначениеКлюча2 = Неопределено;
Если Не Значение2.Свойство(Элемент.Ключ, ЗначениеКлюча2) Тогда
Возврат Ложь;
КонецЕсли;
Если Не ЗначенияРавны(Элемент.Value, ЗначениеКлюча2) Тогда
Возврат Ложь;
КонецЕсли;
КонецЦикла;
Возврат Истина;
ИначеЕсли Тип1 = Тип("Массив") Тогда
Если Значение1.Количество() <> Значение2.Количество() Тогда
Возврат Ложь;
КонецЕсли;
Для Индекс = 0 По Значение1.ВходящийГраница() Цикл
Если Не ЗначенияРавны(Значение1[Индекс], Значение2[Индекс]) Тогда
Возврат Ложь;
КонецЕсли;
КонецЦикла;
Возврат Истина;
Иначе
// Сравнение примитивных типов
Возврат Значение1 = Значение2;
КонецЕсли;
КонецФункции
Выясним причину, по которой прямое хеширование JSON часто дает ложные результаты. Это происходит из-за неопределенного порядка ключей в структурах 1С и произвольного порядка элементов в массивах, приходящих из внешних систем. Чтобы решить эту проблему, применим метод «канонизации».
Для структур мы можем создать вспомогательную функцию, которая выгружает ключи в СписокЗначений, сортирует их по алфавиту и пересобирает структуру в строгом порядке. Для массивов ситуация сложнее: если в массиве лежат простые числа или строки, их легко отсортировать методом Сортировать(). Если же в массиве лежат вложенные структуры, проанализируем следующий подход:
Рассчитаем для каждой вложенной структуры её строковое представление (например, через тот же JSON или ЗначениеВСтрокуВнутр), добавим это представление как временное поле в структуру, перенесем элементы массива в ТаблицуЗначений, отсортируем ТЗ по этому полю и вернем элементы обратно в массив. Теперь, когда данные приведены к единому порядку, мы можем смело использовать MD5 или другой алгоритм хеширования для мгновенного сравнения больших объемов данных. Для расчета криптографических хешей можно использовать внешнюю компоненту вычисления хеш-функций.
Если задача стоит не просто в определении факта изменения («Да/Нет»), а в поиске того, что именно изменилось, рассмотрим метод выгрузки данных в ТаблицуЗначений.
Создадим ТЗ с колонками ПутьКДанным (например, "Заказ.Товары[0].Цена") и Значение. Рекурсивно обойдем структуру и заполним таблицу. Сделав это для обоих объектов, мы можем использовать метод Запрос с полным соединением двух временных таблиц по полю ПутьКДанным. Это позволит нам за один проход увидеть все добавленные, удаленные и измененные поля. Этот метод крайне эффективен при отладке сложных интеграций.
При сравнении данных учитывайте следующие особенности платформы:
Число (целое) и Число (дробное) так, как это делает 1С. Также даты в JSON передаются строками. При сравнении всегда приводите значения к одному типу.Таким образом, выбор метода зависит от ваших целей. Для быстрой проверки в типовых конфигурациях используйте ОбщегоНазначения.ДанныеСовпадают. Для сложных обменов с внешними системами внедряйте канонизацию данных и хеширование, а для детального анализа изменений — рекурсивный обход с формированием дерева расхождений.