Интеграция систем «1С:Предприятие» со сторонним программным обеспечением (веб-сайтами, мобильными приложениями, внешними базами данных SQL) — одна из наиболее частых задач разработчика. Когда встает вопрос обмена по строгой схеме XML/JSON, стандартным инструментом выступает технология XDTO (XML Data Transfer Objects). Однако ручное описание соответствий каждого реквизита в коде превращается в рутину, а использование сложных форматов вроде EnterpriseData в нетиповых конфигурациях вызывает массу вопросов.
В этой статье мы подробно разберем, как уйти от монотонного ручного заполнения свойств XDTO, как адаптировать технологии «1С» для нетиповых конфигураций, рассмотрим современные подходы с использованием JSON и решим частую проблему с пространствами имен при XSLT-преобразованиях.
---
При использовании фабрики XDTO стандартный подход «в лоб» требует от разработчика явного присвоения каждого свойства. Если объектов десятки, а реквизитов сотни, код превращается в бесконечную простыню. Для упрощения этой задачи мы можем использовать универсальные алгоритмы обхода метаданных и свойств пакета.
Проанализируем ситуацию: физическая структура хранения данных в источнике (например, в сторонней SQL-базе) и приемнике (1С) различается, но имена полей в XML-схеме (XSD) зачастую сопоставимы с реквизитами объектов 1С или ключами структуры-источника. Мы можем написать универсальную рекурсивную функцию заполнения объекта XDTO:
Функция ЗаполнитьОбъектXDTO(ИсточникДанных, ТипXDTO) Экспорт
// Создаем объект XDTO по переданному типу
НовыйОбъектXDTO = ФабрикаXDTO.Создать(ТипXDTO);
// Обходим свойства типа XDTO
Для Каждого Свойство Из ТипXDTO.Свойства Цикл
ИмяСвойства = Свойство.Имя;
// Проверяем наличие значения в источнике (например, в Структуре или Выборке)
ЗначениеИсточника = Неопределено;
Если ТипЗнч(ИсточникДанных) = Тип("Структура") И ИсточникДанных.Свойство(ИмяСвойства) Тогда
ЗначениеИсточника = ИсточникДанных[ИмяСвойства];
ИначеЕсли Найти(ТипЗнч(ИсточникДанных), "Выборка") > 0 Тогда
Попытка
ЗначениеИсточника = ИсточникДанных[ИмяСвойства];
Исключение
Продолжить; // Свойство отсутствует в выборке
КонецПопытки;
КонецЕсли;
Если ЗначениеИсточника = Неопределено Тогда
Продолжить;
КонецЕсли;
// Проверяем, является ли свойство простым типом
Если Свойство.Тип.ТипКонтекста = ТипКонтекстаXDTO.Простой Тогда
// Если это ссылка 1С, выгружаем ее уникальный идентификатор (GUID)
Если Справочники.ТипВсеСсылки().СодержитТип(ТипЗнч(ЗначениеИсточника))
Или Документы.ТипВсеСсылки().СодержитТип(ТипЗнч(ЗначениеИсточника)) Тогда
НовыйОбъектXDTO[ИмяСвойства] = Строка(ЗначениеИсточника.УникальныйИдентификатор());
Иначе
НовыйОбъектXDTO[ИмяСвойства] = ЗначениеИсточника;
EndIf;
Иначе
// Если свойство представляет собой вложенный объект XDTO (сложный тип)
НовыйОбъектXDTO[ИмяСвойства] = ЗаполнитьОбъектXDTO(ЗначениеИсточника, Свойство.Тип);
КонецЕсли;
КонецЦикла;
Возврат НовыйОбъектXDTO;
КонецФункции
Использование такого подхода сокращает объем кода интеграции на 80%. Также обратите внимание на Библиотеку стандартных подсистем (БСП). В модуле ОбменДаннымиXDTOСервер содержатся готовые функции преобразования, такие как ОбъектXDTOВСтруктуру(), которые позволяют быстро преобразовывать полученные XML-данные в привычные структуры 1С без ручного разбора дерева XML.
---
Существует распространенное заблуждение, что технология EnterpriseData (ED) применима только для крупных типовых решений (ERP, КА, УТ). Однако, если сторонний разработчик готов формировать файлы по стандарту EnterpriseData, вы можете развернуть этот механизм и в самописной базе.
Выясним, как это сделать с минимальными затратами:
AdditionalInfo (Дополнительные сведения). Оно присутствует во всех ключевых типах EnterpriseData и имеет тип AnyType, что позволяет упаковывать туда произвольные структуры данных без изменения общей XSD-схемы.---
Еще одно частое заблуждение заключается в том, что программа «1С:Конвертация данных 3.0» жестко привязана только к типовому EnterpriseData. Это не так.
Вы можете спроектировать собственный облегченный XSD-пакет под нужды вашей уникальной интеграции, загрузить его в КД 3.0 в качестве конфигурации-приемника или источника и визуально настроить правила конвертации объектов (ПКО) и правила обработки данных (ПОД). Это полностью избавит вас от необходимости писать код чтения/записи XML вручную на стороне 1С.
---
Если внешняя система разработана на современных веб-технологиях (Node.js, Python, PHP), ее разработчикам будет гораздо комфортнее работать с форматом JSON, нежели с перегруженным XML. Платформа 1С позволяет использовать настроенные пакеты XDTO для работы с JSON, сохраняя строгую типизацию схемы.
Рассмотрим пример, как быстро сериализовать объект XDTO в JSON-строку:
// Создаем запись JSON
ЗаписьJSON = Новый ЗаписьJSON;
ПараметрыЗаписи = Новый ПараметрыЗаписиJSON(,,, ЭлементыАннотацииJSON.Использовать);
ЗаписьJSON.УстановитьСтроку(ПараметрыЗаписи);
// Записываем объект XDTO в JSON с использованием сериализатора
СериализаторXDTO.ЗаписатьJSON(ЗаписьJSON, НашОбъектXDTO, НазначениеXMLНоменклатуры.СопоставлениеТипов);
СтрокаJSON = ЗаписьJSON.Закрыть();
// СтрокаJSON готова для отправки через HTTP-запрос внешнему ПО
Внешнее ПО отправляет вам стандартный JSON, а вы на стороне 1С мгновенно восстанавливаете его в строго типизированный объект XDTO с помощью метода СериализаторXDTO.ПрочитатьJSON(). Это решает проблему валидации данных на лету.
---
Часто при обмене со сторонним ПО требуется преобразовать XML из формата 1С в структуру сторонней системы (или наоборот). Для этого применяется технология XSLT-преобразований. На практике разработчики часто сталкиваются с ситуацией, когда шаблон XSLT не находит элементы в исходном XML (например, уникальный идентификатор Ref или пометку удаления DeletionMark), возвращая пустой результат.
Выясним причину этой проблемы. Рассмотрим пример исходного XML-файла:
<?xml version="1.0" encoding="utf-8"?>
<DocumentObject.ЗаказПокупателя xmlns="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Ref>377b50c4-edf5-11e6-9306-0cc47a067dfb</Ref>
<DeletionMark>false</DeletionMark>
</DocumentObject.ЗаказПокупателя>
Обратите внимание на атрибут xmlns="http://v8.1c.ru/8.1/data/enterprise/current-config". Он задает пространство имен по умолчанию. Согласно спецификации XPath 1.0 (которая используется процессором 1С), любые элементы в XML, находящиеся внутри пространства имен по умолчанию, не могут быть выбраны в XPath-выражениях по их простому имени (например, через select="./Ref") без явного указания префикса.
Для успешного преобразования нам необходимо явно объявить префикс пространства имен 1С в самом XSL-шаблоне и использовать его при выборке узлов. Разберем правильный, рабочий шаблон преобразования:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:v8="http://v8.1c.ru/8.1/data/enterprise/current-config"
exclude-result-prefixes="v8">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- Шаблон для корневого узла -->
<xsl:template match="/">
<xsl:apply-templates select="v8:DocumentObject.ЗаказПокупателя"/>
</xsl:template>
<!-- Шаблон преобразования документа -->
<xsl:template match="v8:DocumentObject.ЗаказПокупателя">
<DocumentObject.Задание xmlns="http://v8.1c.ru/8.1/data/enterprise/current-config"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- Выбираем элементы, обязательно используя префикс v8 -->
<xsl:copy-of select="v8:Ref" />
<xsl:copy-of select="v8:DeletionMark" />
<!-- Добавляем новые статические или расчетные узлы -->
<Date>2017-02-08T14:53:14</Date>
</DocumentObject.Задание>
</xsl:template>
</xsl:stylesheet>
Использование директивы exclude-result-prefixes="v8" гарантирует, что временный префикс v8 не перейдет в результирующий XML-файл, благодаря чему мы получим чистую и валидную структуру на выходе.
Применяя данные подходы, мы переводим интеграцию на качественно новый уровень: уходим от ручного написания кода парсинга, повышаем отказоустойчивость обменов и обеспечиваем простоту интеграции со сторонними разработчиками.