Поиск строки XML-файла, содержащей значение, не соответствующее типу свойства XDTO пакета

25.01.21

Интеграция - Файловый обмен (TXT, XML, DBF), FTP

Ведется обмен со сторонней системой через XML-файлы. XML-файлы содержат данные, соответствующие ПакетуXDTO, и загружаются с использованием ФабрикиXDTO. Сторонняя система не всегда корректно формирует XML-файл и при загрузке файла метод ПрочитатьXML() ФабрикиXDTO выбрасывает исключение. По описанию ошибки не всегда можно понять, какая именно секция содержит некорректное значение.

Скачать файл

ВНИМАНИЕ: Файлы из Базы знаний - это исходный код разработки. Это примеры решения задач, шаблоны, заготовки, "строительные материалы" для учетной системы. Файлы ориентированы на специалистов 1С, которые могут разобраться в коде и оптимизировать программу для запуска в базе данных. Гарантии работоспособности нет. Возврата нет. Технической поддержки нет.

Наименование По подписке [?] Купить один файл
ПроверкаКорректностиXMLФайла.epf
.epf 10,09Kb
36
36 Скачать (1 SM) Купить за 1 850 руб.

Формат загружаемого файла должен соответствовать ПакетуXDTO

<?xml version="1.0" encoding="UTF-8"?>
<My-Object xmlns="http://www.MyURL" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
	<header>
		<num>2019-02-10</num> <!-------тут должно быть число----->
		<sender-id>00001</sender-id>
		<reciever-id>00000</reciever-id>
		<created-date>2019-02-11T08:09:09</created-date>
	</header>
	<docs>
		<doc>
			<doc-date>100</doc-date> <!-------тут должна быть дата----->
			<doc-num>1</doc-num>
			<doc-gds>
				<doc-gd>
					<line-num>1</line-num>
					<gd-code>000001</gd-code>
					<qnty>10</qnty>
					<sum>1000</sum>
					<price>100</price>
				</doc-gd>
			</doc-gds>
		</doc>
	</docs>
</My-Object>

Но сторонняя система ничего не знает об XDTO и XSD. Она формирует по сути текстовый файл определенного формата. Поэтому при его создании валидность не проверяется.

При больших объемах XML-файлов поиск секции содержащей некорректное значение становится проблемой. Ошибки выдаваемые платформой не очень информативны

Мы можем отсюда понять, что некая секция вместо даты содержит значение "100". Нам нужно найти секцию, которая согласно ПакетуXDTO имеет тип "Дата", но при этом содержит значение "100". Значение "100" может встречаться в файле многократно, а типы секций в XML-файле не обозначены.

Со значением "2019-02-10" и типом Integer ситуация аналогичная.

Если читать XML-файл построчно, то сопоставить получаемые данные с ПакетомXDTO будет проблематично. Поэтому реализация выполнена  через ДокументDOM.

Алгоритм следующий:

  1. Читаем XML-файл в ДокументDOM
  2. Получаем корневой узел.
  3. Затем последовательно перебираем соседние узлы первого уровня вложенности
  4. Читаем полученный узел ФабрикойXDTO с указанием типа
  5. Если чтение произошло без ошибок переходим к следующему соседнему узлу и переходим к пункту 4
  6. Если чтение узла выбросило исключение, то проверяем тип и состав вложенного узла
  7. Если дочерний узел содержит простое значение, значит оно и является некорректным. Получаем следующий соседний узел и переходим к пункту 4
  8. Если вложенный узел содержит дочерние узлы, то проверяем должен ли он их содержать согласно ПакетуXDTO. Если нет, значит этот узел и является некорректным. Получаем следующий соседний узел и переходим к пункту 4
  9. Если вложенный узел содержит дочерние узлы, то последовательно получаем каждый из них. Проверяем есть ли такой узел в ПакетеXDO. Если нет, значит этот узел и является некорректным. Получаем следующий соседний узел и переходим к пункту 4
  10. После перебора всех вложенных узлов проверяем, что все обязательные узлы присутствую.

Таким образом мы получим все некорректные узлы. Но остается другая проблема. Как узнать в какой строке XML-файла находится некорректный узел?

