В противоположность предыдущим статьям, где были представлены примеры выгрузки в файл XML объектов конфигурации и, соответственно, эксплуатировались уже заложенные в 1С: Предприятие 8 схемы данных. В данной статье пойдет речь и будет представлен пример формирования методами 1С: Предприятие 8 XML-файла построенного, вообще говоря, по произвольной схеме данных. Предлагаемый в данной статье пример входит в состав прототипа обработки рассылки почтовых сообщений сотрудникам компании с расчетными листами заработной платы в качестве приложения. Поэтому, «www.spaming.org» в качестве URI пространства имен пусть вас не смущает. По мне, так любая массовая рассылка, пусть и полезная, с точки зрения отправителя, является спамом.
Настройка схемы данных XML в 1С: Предприятие 8
Конфигуратор 1С: Предприятие 8 снабжен редактором XML-схем файлов данных. Чтобы его открыть, нужно перейти в режим редактирования или создать новый пакет XDTO (XML Data Transfer Objects). Каждый пакет имеет уникальный в пределах конфигурации URI пространство имен, в данном случае «http://www.spaming.org», директивы импорта, если схема наследует объекты других схем и, собственно, описание самих типов объектов схемы.
Рис. 1 Схема в редакторе XDTO пакетов 1С: Предприятие 8.
В редакторе можно задавать свойства каждого из узлов схемы, в итоге, текстовое описание (XSD-файл) вышеуказанной схемы выглядит следующим образом:
<xs:schema xmlns:ns1="http://v8.1c.ru/8.1/data/core" xmlns:ns2="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:tns="http://www.spaming.org" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.spaming.org" attributeFormDefault="unqualified" elementFormDefault="qualified">
<xs:import namespace="http://v8.1c.ru/8.1/data/core"/>
<xs:import namespace="http://v8.1c.ru/8.1/data/enterprise/current-config"/>
<xs:complexType name="ИнтернетПочтовыйПрофиль">
<xs:sequence>
<xs:element name="АдресСервераSMTP" type="xs:string" minOccurs="0"/>
<xs:element name="АдресСервераPOP3" type="xs:string" minOccurs="0"/>
<xs:element name="АдресСервераIMAP" type="xs:string" minOccurs="0"/>
<xs:element name="ПортSMTP" type="xs:integer" minOccurs="0"/>
<xs:element name="ПортPOP3" type="xs:integer" minOccurs="0"/>
<xs:element name="ПортIMAP" type="xs:integer" minOccurs="0"/>
<xs:element name="Пользователь" type="xs:string" minOccurs="0"/>
<xs:element name="ПользовательIMAP" type="xs:string" minOccurs="0"/>
<xs:element name="ПользовательSMTP" type="xs:string" minOccurs="0"/>
<xs:element name="Пароль" type="xs:string" minOccurs="0"/>
<xs:element name="ПарольIMAP" type="xs:string" minOccurs="0"/>
<xs:element name="ПарольSMTP" type="xs:string" minOccurs="0"/>
<xs:element name="АутентификацияSMTP" type="ns2:EnumRef.СпособыSMTPАутентификации" minOccurs="0"/>
<xs:element name="ВремяОжидания" type="xs:integer" minOccurs="0"/>
<xs:element name="ИспользоватьSSLSMTP" type="xs:boolean" minOccurs="0"/>
<xs:element name="ИспользоватьSSLPOP3" type="xs:boolean" minOccurs="0"/>
<xs:element name="ИспользоватьSSLIMAP" type="xs:boolean" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="Рассылка">
<xs:sequence>
<xs:element name="Идентификатор" type="xs:string"/>
<xs:element name="Сообщения">
<xs:complexType>
<xs:sequence>
<xs:element name="Сообщение" type="tns:Сообщение" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="Сообщение">
<xs:sequence>
<xs:element name="Отправитель" type="xs:string"/>
<xs:element name="ИмяОтправителя" type="xs:string"/>
<xs:element name="Тема" type="xs:string"/>
<xs:element name="Текст" type="xs:string"/>
<xs:element name="Получатели" type="ns1:Array"/>
<xs:element name="Вложения" type="ns1:Array"/>
<xs:element name="Идентификатор" type="xs:string"/>
<xs:element name="Заголовок" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ФайлДанных">
<xs:sequence>
<xs:element name="ИнтернетПочтовыйПрофиль" type="tns:ИнтернетПочтовыйПрофиль"minOccurs="0" maxOccurs="1"/>
<xs:element name="Рассылки""minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element name="Рассылка" type="tns:Рассылка" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:schema>
Импорт XML-схемы в конфигурацию 1С: Предприятие 8
Если мы создали XML-схему данных с помощью конфигуратора, тогда, при сохранении изменений конфигурации, схема данных уже будет импортирована и доступна в любом месте программы:
ИнтернетПочтовыйПрофильТип = ФабрикаXDTO.Тип("http://www.spaming.org", "ИнтернетПочтовыйПрофиль");
ФабрикаXDTO — предопределенный объект конфигурации, набор схем которого содержит все схемы заданные в пакетах XDTO, плюс некоторые стандартные. Вместо этого объекта, 1С: Предприятие позволяет нам создать собственную фабрику и импортировать туда только те схемы данных, которые будем использовать:
// Производим чтение XML-схемы данных, записанной в XSD-файле в объект "ДокументDOM"
ЧтениеХМЛ = Новый ЧтениеXML;
ЧтениеХМЛ.ОткрытьФайл(ИмяФайла);
ПостроительДОМ = Новый ПостроительDOM;
ДокументDOM = ПостроительДОМ.Прочитать(ЧтениеХМЛ);
// Преобразование описания XML-схемы, представленной в объекте "ДокументDOM" в объект "СхемаXML"
ПостроительСхемXML = Новый ПостроительСхемXML;
СхемаXML = ПостроительСхемXML.СоздатьСхемуXML(ДокументDOM);
// Формирование набора XML-схем, на базе которого будет построена новая фабрика XDTO
НаборСхемXML = Новый НаборСхемXML;
НаборСхемXML.Добавить(СхемаXML);
// Импорт "зависимостей" из стандартной Фабрики XDTO для нашей новой фабрики в соответствии с директивами импорта
НаборСхемЭкспорт = ФабрикаXDTO.ЭкспортСхемыXML("http://v8.1c.ru/8.1/data/core");
Для Каждого СхемаXML Из НаборСхемЭкспорт Цикл
НаборСхемXML.Добавить(СхемаXML);
КонецЦикла;
НаборСхемЭкспорт = ФабрикаXDTO.ЭкспортСхемыXML("http://v8.1c.ru/8.1/data/enterprise/current-config");
Для Каждого СхемаXML Из НаборСхемЭкспорт Цикл
НаборСхемXML.Добавить(СхемаXML);
КонецЦикла;
// Собственно, создание фабрики
Фабрика = Новый ФабрикаXDTO(НаборСхемXML);
// Проверка работы механизма
ИнтернетПочтовыйПрофильТип = Фабрика.Тип("http://www.spaming.org", "ИнтернетПочтовыйПрофиль");
В общем случае, если нет необходимости подгружать XML-схему данных динамически, более целесообразно в плане простоты доступа, хранить эту схему непосредственно в конфигурации. Чтобы избежать необходимости анализа зависимостей импортируемой XML-схемы, можно перегружать все схемы из предопределенной фабрики XDTO. Добавить каким то образом XML-схему непосредственно в предопределенную фабрику XDTO не получится. Только через конфигурирование нового пакета XDTO с сохранением конфигурации.
Создание XML документа подчиненного заданной XML-схеме данных
Для простоты примера, будем предполагать, что XML-схема задана в пакетах XDTO. Тогда нижеуказанный код сформирует пример файла:
// Объект "ЗаписьXML" для формирования конечного XML-файла в строку
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку();
// Обращение по имени типа, при создании типа объекта "ФайлДанных", и URI пространства имен
ФайлДанныхТип = ФабрикаXDTO.Тип("http://www.spaming.org", "ФайлДанных");
ФайлДанных = ФабрикаXDTO.Создать(ФайлДанныхТип);
// Создание значения свойства по типу свойства, при этой, свойство само является объектом
ФайлДанных.Рассылки = ФабрикаXDTO.Создать(ФайлДанныхТип.Свойства.Получить("Рассылки").Тип);
РассылкаТип = ФабрикаXDTO.Тип("http://www.spaming.org", "Рассылка");
Рассылка = ФабрикаXDTO.Создать(РассылкаТип);
// Создание значения свойства по типу свойства, при этой, свойство само является примитивным типом - значением
Рассылка.Идентификатор = ФабрикаXDTO.Создать(РассылкаТип.Свойства.Получить("Идентификатор").Тип, "082017");
// Добавление элемента в список
ФайлДанных.Рассылки.Рассылка.Добавить(Рассылка);
// Запись результата в XML-файл
ФабрикаXDTO.ЗаписатьXML(ЗаписьXML, ФайлДанных);
// Вывод XML-файла в строку
Сообщить(ЗаписьXML.Закрыть());
Результатом которого будет следующее:
<ФайлДанных xmlns="http://www.spaming.org" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Рассылки>
<Рассылка>
<Идентификатор>082017</Идентификатор>
</Рассылка>
</Рассылки>
</ФайлДанных>
Проверка XML-файла на соответствие схеме
Проверить XML-файл на соответствие XML-схеме очень просто:
ЧтениеХМЛ = Новый ЧтениеXML;
ЧтениеХМЛ.ОткрытьФайл(БазаРассылки);
Попытка
// Попытка чтения XDTO-объекта из XML-файла
ФайлДанных = ФабрикаXDTO.ПрочитатьXML(ЧтениеХМЛ, ФабрикаXDTO.Тип("http://www.spaming.org", "ФайлДанных"));
Исключение
// Текст ошибки несоответствия формата XML-файла описанию XML-схемы
Сообщить(ОписаниеОшибки());
КонецПопытки;
В момент, когда фабрика производит чтение XML в объект XDTO, производится проверка файла на соответствие XML-схеме и конвертация значений и объектов, содержащихся в файле, в объекты XDTO. Если в описании некоторых типов объектов присутствуют типы, определенные для платформы или конфигурации 1С, тогда в момент чтения файла фабрикой XDTO происходит конвертация представленного в XML описания объекта в объект 1С.
Редактирование XML-файла в соответствии с заданной схемой данных
Редактирование XML-файла осуществляется тем же путем и так же просто, как и создание нового, с той лишь разницей, что процедуры изменения данных XML-файла должны придерживаться принципов целостности и безопасности данных. Попробуем добавить новый узел «Рассылка» с идентификатором «092017», предварительно проверив, что этим мы не нарушим уникальность элементов списка «Рассылки».
// Чтение текущих данных XML-файла
ЧтениеХМЛ = Новый ЧтениеXML;
ЧтениеХМЛ.ОткрытьФайл(БазаРассылки);
Попытка
// Создание объекта XDTO, чтение/конвертация данных всех его узлов
ФайлДанных = ФабрикаXDTO.ПрочитатьXML(ЧтениеХМЛ, ФабрикаXDTO.Тип("http://www.spaming.org", "ФайлДанных"));
Исключение
// Обработка ошибок
Сообщить(ОписаниеОшибки());
КонецПопытки;
// Флаг-индикатор того, что запись искомая существует
Найдена = Ложь;
ИдентификаторРассылки = "092017";
Для Каждого Рассылка Из ФайлДанных.Рассылки.Рассылка Цикл
Если Рассылка.Идентификатор = ИдентификаторРассылки Тогда
// Обработка найденной записи
Найдена = Истина;
КонецЕсли;
КонецЦикла;
Если НЕ Найдена Тогда
// Создание новой рассылки...
РассылкаТип = ФабрикаXDTO.Тип("http://www.spaming.org", "Рассылка");
Рассылка = ФабрикаXDTO.Создать(РассылкаТип);
Рассылка.Идентификатор = ФабрикаXDTO.Создать(РассылкаТип.Свойства.Получить("Идентификатор").Тип, ИдентификаторРассылки);
// ... добавление ее в список
ФайлДанных.Рассылки.Рассылка.Добавить(Рассылка);
КонецЕсли;
// Запись результирующего XML-файла
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку();
ФабрикаXDTO.ЗаписатьXML(ЗаписьXML, ФайлДанных);
Сообщить(ЗаписьXML.Закрыть());
Это был вариант с перебором элементов списка и, в данном примере, его применение вполне оправданно. В общем же случае, думаю, без запросов XPath нам не обойтись и ниже представлен пример, как действовать в данном случае.
// Чтение текущих данных XML-файла, на этот раз в DOM
ЧтениеХМЛ = Новый ЧтениеXML;
ЧтениеХМЛ.ОткрытьФайл(БазаРассылки);
ПостроительДОМ = Новый ПостроительDOM;
ДокументDOM = ПостроительДОМ.Прочитать(ЧтениеХМЛ);
// Временный объект для построения узлов-элементов DOM
ЗаписьУзловDOM = Новый ЗаписьУзловDOM;
ЗаписьУзловDOM.Открыть(ДокументDOM.ДокументВладелец);
// Родительский узел списка рассылок
УзелРассылки = ДокументDOM.ЭлементДокумента.ПолучитьЭлементыПоИмени("Рассылки")[0];
// Организация поиска узла методом XPath-запроса
ИдентификаторРассылки = "092017";
Разыменователь = Новый РазыменовательПространствИменDOM("ns", "http://www.spaming.org");
РезультатЗапроса = ДокументDOM.ВычислитьВыражениеXPath("//ns:Идентификатор[.='" + ИдентификаторРассылки + "']",
УзелРассылки, Разыменователь, ТипРезультатаDOMXPath.ЛюбойНеупорядоченныйУзел);
Узел = РезультатЗапроса.ПолучитьСледующий();
// Анализ результатов поиска
Если Узел = Неопределено Тогда
// Создание новой рассылки
РассылкаТип = ФабрикаXDTO.Тип("http://www.spaming.org", "Рассылка");
Рассылка = ФабрикаXDTO.Создать(РассылкаТип);
Рассылка.Идентификатор = ФабрикаXDTO.Создать(РассылкаТип
.Свойства.Получить("Идентификатор").Тип, ИдентификаторРассылки);
ФабрикаXDTO.ЗаписатьXML(ЗаписьУзловDOM, Рассылка);
УзелРассылки.ДобавитьДочерний(ЗаписьУзловDOM.УзелDOM);
КонецЕсли;
// Запись результирующего XML-файла
ЗаписьXMLСтрока = Новый ЗаписьXML;
ЗаписьXMLСтрока.УстановитьСтроку("UTF-8");
ЗаписьDOM = Новый ЗаписьDOM;
ЗаписьDOM.Записать(ДокументDOM, ЗаписьXMLСтрока);
Сообщить(ЗаписьXMLСтрока.Закрыть());
И в том и в другом случае, результатом будет следующий документ:
<?xml version="1.0" encoding="UTF-8"?>
<ФайлДанных xmlns="http://www.spaming.org" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Рассылки>
<Рассылка>
<Идентификатор>082017</Идентификатор>
</Рассылка>
<Рассылка>
<Идентификатор>092017</Идентификатор>
</Рассылка>
</Рассылки>
</ФайлДанных>
В итоге, как мы уже имели возможность при помощи представленных в статье примеров убедиться, в «лице» XDTO мы имеем достаточно мощный механизм, позволяющий организовать не только двусторонний обмен между базами, вообще говоря, различной конфигурации, но и, скажем, работу с небольшой базой данных в формате XML для нужд каких либо обработок.
Представленный набор примеров покрывает, быть может, 10% всех возможностей XDTO и, конечно, не является описанием единственно правильного взаимодействия с этими объектами. Тем не менее, это вполне рабочий набор примеров. При работе с XML, я сознательно избегаю таких методов, как: «ЗаписатьНачалоАтрибута» или «ЗаписатьНачалоЭлемента». Я не считаю это хорошей практикой использования XML. На сайте есть статья на эту тему. Вместо этого, я стараюсь «смотреть» на XML со стороны DOM (Data Object Model) и XDTO (XML Data Transfer Objects). Потому, как, с учетом наличия в 1С: Предприятие 8 поддержки XSL и XPath, это, безусловно, более перспективное занятие.