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

25.01.21

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

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

Файлы

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

Наименование Скачано Купить файл
ПроверкаКорректностиXMLФайла.epf
.epf 10,09Kb
40 1 850 руб. Купить

Подписка PRO — скачивайте любые файлы со скидкой до 85% из Базы знаний

Оформите подписку на компанию для решения рабочих задач

Оформить подписку и скачать решение со скидкой

Формат загружаемого файла должен соответствовать Пакету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С:Предприятие 8 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" с помощью правил конвертации будет максимально комфортным! | Можно бесплатно проверить перенос на вашем сервере!

52967 руб.

25.02.2015    180111    343    281    

406

SALE! 15%

Перенос данных 1C Файловый обмен (TXT, XML, DBF), FTP Системный администратор Программист 1С:Предприятие 8 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. Не требуется синхронного обновления правил после обновления другой конфигурации, участвующей в обмене. Типовой обмен через планы обмена кнопкой Синхронизация вручную или автоматически по расписанию, или вручную обработкой.

27180 руб.

12.06.2017    156473    927    306    

473

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

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

38000 руб.

15.12.2021    31660    229    61    

173

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

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

38000 руб.

23.07.2020    64221    303    81    

243

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

Перенос данных из ERP в БП 3 | из КА 2 в БП 3 | из УТ 11 в БП 3 | из ЕРП в БП 3 | Сэкономьте время - используйте готовое решение для перехода! | Перенос разработан в формате КД 2 (правила конвертации данных) | Переносятся все возможные виды документов, начальных остатков и нормативно-справочная информация| Можно опционально выгружать каждую пару "номенклатура+характеристика" как отдельную номенклатуру | Есть выгрузка настроек счетов учета и зарплатных данных из ERP / КА 2 | Можно проверить на вашем сервере перед покупкой

61356 руб.

15.04.2019    81057    218    168    

157

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

Перенос данных из ЗУП 3 в ЗУП 3 | из ЗУП 3 в КА 2 | из ЗУП 3 в ERP | Оперативно обновляется при выходе новых релизов 1С | Готовые правила конвертации (КД 2) для перехода с "ЗУП 3" на "УП ред. 3" / "КА, ред. 2" / "ERP, ред. 2" |Переносится нормативно-справочная информация и документы с движениями

58422 руб.

11.01.2021    36716    32    55    

34

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

Перенос данных из КА 1.1 в КА 2 | из КА 1.1 в УТ 11 | Воспользовались более 367 компаний! | Переносятся все возможные виды документов, начальных остатков и вся справочная информация из "1С:КА 1.1" в "1С:КА 2.х" / "1С:УТ 11" | Разработан в формате КД 2 (правила конвертации данных) | Фильтр по организациям при выгрузке | Выбор разных алгоритмов выгрузки начальных остатков | Можно проверить перенос до покупки!

61356 руб.

04.12.2015    197729    260    354    

413

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

Правила конвертации (КД 2) для переноса данных из БП 3 в УНФ | Переносятся все виды документов, начальные остатки и вся возможная справочная информация | Есть фильтр по организациям | Оперативно обновляем на новые релизы | Учет в БП 3 должен быть корректным, некорректные данные не переносятся | Можно бесплатно проверить на вашем сервере до покупки!

61356 руб.

10.07.2018    76800    53    143    

58
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
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.Существует = Истина;
	КонецЕсли;	
КонецЦикла;
Показать
Для отправки сообщения требуется регистрация/авторизация