Для этого необходимо некорректному узлу добавить атрибут, в качестве значения подставить UID и затем сохранить ДокументDOM во временный файл. Список подставленных UIDов и описание причины некорректности узла сохраняются в ТЗ. Полученный файл можно прочитать построчно и в случае обнаружения в строке ранее сохраненного UIDа можно понять в какой строке содержится некорректный узел и в чем именно ошибка.Так же во время чтения можно удалить ранее созданный атрибут и его значение, после чего файл примет первоначальный вид.

Ниже приведен пример кода, который выполняет все эти функции.

&НаСервере
Процедура ПроверитьНаСервере(АдресПроверяемогоФайла)
	
	#Область ЧтениеДокументаДОМ
	//Читаем файл из временного хранилища
	Поток = ПолучитьИзВременногоХранилища(АдресПроверяемогоФайла).ОткрытьПотокДляЧтения();
	ЧтениеХМЛ = Новый ЧтениеXML;
	ЧтениеХМЛ.ОткрытьПоток(Поток);
	ПостроительДОМ = Новый ПостроительDOM;
	ДокументДОМ	= ПостроительДОМ.Прочитать(ЧтениеХМЛ);
	ЧтениеХМЛ.Закрыть();
	Поток.Закрыть();
	#КонецОбласти
	
	#Область ОбходДереваДОМ
	//Обходим дерево ДОМ
	ОбходДереваDOM = Новый ОбходДереваDOM(ДокументДОМ);
	//Получаем корневой узел
	ОбходДереваDOM.СледующийУзел();
	//Получаем тип корневого узла
	ТипОбъектаXDTO = ФабрикаXDTO.Тип("http://www.MyURL", "MyObject");
	//Получаем первый вложенный узел
	УзелDOM = ОбходДереваDOM.СледующийУзел();
	Пока УзелDOM <> Неопределено Цикл

		#Область ПоискСвойстваОбъектаXDTOПоЛокальномуИмениУзла
		СвойствоОбъектаXDTO = Неопределено;
		Для Каждого СвойствоXDTO Из ТипОбъектаXDTO.Свойства Цикл
			Если СвойствоXDTO.ЛокальноеИмя = УзелDOM.ИмяУзла Тогда
				//СвойствоТипОбъектаXDTO = ФабрикаXDTO.Тип(СвойствоXDTO.Тип.URIПространстваИмен, СвойствоXDTO.Тип.Имя);
				СвойствоОбъектаXDTO = СвойствоXDTO;
				Прервать;
			КонецЕсли;
		КонецЦикла;
		Если СвойствоОбъектаXDTO = Неопределено Тогда
			Сообщить("Объект " + УзелDOM.РодительскийУзел.ИмяУзла + " неивестное свойство " + УзелDOM.ИмяУзла);
			УзелDOM = ОбходДереваDOM.СледующийСоседний();
			Продолжить;
		КонецЕсли;
		#КонецОбласти
		
		ПроверитьУзел(УзелDOM, СвойствоОбъектаXDTO.Тип);
		
		//Получаем следующий узел на этом же уровне вложенности
		УзелDOM = ОбходДереваDOM.СледующийСоседний();
	КонецЦикла;
	#КонецОбласти
	
	#Область ЗаписьДокументаДОМ
	//Записываем документ ДОМ в поток
	Поток = Новый ПотокВПамяти ;
	ЗаписьХМЛ = Новый ЗаписьXML;
	ЗаписьХМЛ.Отступ = Истина;
	ЧетыреПробела = "    ";
	ПараметрыЗаписи = Новый ПараметрыЗаписиXML("UTF-8", , Истина, Ложь, ЧетыреПробела);
	ЗаписьХМЛ.ОткрытьПоток(Поток, ПараметрыЗаписи);
	ЗаписьДОМ = Новый ЗаписьDOM;
	ЗаписьДОМ.Записать(ДокументДОМ, ЗаписьХМЛ);
	ЗаписьХМЛ.Закрыть();
	#КонецОбласти
	
	ПозицияКурсора = 0;
	Смещение	   = 0;
	
	//Закрываем Поток для записи и открываем для чтения
	Поток = Поток.ЗакрытьИПолучитьДвоичныеДанные().ОткрытьПотокДляЧтения();
	//Читаем текст документа ДОМ из потока
	ТекстовыйДокумент.Прочитать(Поток);
	Поток.Закрыть();
	Для НомерСтроки = 1 По ТекстовыйДокумент.КоличествоСтрок() Цикл
		ТекстСтроки = ТекстовыйДокумент.ПолучитьСтроку(НомерСтроки);
		Для Каждого СтрокаТаблицыОшибок Из ТаблицаОшибок Цикл
			Если  СтрокаТаблицыОшибок.Обработана Или СтрНайти(ТекстСтроки, СтрокаТаблицыОшибок.ИД) = 0 Тогда
				Продолжить;
			КонецЕсли;
			СтрокаТаблицыОшибок.Номер	   = НомерСтроки;
			СтрокаТаблицыОшибок.Обработана = Истина;
			СтрокаТаблицыОшибок.Закладка   = ПозицияКурсора + 1 + СтрДлина(ТекстСтроки) - СтрДлина(СокрЛП(ТекстСтроки)) - Смещение;
			
			Смещение = Смещение + СтрДлина("Error=""""00000000-0000-0000-0000-000000000000""");
			
			ТекстовыйДокумент.ЗаменитьСтроку(НомерСтроки, СтрЗаменить(ТекстСтроки, " Error=""" + СтрокаТаблицыОшибок.ИД + """", "")); 
		КонецЦикла;
		ПозицияКурсора = ПозицияКурсора + СтрДлина(ТекстСтроки) + 1;
	КонецЦикла;	
	
КонецПроцедуры

&НаСервере
Процедура ПроверитьУзел(УзелDOM, ТипXDTO)
	
	//Пытаемся прочитать УзелDOM ФабрикойXDTO
	УзелКорректный = Истина;
	
	#Область ЧтениеУзловДОМ
	ЧтениеУзловDOM = Новый ЧтениеУзловDOM;
	ЧтениеУзловDOM.Открыть(УзелDOM);
	Попытка
		Фабрика = ФабрикаXDTO.ПрочитатьXML(ЧтениеУзловDOM, ТипXDTO);
	Исключение
		УзелКорректный = Ложь;
	КонецПопытки;
	ЧтениеУзловDOM.Закрыть();
	#КонецОбласти
	
	//Если узел корректный, выходим из рекурсии
	Если УзелКорректный Тогда
		Возврат;
	КонецЕсли;
	//Если это свойство содержащее простое значение, значит оно некорректное
	Если ТипЗнч(УзелDOM.ПервыйДочерний) = Тип("ТекстDOM") Или ТипЗнч(ТипXDTO) = Тип("ТипЗначенияXDTO") Тогда
		ТекстСообщения = "Узел """ + УзелDOM.ИмяУзла + """ (тип """ +  ТипXDTO.Имя + """) содержит некорректное значение """ + УзелDOM.ТекстовоеСодержимое + """";
		ЗарегистрироватьОшибку(УзелDOM, ТекстСообщения); 
	//Есть вложенные, а их быть не должно	
	ИначеЕсли УзелDOM.ЕстьДочерниеУзлы() И ТипXDTO.Свойства.Количество() = 0 Тогда
		ТекстСообщения = "Узел """ + УзелDOM.ИмяУзла + """ не должен содержать вложенных секций.";
		ЗарегистрироватьОшибку(УзелDOM, ТекстСообщения);
	//Не вложенных, а они должны быть
	ИначеЕсли Не УзелDOM.ЕстьДочерниеУзлы() И ТипXDTO.Свойства.Количество() <> 0 Тогда
		ТекстСообщения = "Узел """ + УзелDOM.РодительскийУзел.ИмяУзла + """ содержит пустую секцию """ + УзелDOM.ИмяУзла + """";
		ЗарегистрироватьОшибку(УзелDOM, ТекстСообщения);
	Иначе
		//Получаем свойства соответствующего ТипаXDTO
		УзелDOMСвойстваXDTO = Новый ТаблицаЗначений;
		УзелDOMСвойстваXDTO.Колонки.Добавить("ТипXDTO");
		УзелDOMСвойстваXDTO.Колонки.Добавить("ЛокальноеИмя",   Новый ОписаниеТипов("Строка"));
		УзелDOMСвойстваXDTO.Колонки.Добавить("Существует",	   Новый ОписаниеТипов("Булево"));
		УзелDOMСвойстваXDTO.Колонки.Добавить("НеОбязательное", Новый ОписаниеТипов("Булево"));
		Для Каждого СвойствоТипаXDTO Из ТипXDTO.Свойства Цикл
			НоваяСтрока = УзелDOMСвойстваXDTO.Добавить();
			НоваяСтрока.ТипXDTO			= СвойствоТипаXDTO.Тип;
			НоваяСтрока.ЛокальноеИмя	= СвойствоТипаXDTO.ЛокальноеИмя;
			НоваяСтрока.Существует		= Ложь;
			НоваяСтрока.НеОбязательное	= СвойствоТипаXDTO.НижняяГраница = 0;
		КонецЦикла;
		//Проверяем наличие свойств
		Для Каждого Дочерний Из УзелDOM.ДочерниеУзлы Цикл
			СвойствоТипаXDTO = УзелDOMСвойстваXDTO.Найти(Дочерний.ИмяУзла, "ЛокальноеИмя"); 
			Если СвойствоТипаXDTO <> Неопределено Тогда
				СвойствоТипаXDTO.Существует = Истина;
				ПроверитьУзел(Дочерний, СвойствоТипаXDTO.ТипXDTO);
			Иначе
				ТекстСообщения = "Узел """ + УзелDOM.ИмяУзла + """ содержит недопустимую вложенную секцию """ + Дочерний.ИмяУзла + """";
				ЗарегистрироватьОшибку(Дочерний, ТекстСообщения); 
			КонецЕсли;	
		КонецЦикла;
		ОтсутствующиеСвойства = УзелDOMСвойстваXDTO.НайтиСтроки(Новый Структура("Существует, НеОбязательное", Ложь, Ложь));
		Для Каждого ОтсутствующееСвойство Из ОтсутствующиеСвойства Цикл
			ТекстСообщения = "Отсутствует обязательная вложенная секция";
			ЗарегистрироватьОшибку(УзелDOM, ТекстСообщения); 
		КонецЦикла;	
	КонецЕсли;
	
КонецПроцедуры

&НаСервере
Процедура ЗарегистрироватьОшибку(УзелДОМ, ОписаниеОшибки)
	
	ПолноеИмяУзла = УзелДОМ.ИмяУзла;
	РодительскийУзел = УзелДОМ.РодительскийУзел;
	Пока РодительскийУзел.ИмяУзла <> "#document" Цикл
		МассивСтрок = Новый Массив;
		МассивСтрок.Добавить(РодительскийУзел.ИмяУзла);
		МассивСтрок.Добавить("\");
		МассивСтрок.Добавить(ПолноеИмяУзла);
		ПолноеИмяУзла = СтрСоединить(МассивСтрок);
		РодительскийУзел = РодительскийУзел.РодительскийУзел;
	КонецЦикла;
	
	ГУИД = Строка(Новый УникальныйИдентификатор);
	
	Атрибут = УзелДОМ.ДокументВладелец.СоздатьАтрибут("Error");
	Атрибут.Значение = СокрЛП(ГУИД);
	УзелДОМ.Атрибуты.УстановитьИменованныйЭлемент(Атрибут);
	
	СтрокаТаблицы = ТаблицаОшибок.Добавить();
	СтрокаТаблицы.ИД = ГУИД;
	СтрокаТаблицы.Реквизит = ПолноеИмяУзла;
	СтрокаТаблицы.Значение = УзелДОМ.ИмяУзла;
	СтрокаТаблицы.Описание = ОписаниеОшибки;
	
КонецПроцедуры

Во вложении обработка визуально отображающая строки XML-файла содержащие некорректные секции. Переход к строке осуществляется по двойному клику на строку таблицы ошибок. Конфигурация должна содержать XDTO-пакет с пространством имен проверяемого XML-файла.

Платформа 8.3.14.

См. также

Перенос данных 1C Файловый обмен (TXT, XML, DBF), FTP Системный администратор Программист Платформа 1С v8.3 1С:Розница 2 1С:Управление нашей фирмой 1.6 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Управление нашей фирмой 3.0 1С:Розница 3.0 Россия Платные (руб)

Правила в универсальном формате обмена для ERP 2.5, КА 2.5, УТ 11.5, БП 3.0, Розница, УНФ, для последних версий конфигураций. Ссылки на другие конфигурации в описании публикации. Правила совместимы со всеми другими версиями конфигураций новыми и старыми, поддерживающими обмен и синхронизацию в формате EnterpriseData. Не требуется синхронного обновления правил после обновления другой конфигурации, участвующей в обмене. Типовой обмен через планы обмена кнопкой Синхронизация вручную или автоматически по расписанию, или вручную обработкой.

26280 руб.

12.06.2017    142050    802    297    

422

SALE! 10%

Перенос данных 1C Файловый обмен (TXT, XML, DBF), FTP Системный администратор Программист Платформа 1С v8.3 1С:Управление производственным предприятием 1С:Бухгалтерия 3.0 Россия Бухгалтерский учет Управленческий учет Платные (руб)

Перенос данных из 1С:Управление производственным предприятием 1.3 в 1С:Бухгалтерия предприятия 3.0 с помощью правил обмена. Переносятся остатки, документы (обороты за период), справочная информация. Правила проверены на конфигурациях УПП 1.3 (1.3.236.x) и БП 3.0 (3.0.164.x). Правила подходят для версии ПРОФ и КОРП.

35000 31500 руб.

15.12.2021    24285    171    51    

131

SALE! 10%

Перенос данных 1C Файловый обмен (TXT, XML, DBF), FTP Системный администратор Программист Платформа 1С v8.3 Оперативный учет 1С:Управление торговлей 10 Россия Управленческий учет Платные (руб)

Перенос данных из 1С:Управление торговлей 10.3 в 1С:Управление торговлей 11.5 с помощью правил обмена. Переносятся остатки, документы (обороты за период), справочная информация. Правила проверены на конфигурациях УТ 10.3 (10.3.88.x) и УТ 11.5 (11.5.19.x).

35000 31500 руб.

23.07.2020    51801    229    70    

187

SALE! 10%

Перенос данных 1C Файловый обмен (TXT, XML, DBF), FTP Системный администратор Программист Платформа 1С v8.3 1С:Комплексная автоматизация 1.х 1С:Управление производственным предприятием 1С:Бухгалтерия 3.0 Россия Бухгалтерский учет Платные (руб)

Перенос данных из 1С:Управление производственным предприятием 1.3 в 1С:Бухгалтерия предприятия 3.0 с помощью правил обмена | Можно выполнить переход с УПП на БП 3 или запускать выгрузку данных за выбранный период времени | Переносятся документы, начальные остатки и вся справочная информация | Есть фильтр по организации и множество других параметров выгрузки | Поддерживается несколько сценариев работы: как первичный полный перенос, так и перенос только новых документов | Перенос данных возможен в "1С: Бухгалтерия 3.0" версии ПРОФ, КОРП или базовую | Переход с "1С: УПП1.3" / "1С:КА 1.1" на "1С:БП3.0" с помощью правил конвертации будет максимально комфортным! | Можно бесплатно проверить перенос на вашем сервере!

48278 43450 руб.

25.02.2015    171381    304    257    

380

SALE! 10%

Перенос данных 1C Файловый обмен (TXT, XML, DBF), FTP Программист Платформа 1С v8.3 1С:ERP Управление предприятием 2 1С:Комплексная автоматизация 2.х 1С:Зарплата и Управление Персоналом 3.x Россия Бухгалтерский учет Управленческий учет Платные (руб)

Перенос данных из ERP в ЗУП 3 | из КА 2 в ЗУП | Готовые правила конвертации данных (КД 2) для переноса остатков, документов с движениями и справочной информации 3 | Есть перенос начальной задолженности по зарплате и начальной штатной расстановки на выбранную дату | Обороты за прошлые годы (данные для расчета среднего) переносятся свернуто в документ "Перенос данных" | Есть фильтр по организациям | Документы за текущий период переносятся сразу с движениями, поэтому не потребуется делать перерасчеты | Перенос можно проверить перед покупкой, обращайтесь!

53111 47800 руб.

03.12.2020    36816    95    66    

92

Перенос данных 1C Файловый обмен (TXT, XML, DBF), FTP Системный администратор Программист Платформа 1С v8.3 Платформа 1C v8.2 1С:Комплексная автоматизация 1.х 1С:Управление торговлей 10 1С:Управление производственным предприятием Россия Платные (руб)

Регулярный обмен, выгрузка, перенос из КА 1.1, УПП 1.3, УТ 10.3 для обмена с любыми конфигурациями, поддерживающими обмен в формате EnterpriseData (КД3) - БП 3.0, ERP, КА 2, УТ 11, Розница 2, УНФ 1.6 и другими. Правила для старых и доработанных конфигураций не требуют синхронного обновления и совместимы с новыми и будущими конфигурациями. Обмен по расписанию, через папку, FTP, почту.

15300 руб.

18.02.2016    187117    591    509    

528

SALE! 10%

Перенос данных 1C Файловый обмен (TXT, XML, DBF), FTP Системный администратор Программист Платформа 1С v8.3 1С:Комплексная автоматизация 1.х 1С:Управление торговлей 10 1С:Управление производственным предприятием 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Россия Платные (руб)

Перенос данных из ERP в УПП 1.3 | из КА 2 в КА 1.1 | из КА 2 в УПП 1.3 | из КА 2 в УТ 10.3 | из ERP в КА 1.1 | из ERP в УТ 10.3 | из УТ 11 в УТ 10.3 | из УТ 11 в УПП 1.3 | из УТ 11 в КА 1.1 | Можно переносить только новые объекты, найденные в приемнике перезаписываться не будут | Есть фильтр по организации при выгрузке данных | Оперативно обновляем на новые релизы 1С

53111 47800 руб.

28.11.2015    83362    32    125    

65

SALE! 10%

Перенос данных 1C Взаиморасчеты Оптовая торговля Логистика, склад и ТМЦ Файловый обмен (TXT, XML, DBF), FTP Системный администратор Программист Платформа 1С v8.3 1С:Управление торговлей 10 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Россия Управленческий учет Платные (руб)

Можно проверить до покупки, оставьте заявку! Воспользовались более 268 компаний! Перенос данных из УТ 10.3 в УТ 11 | из УТ 10.3 в КА 2 | из УТ 10.3 в ERP. Предлагаем качественное и проверенное временем решение для перехода с УТ 10.3. Можно перенести начальные остатки, нормативно-справочную информацию и все возможные документы. При выгрузке можно установить отбор по периоду, организациям и складам. При выходе новых релизов конфигураций 1C оперативно выпускаем обновление переноса данных.

55778 50200 руб.

24.04.2015    195258    152    244    

282
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. renmy 93 06.09.22 15:41 Сейчас в теме
Статья супер. Автору большое спасибо.

Пришлось немного переделать по себя.
1. В моей схеме типы определяются в свойствах. Получение типа сделал через корневые свойства.
//Получаем тип корневого узла
//ТипОбъектаXDTO = ФабрикаXDTO.Тип("http://www.MyURL", "MyObject");
ТипОбъектаXDTO = ФабрикаXDTO.Пакеты.Получить("ИмяПакета").КорневыеСвойства.Получить("ИмяУзла").Тип;

2.Выводились атрибуты в ошибку "Отсутствует обязательная вложенная секция"
Добавил проверку атрибутов перед кодом:
ОтсутствующиеСвойства = УзелDOMСвойстваXDTO.НайтиСтроки(Новый Структура("Существует, НеОбязательное", Ложь, Ложь));


//Исключаем существующие атрибуты из отсутствующих
Для Каждого Дочерний Из УзелDOM.АтрибутыЦикл
	СвойствоТипаXDTO = УзелDOMСвойстваXDTO.Найти(Дочерний.ЛокальноеИмя, "ЛокальноеИмя"); 
	Если СвойствоТипаXDTO <> Неопределено Тогда
		СвойствоТипаXDTO.Существует = Истина;
	КонецЕсли;	
КонецЦикла;
Показать
Оставьте свое сообщение