При работе с внешними XML-документами в 1С часто возникает задача не только прочитать данные с использованием механизма XDTO, но и внести в них изменения, а затем записать модифицированный XML-файл. На первый взгляд, это кажется простой операцией, поскольку объекты XDTO предоставляют доступ к своим свойствам через точечную нотацию, аналогично обычным объектам 1С.
Однако, как показывает практика, при попытке изменить значения свойств в объектах XDTO могут возникнуть неожиданные трудности и ошибки. Рассмотрим типичную ситуацию и разберем пути её решения.
Да, значения аргументов (свойств) в объектах XDTO можно изменять. Экземпляры объектов XDTO являются изменяемыми (мутабельными), что означает, что их состояние может быть изменено путем присвоения новых значений отдельным свойствам. Обращение к свойствам объекта XDTO осуществляется через точку, как к обычным объектам 1С. В этом и заключается основная идея работы с XDTO как с типизированными объектами.
Начнем с типового примера чтения XML, создания фабрики XDTO и попытки изменения данных:
СхемаXML = Новый СхемаXML;
СхемаXML.РасположениеСхемы = "C:\ПутьКФайлу\Схема.xsd"; // Путь к файлу XSD
СхемыXML = Новый НаборСхемXML;
СхемыXML.Добавить(СхемаXML);
ЧтениеXML = Новый ЧтениеXML();
ЧтениеXML.ОткрытьФайл("C:\ПутьКФайлу\Входной.xml"); // Путь к входному XML-файлу
Фабрика = Новый ФабрикаXDTO(СхемыXML);
Данные = Фабрика.ПрочитатьXML(ЧтениеXML); // Читаем XML в объект XDTO
ЧтениеXML.Закрыть();
// Предполагаемая попытка изменить данные
// Данные.MessageHeader.MessageID = "НовоеЗначениеID"; // <-- Здесь возникает ошибка
// Далее предполагается запись измененных данных
// Запись = Новый ЗаписьXML;
// Запись.ОткрытьФайл("C:\ПутьКФайлу\Выходной.xml", Новый ПараметрыЗаписиXML("UTF-8", "1.0", ));
// Запись.ЗаписатьОбъявлениеXML();
// Фабрика.ЗаписатьXML(Запись,Данные);
// Запись.Закрыть();
Как мы видим из обсуждения, при попытке присвоить значение свойству, например, Данные.MessageHeader.MessageID = Строка(РасхНакладная.Номер);, возникает ошибка:
{Форма.Форма.Форма(20)}: Ошибка при установке значения атрибута контекста (MessageID)
по причине:
по причине:
Несоответствие типов XDTO:
Тип '{http://www.w3.org/2001/XMLSchema}anyType' не найден
Значение не может быть установлено свойству, имеющему объектный тип
И при этом уточняется, что тип свойства Данные.MessageHeader.MessageID должен быть строкой.
Эта ошибка является одной из наиболее распространенных и коварных при работе с XDTO. Она указывает на то, что механизм XDTO не смог однозначно определить тип данных для конкретного элемента или атрибута в XML-документе на основе предоставленной XSD-схемы. В результате, вместо конкретного типа (например, строка, число, дата), XDTO назначает этому свойству универсальный тип anyType, который является базовым для всех объектов XDTO, но не позволяет напрямую присваивать простые значения.
Проанализируем ключевые причины возникновения этой проблемы:
targetNamespace). Это пространство имен указывает, к какой "области" относятся типы и элементы, определенные в этой схеме. Когда ФабрикаXDTO читает XML-документ, она пытается сопоставить элементы этого документа с типами, определенными в загруженных схемах. Для этого сопоставления крайне важно, чтобы элементы в XML-документе были "связаны" с тем же пространством имен, которое указано в targetNamespace XSD-схемы.xmlns или xmlns:prefix), или если указанное пространство имен не совпадает с targetNamespace в XSD, ФабрикаXDTO "не понимает", к какой схеме относятся эти элементы. В результате она не может применить правильное типизирование, и вместо конкретного типа назначает anyType. Это приводит к тому, что при попытке присвоить строковое или числовое значение свойству, которое XDTO считает anyType (объектный тип), система выдает ошибку "Значение не может быть установлено свойству, имеющему объектный тип".ФабрикаXDTO также может неверно определить типы данных, что приведет к использованию anyType.Основное направление решения — это обеспечение корректного сопоставления типов между XML-документом и XSD-схемой, в первую очередь, через правильное использование пространств имен.
Это наиболее эффективное и правильное решение, если причина кроется в несоответствии пространств имен. Мы должны убедиться, что XML-документ явно указывает то же целевое пространство имен, которое определено в вашей XSD-схеме.
targetNamespace в XSD: Откройте ваш файл XSD и найдите атрибут targetNamespace в корневом элементе <xs:schema>. Например:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://mycompany.com/schema"
elementFormDefault="qualified">
<!-- Определения типов и элементов -->
</xs:schema>
http://mycompany.com/schema.xmlns в корневой элемент:
<MyRootElement xmlns="http://mycompany.com/schema">
<MessageHeader>
<MessageID>ИсходныйID</MessageID>
<!-- ... -->
</MessageHeader>
<!-- ... -->
</MyRootElement>
xmlns:prefix:
<p:MyRootElement xmlns:p="http://mycompany.com/schema">
<p:MessageHeader>
<p:MessageID>ИсходныйID</p:MessageID>
<!-- ... -->
</p:MyRootElement>
elementFormDefault="qualified" (как в примере выше), то даже дочерние элементы должны быть квалифицированы префиксом или принадлежать пространству имен по умолчанию. Если elementFormDefault="unqualified", то только корневой элемент и глобальные элементы будут квалифицированы.После корректировки XML-файла, ФабрикаXDTO сможет правильно сопоставить элементы с типами из схемы, и вы сможете изменять свойства через точечную нотацию без ошибки anyType.
Давайте посмотрим на исправленный код, который теперь сможет изменять значения:
СхемаXML = Новый СхемаXML;
СхемаXML.РасположениеСхемы = "C:\ПутьКФайлу\Схема.xsd";
СхемыXML = Новый НаборСхемXML;
СхемыXML.Добавить(СхемаXML);
ЧтениеXML = Новый ЧтениеXML();
ЧтениеXML.ОткрытьФайл("C:\ПутьКФайлу\Входной.xml"); // Теперь Входной.xml содержит правильное пространство имен
Фабрика = Новый ФабрикаXDTO(СхемыXML);
Данные = Фабрика.ПрочитатьXML(ЧтениеXML); // XDTO-объект теперь правильно типизирован
ЧтениеXML.Закрыть();
// Теперь это будет работать!
Данные.MessageHeader.MessageID = "НовоеЗначениеID"; // Присваиваем новое строковое значение
Запись = Новый ЗаписьXML;
Запись.ОткрытьФайл("C:\ПутьКФайлу\Выходной.xml", Новый ПараметрыЗаписиXML("UTF-8", "1.0", ));
Запись.ЗаписатьОбъявлениеXML();
Фабрика.ЗаписатьXML(Запись,Данные); // Записываем измененный XML
Запись.Закрыть();
После выполнения этого кода, в файле Выходной.xml мы увидим измененное значение MessageID.
В некоторых сложных случаях, когда корневой элемент XML-документа по каким-либо причинам не имеет четко выраженного пространства имен или фабрика испытывает затруднения с его определением, можно попробовать явно указать тип корневого элемента при чтении. Этот подход менее универсален, но может помочь, если вы точно знаете тип корневого объекта в XSD-схеме.
Для этого используется метод ФабрикаXDTO.ПрочитатьXML(ЧтениеXML, ТипXDTO), где ТипXDTO — это объект типа ТипXDTO, полученный из фабрики.
// ... (чтение схемы, создание фабрики)
ТипКорневогоЭлемента = Фабрика.Тип("http://mycompany.com/schema", "MyRootElementType"); // Укажите URI пространства имен и имя типа корневого элемента из XSD
// Если тип элемента не определен в схеме как глобальный тип, а как глобальный элемент, то можно использовать:
// ТипКорневогоЭлемента = Фабрика.ТипЭлемента("http://mycompany.com/schema", "MyRootElement");
ЧтениеXML = Новый ЧтениеXML();
ЧтениеXML.ОткрытьФайл("C:\ПутьКФайлу\Входной.xml");
Данные = Фабрика.ПрочитатьXML(ЧтениеXML, ТипКорневогоЭлемента); // Явно указываем ожидаемый тип
ЧтениеXML.Закрыть();
// Теперь можно изменять свойства
Данные.MessageHeader.MessageID = "НовоеЗначениеID";
// ... (запись обратно в XML)
Однако, если основная проблема заключается в отсутствии пространств имен в самом XML-файле, то даже явное указание типа может не всегда решить проблему, так как XDTO все равно будет искать соответствие элементов с их пространствами имен.
Если все вышеперечисленные методы не приносят желаемого результата, или если требуется более гибкий, низкоуровневый контроль над XML-структурой без жесткой привязки к XSD-схеме, мы можем использовать стандартные объекты 1С для работы с DOM-моделью XML-документа.
Подход с ДокументDOM позволяет работать с XML как с деревом узлов, напрямую получая, изменяя и создавая элементы и атрибуты. Это обходит типизацию XDTO, но требует более тщательной ручной работы с иерархией XML.
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл("C:\ПутьКФайлу\Входной.xml");
ДокументDOM = Новый ДокументDOM;
ДокументDOM.Прочитать(ЧтениеXML);
ЧтениеXML.Закрыть();
ПолучитьЭлементыПоИмени(), НайтиУзел() и свойства ТекстовоеСодержимое для изменения значений.
Корень = ДокументDOM.ПервыйДочернийУзел; // Корневой элемент XML
Если Корень <> Неопределено Тогда
// Предположим, мы ищем элемент <MessageID> внутри <MessageHeader>
// Для надежности обходим вручную или используем XPath, если доступен
Для Каждого ДочернийУзелКорень Из Корень.ДочерниеУзлы Цикл
Если ДочернийУзелКорень.Имя = "MessageHeader" Тогда
Для Каждого ДочернийУзелHeader Из ДочернийУзелКорень.ДочерниеУзлы Цикл
Если ДочернийУзелHeader.Имя = "MessageID" Тогда
ДочернийУзелHeader.ТекстовоеСодержимое = "НовоеЗначениеIDЧерезDOM";
Прервать; // Выходим после первого найденного MessageID
КонецЦикла;
Прервать; // Выходим после обработки MessageHeader
КонецЕсли;
КонецЦикла;
КонецЕсли;
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл("C:\ПутьКФайлу\ВыходнойDOM.xml", Новый ПараметрыЗаписиXML("UTF-8", "1.0", Истина)); // Истина для форматирования
ДокументDOM.Записать(ЗаписьXML);
ЗаписьXML.Закрыть();
Метод ДокументDOM требует более глубокого понимания структуры XML, но дает полный контроль над документом. Он полезен, когда XDTO-фабрика не справляется с типизацией или когда требуется манипулировать XML, который не имеет строгой XSD-схемы или содержит много необязательных/динамических частей.
Мы выяснили, что объекты XDTO в 1С по своей природе изменяемы, и их свойства можно модифицировать. Однако, распространенная ошибка "Несоответствие типов XDTO: Тип anyType не найден" часто блокирует эту возможность. Основная причина этой проблемы кроется в несоответствии пространств имен между XML-документом и XSD-схемой.
Для успешного изменения данных мы рекомендуем:
targetNamespace в вашей XSD-схеме. Это позволит ФабрикеXDTO правильно типизировать объекты.ДокументDOM для прямого манипулирования XML-структурой.Правильное применение этих подходов позволит вам эффективно работать с внешними XML-документами, изменяя их содержимое средствами 1С и сохраняя обратно в файл.