РазборПредставленияОбъектаXML (БСП)

Автор: 1С
ОбщийМодуль.ВерсионированиеОбъектов
БСП

Разбор представления объекта xml. Процедура считывает данные XML из файла и заполняет структуры данных.

// Процедура считывает данные XML из файла и заполняет структуры данных.
//
// Возвращаемое значение:
// Структура, содержащая два соответствия: ТабличныеЧасти, Реквизиты.
// Структура хранения данных:
// Соответствие ТабличныеЧасти, которое содержит в себе значения табличных частей
// формат: 
//          СоответствиеИмя1 -> ТаблицаЗначений1
//                            |      |     ... |
//                            Поле1  Поле2     ПолеM1.
//
//          СоответствиеИмя2 -> ТаблицаЗначений2
//                            |      |     ... |
//                            Поле1  Поле2     ПолеM2.
//
//
//          СоответствиеИмяN -> ТаблицаЗначенийN
//                            |      |     ... |
//                            Поле1  Поле2     ПолеM3.
//
// Соответствие ЗначенияРеквизитов
//          ИмяРеквизита1 -> Значение1
//          ИмяРеквизита2 -> Значение2
//          ...
//          ИмяРеквизитаN -> ЗначениеN.
//
Функция РазборПредставленияОбъектаXML(ДанныеВерсии, Ссылка) Экспорт
	
	Результат = Новый Структура;
	Результат.Вставить("ТабличныеДокументы");
	Результат.Вставить("ДополнительныеРеквизиты");
	Результат.Вставить("СкрываемыеРеквизиты", Новый Массив);
	
	ДвоичныеДанные = ДанныеВерсии;
	Если ТипЗнч(ДанныеВерсии) = Тип("Структура") Тогда
		ДвоичныеДанные = ДанныеВерсии.Объект;
		ДанныеВерсии.Свойство("ТабличныеДокументы", Результат.ТабличныеДокументы);
		ДанныеВерсии.Свойство("ДополнительныеРеквизиты", Результат.ДополнительныеРеквизиты);
		ДанныеВерсии.Свойство("СкрываемыеРеквизиты", Результат.СкрываемыеРеквизиты);
	КонецЕсли;
	
	ЗначенияРеквизитов = Новый ТаблицаЗначений;
	ЗначенияРеквизитов.Колонки.Добавить("НаименованиеРеквизита");
	ЗначенияРеквизитов.Колонки.Добавить("ЗначениеРеквизита");
	ЗначенияРеквизитов.Колонки.Добавить("ТипРеквизита");
	ЗначенияРеквизитов.Колонки.Добавить("Тип");
	
	ТабличныеЧасти = Новый Соответствие;
	
	ЧтениеXML = Новый ЧтениеFastInfoSet;
	ЧтениеXML.УстановитьДвоичныеДанные(ДвоичныеДанные);
	
	// Уровень позиции маркера в иерархии XML:
	// 0 - уровень не задан
	// 1 - первый элемент (имя объекта)
	// 2 - описание реквизита или табличной части
	// 3 - описание строки табличной части
	// 4 - описание поля строки табличной части.
	УровеньЧтения = 0;
	
	МетаданныеОбъекта = Ссылка.Метаданные();
	ТипЗначенияПоляТЧ = "";
	
	// Основной цикл разбора по XML.
	Пока ЧтениеXML.Прочитать() Цикл
		Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
			УровеньЧтения = УровеньЧтения + 1;
			Если УровеньЧтения = 1 Тогда // Указатель на первом элементе XML - корень XML.
				// В ЧтениеXML.Имя имя объекта, но оно нам не нужно.
			ИначеЕсли УровеньЧтения = 2 Тогда // Указатель на втором уровне - это реквизит или имя табличной части.
				ИмяРеквизита = ЧтениеXML.Имя;
				
				// Любой реквизит "может оказаться" табличной частью, поэтому на всякий случай его запомним.
				ИмяТабличнойЧасти = ИмяРеквизита;
				Если МетаданныеТабличнойЧасти(МетаданныеОбъекта, ИмяТабличнойЧасти) <> Неопределено Тогда
					ТабличныеЧасти.Вставить(ИмяТабличнойЧасти, Новый ТаблицаЗначений);
				КонецЕсли;
				
				НовоеЗР = ЗначенияРеквизитов.Добавить();
				НовоеЗР.НаименованиеРеквизита = ИмяРеквизита;
				
				Если ЧтениеXML.КоличествоАтрибутов() > 0 Тогда
					Пока ЧтениеXML.ПрочитатьАтрибут() Цикл
						Если ЧтениеXML.ТипУзла = ТипУзлаXML.Атрибут 
							И ЧтениеXML.Имя = "xsi:type" Тогда
								НовоеЗР.ТипРеквизита = ЧтениеXML.Значение;
								XMLТип = ЧтениеXML.Значение;
								Если СтрНачинаетсяС(XMLТип, "xs:") Тогда
									НовоеЗР.Тип = ИзXMLТипа(Новый ТипДанныхXML(Прав(XMLТип, СтрДлина(XMLТип)-3), "http://www.w3.org/2001/XMLSchema"));
								Иначе
									НовоеЗР.Тип = ИзXMLТипа(Новый ТипДанныхXML(XMLТип, ""));
								КонецЕсли;
						КонецЕсли;
					КонецЦикла;
				КонецЕсли;
				
				Если Не ЗначениеЗаполнено(НовоеЗР.Тип) Тогда
					ОписаниеРеквизита = МетаданныеРеквизита(МетаданныеОбъекта, ИмяРеквизита);
					Если ОписаниеРеквизита = Неопределено Тогда
						ОписаниеРеквизита = Метаданные.ОбщиеРеквизиты.Найти(ИмяРеквизита);
					КонецЕсли;
					Если ОписаниеРеквизита = Неопределено И Метаданные.ПланыСчетов.Содержит(МетаданныеОбъекта) Тогда
						ОписаниеРеквизита = МетаданныеОбъекта.ПризнакиУчета.Найти(ИмяРеквизита);
					КонецЕсли;
					
					Если ОписаниеРеквизита <> Неопределено
						И ОписаниеРеквизита.Тип.Типы().Количество() = 1 Тогда
						НовоеЗР.Тип = ОписаниеРеквизита.Тип.Типы()[0];
					КонецЕсли;
				КонецЕсли;
			ИначеЕсли (УровеньЧтения = 3) И ЧтениеXML.Имя = "Row" Тогда // Указатель на поле табличной части.
				Если ТабличныеЧасти[ИмяТабличнойЧасти] = Неопределено Тогда
					ТабличныеЧасти.Вставить(ИмяТабличнойЧасти, Новый ТаблицаЗначений);
				КонецЕсли;
				ТабличныеЧасти[ИмяТабличнойЧасти].Добавить();
			ИначеЕсли УровеньЧтения = 4 Тогда
				Если ЧтениеXML.Имя = "v8:Type" Тогда
					Если НовоеЗР.ЗначениеРеквизита = Неопределено Тогда
						НовоеЗР.ЗначениеРеквизита = "";
					КонецЕсли;
				Иначе // Указатель на поле табличной части.
					ТипЗначенияПоляТЧ = "";
					ИмяПоляТЧ = ЧтениеXML.Имя;
					Таблица   = ТабличныеЧасти[ИмяТабличнойЧасти];
					Если Таблица.Колонки.Найти(ИмяПоляТЧ)= Неопределено Тогда
						Таблица.Колонки.Добавить(ИмяПоляТЧ);
					КонецЕсли;
					
					Если ЧтениеXML.КоличествоАтрибутов() > 0 Тогда
						Пока ЧтениеXML.ПрочитатьАтрибут() Цикл
							Если ЧтениеXML.ТипУзла = ТипУзлаXML.Атрибут 
								И ЧтениеXML.Имя = "xsi:type" Тогда
									XMLТип = ЧтениеXML.Значение;
									Если СтрНачинаетсяС(XMLТип, "xs:") Тогда
										ТипЗначенияПоляТЧ = ИзXMLТипа(Новый ТипДанныхXML(Прав(XMLТип, СтрДлина(XMLТип)-3), "http://www.w3.org/2001/XMLSchema"));
									Иначе
										ТипЗначенияПоляТЧ = ИзXMLТипа(Новый ТипДанныхXML(XMLТип, ""));
									КонецЕсли;
							КонецЕсли;
						КонецЦикла;
					КонецЕсли;
				КонецЕсли;
			КонецЕсли;
		ИначеЕсли ЧтениеXML.ТипУзла = ТипУзлаXML.КонецЭлемента Тогда
			УровеньЧтения = УровеньЧтения - 1;
		ИначеЕсли ЧтениеXML.ТипУзла = ТипУзлаXML.Текст Тогда
			Если (УровеньЧтения = 2) Тогда // значение реквизита
				Попытка
					НовоеЗР.ЗначениеРеквизита = ?(ЗначениеЗаполнено(НовоеЗР.Тип), XMLЗначение(НовоеЗР.Тип, ЧтениеXML.Значение), ЧтениеXML.Значение);
				Исключение
					НовоеЗР.ЗначениеРеквизита = ЧтениеXML.Значение;
				КонецПопытки;
			ИначеЕсли (УровеньЧтения = 4) Тогда // значение реквизита
				Если НовоеЗР.Тип = Тип("ОписаниеТипов") Тогда
					ТипСтрокой = Строка(ИзXMLТипа(Новый ТипДанныхXML(ЧтениеXML.Значение, "")));
					Если ПустаяСтрока(ТипСтрокой) Тогда
						ТипСтрокой = ЧтениеXML.Значение;
					КонецЕсли;
					Если Не ПустаяСтрока(НовоеЗР.ЗначениеРеквизита) Тогда
						НовоеЗР.ЗначениеРеквизита = НовоеЗР.ЗначениеРеквизита + Символы.ПС;
					КонецЕсли;
					НовоеЗР.ЗначениеРеквизита = НовоеЗР.ЗначениеРеквизита + ТипСтрокой;
				Иначе
					Если ТипЗначенияПоляТЧ = "" Тогда
						ОписаниеРеквизита = Неопределено;
						МетаданныеТабличнойЧасти = МетаданныеТабличнойЧасти(МетаданныеОбъекта, ИмяТабличнойЧасти);
						Если МетаданныеТабличнойЧасти <> Неопределено Тогда
							ОписаниеРеквизита = МетаданныеРеквизитаТабличнойЧасти(МетаданныеТабличнойЧасти, ИмяПоляТЧ);
							Если ОписаниеРеквизита = Неопределено И Метаданные.ПланыСчетов.Содержит(МетаданныеОбъекта) Тогда
								ОписаниеРеквизита = МетаданныеОбъекта.ПризнакиУчетаСубконто.Найти(ИмяПоляТЧ);
							КонецЕсли;
							Если ОписаниеРеквизита <> Неопределено
								И ОписаниеРеквизита.Тип.Типы().Количество() = 1 Тогда
									ТипЗначенияПоляТЧ = ОписаниеРеквизита.Тип.Типы()[0];
							КонецЕсли;
						КонецЕсли;
					КонецЕсли;
					ПоследняяСтрока = ТабличныеЧасти[ИмяТабличнойЧасти].Получить(ТабличныеЧасти[ИмяТабличнойЧасти].Количество()-1);
					ПоследняяСтрока[ИмяПоляТЧ] = ?(ЗначениеЗаполнено(ТипЗначенияПоляТЧ), XMLЗначение(ТипЗначенияПоляТЧ, ЧтениеXML.Значение), ЧтениеXML.Значение);
				КонецЕсли;
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
	
	// Из списка реквизитов исключаем табличные части.
	Для Каждого Элемент Из ТабличныеЧасти Цикл
		ЗначенияРеквизитов.Удалить(ЗначенияРеквизитов.Найти(Элемент.Ключ));
	КонецЦикла;
	
	// Заполнение колонок таблицы в случае, когда в объекте табличная часть пустая, и имена колонок не были прочитаны.
	Для Каждого ТабличнаяЧасть Из ТабличныеЧасти Цикл
		ИмяТаблицы = ТабличнаяЧасть.Ключ;
		Таблица = ТабличнаяЧасть.Значение;
		Если Таблица.Колонки.Количество() = 0 Тогда
			МетаданныеТаблицы = МетаданныеТабличнойЧасти(МетаданныеОбъекта, ИмяТаблицы);
			Если МетаданныеТаблицы <> Неопределено Тогда
				Для Каждого ОписаниеКолонки Из РеквизитыТабличнойЧасти(МетаданныеТаблицы) Цикл
					Если Таблица.Колонки.Найти(ОписаниеКолонки.Имя)= Неопределено Тогда
						Таблица.Колонки.Добавить(ОписаниеКолонки.Имя);
					КонецЕсли;
				КонецЦикла;
				Если Метаданные.ПланыСчетов.Содержит(МетаданныеОбъекта) Тогда
					Для Каждого ОписаниеКолонки Из МетаданныеОбъекта.ПризнакиУчетаСубконто Цикл
						Если Таблица.Колонки.Найти(ОписаниеКолонки.Имя)= Неопределено Тогда
							Таблица.Колонки.Добавить(ОписаниеКолонки.Имя);
						КонецЕсли;
					КонецЦикла;
				КонецЕсли;
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
	
	Результат.Вставить("Реквизиты", ЗначенияРеквизитов);
	Результат.Вставить("ТабличныеЧасти", ТабличныеЧасти);
	
	Если Результат.СкрываемыеРеквизиты <> Неопределено Тогда
		Для Каждого ИмяРеквизита Из Результат.СкрываемыеРеквизиты Цикл
			Если СтрЗаканчиваетсяНа(ИмяРеквизита, ".*") Тогда
				ИмяТабличнойЧасти = Лев(ИмяРеквизита, СтрДлина(ИмяРеквизита) - 2);
				Если Результат.ТабличныеЧасти[ИмяТабличнойЧасти] <> Неопределено Тогда
					Результат.ТабличныеЧасти.Удалить(ИмяТабличнойЧасти);
				КонецЕсли;
			Иначе
				НайденныеРеквизиты = Результат.Реквизиты.НайтиСтроки(Новый Структура("НаименованиеРеквизита", ИмяРеквизита));
				Для Каждого Реквизит Из НайденныеРеквизиты Цикл
					Результат.Реквизиты.Удалить(Реквизит);
				КонецЦикла;
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	Если Результат.ДополнительныеРеквизиты <> Неопределено Тогда
		Для Каждого ДополнительныйРеквизит Из Результат.ДополнительныеРеквизиты Цикл
			Реквизит = ЗначенияРеквизитов.Добавить();
			Реквизит.НаименованиеРеквизита = ДополнительныйРеквизит.Наименование;
			Реквизит.ЗначениеРеквизита = ДополнительныйРеквизит.Значение;
			Реквизит.Тип = ТипЗнч(ДополнительныйРеквизит.Значение);
		КонецЦикла;
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

///////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2019, ООО 1С-Софт
// Все права защищены. Эта программа и сопроводительные материалы предоставляются 
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
// Текст лицензии доступен по ссылке:
// https://creativecommons.org/licenses/by/4.0/legalcode
///////////////////////////////////////////////////////////////////////////////////////////////////////

Рекомендации

Похожие публикации

ПослеРазбораВерсииОбъекта (БСП)

ПросклонятьПредставление (БСП)

РазборВерсии (БСП)

ПредставлениеПроблемногоОбъекта (БСП)

ПредставлениеОбъекта (БСП)

ПредставлениеСпискаОбъектов (БСП)

ПредставлениеОбъектаМетаданных (БСП)

ОшибкаРазбораПодтверждения (БСП)

ПредставлениеОбъектаХДТО (БСП)

TurboConf - расширение Конфигуратора 1С