Конструкторы получают настоящий документ, его корень и явно созданный отбор. Проверяется реальный фильтр итератора; счётчики объясняют различие начальных позиций двух способов обхода.
Где и как запускать
Полный клиентский модуль управляемой формы в толстом клиенте; собственное дерево, реальные фильтр, итератор и обход создаются только на клиенте, без серверного вызова.
Подготовка
Создайте собственную новую пустую конфигурацию УчебнаяКанонизацияXML85, управляемое приложение, платформа8.5.1.1343. Не используйте production или базу с чужими обработчиками.
Обработки → ОпытXML85 → управляемая форма Форма, основная форма обработки. Для каждого урока создайте отдельную обработку либо заменяйте целиком модуль формы: три модуля ниже не вставляются друг в друга.
Создайте указанную в уроке команду с одноимённым действием и перетащите её на форму. Код — полный модуль формы; вспомогательную серверную функцию оставьте на уровне модуля.
Нажмите F7 и обновите только новый стенд. runtimeVerifiedScenarios=0: справочник проверялся статически; данный код в 1С здесь не выполнялся.
Запустите новый стенд именно толстым клиентом с управляемым приложением. Всё дерево, фильтр, итератор и обход создаются на клиенте; серверного вызова в этом уроке нет. В тонком или веб-клиенте этот модуль не запускайте.
Исходные данные заданы прямо в коде. Нет внешнего XML, DTD, сети, подписания документов или записи прикладных объектов.
Команда и кнопка СравнитьОбходы85. Реквизиты формы не нужны; products, два product и их тексты создаются в памяти толстого клиента.
Внимательно сравните результаты: итератор перечисляет products;product;product;, а СледующийУзел обхода дерева начинает после текущего products и перечисляет только два product.
&НаКлиенте
Процедура СравнитьОбходы85(Команда)
Сообщить(ОбойтиСвоеДеревоНаКлиенте());
КонецПроцедуры
&НаКлиенте
Функция ОбойтиСвоеДеревоНаКлиенте()
Документ = Новый ДокументDOM("", "products");
Корень = Документ.ЭлементДокумента;
ПервыйТовар = Документ.СоздатьЭлемент("product");
ПервыйТовар.ДобавитьДочерний(Документ.СоздатьТекстовыйУзел("Книга"));
Корень.ДобавитьДочерний(ПервыйТовар);
ВторойТовар = Документ.СоздатьЭлемент("product");
ВторойТовар.ДобавитьДочерний(Документ.СоздатьТекстовыйУзел("Ручка"));
Корень.ДобавитьДочерний(ВторойТовар);
Отбор = Новый ФильтрУзловDOM(, ПараметрыОтбораУзловDOM.ОтображатьЭлемент);
Итератор = Новый ИтераторУзловDOM(Документ, Корень, Отбор, Ложь);
Попытка
ФактическийФильтр = Итератор.Фильтр;
Если Не ФактическийФильтр.ОтображатьЭлемент Или ФактическийФильтр.ОтображатьВсе Тогда
ВызватьИсключение "Итератор должен использовать реальный отбор только элементов";
КонецЕсли;
ЧислоИтератора = 0;
ИменаИтератора = "";
Узел = Итератор.СледующийУзел();
Пока Узел <> Неопределено Цикл
ЧислоИтератора = ЧислоИтератора + 1;
ИменаИтератора = ИменаИтератора + Узел.ИмяУзла + ";";
Узел = Итератор.СледующийУзел();
КонецЦикла;
Исключение
ОшибкаОбхода = ОписаниеОшибки();
Попытка
Итератор.Отсоединить();
Исключение
ОшибкаОбхода = ОшибкаОбхода + " Ошибка отсоединения: " + ОписаниеОшибки();
КонецПопытки;
ВызватьИсключение ОшибкаОбхода;
КонецПопытки;
Итератор.Отсоединить();
Если ЧислоИтератора <> 3 Или ИменаИтератора <> "products;product;product;" Тогда
ВызватьИсключение "От корня итератор должен перечислить три элемента, включая products";
КонецЕсли;
Обход = Новый ОбходДереваDOM(Документ, Корень, Отбор, Ложь);
ЧислоОбхода = 0;
ИменаОбхода = "";
Узел = Обход.СледующийУзел();
Пока Узел <> Неопределено Цикл
ЧислоОбхода = ЧислоОбхода + 1;
ИменаОбхода = ИменаОбхода + Узел.ИмяУзла + ";";
Узел = Обход.СледующийУзел();
КонецЦикла;
Если ЧислоОбхода <> 2 Или ИменаОбхода <> "product;product;" Тогда
ВызватьИсключение "СледующийУзел обхода начинает после текущего корня: два product";
КонецЕсли;
Если ПервыйТовар.ТекстовоеСодержимое <> "Книга" Или ВторойТовар.ТекстовоеСодержимое <> "Ручка" Тогда
ВызватьИсключение "Обход не должен менять исходные данные";
КонецЕсли;
Возврат "Итератор: products;product;product;. Обход со СледующийУзел: product;product;. Текстовые узлы не попали в отбор, исходные данные сохранены.";
КонецФункцииРазбор по шагам
Документ создаёт собственный products, два product и два текста. Каждый ребёнок создаётся тем же документом; внешних узлов нет.
ФильтрУзловDOM создаётся через параметр ОтображатьЭлемент: передаётся значение ПараметрыОтбораУзловDOM, не Булево. Пропущенный первый параметр не включает ОтображатьВсе.
Новый ИтераторУзловDOM получает документ, реальный корень, фильтр и Ложь для обхода сущностей. Его readonly Фильтр читается и проверяется по фактическим признакам; попыток записать это свойство нет.
Первый СледующийУзел итератора возвращает корень, затем два product. Тексты исключены фильтром. Цикл заканчивается на Неопределено, не по заранее придуманному числу.
Итератор отсоединяется после чтения и при ошибке. После Отсоединить следующие узлы не запрашиваются; первичная ошибка и ошибка освобождения не скрываются.
Новый ОбходДереваDOM получает те же реальные параметры. Его СледующийУзел переходит от текущего корня дальше, поэтому два product, а не три элемента. Исходные тексты проверяются после обоих обходов.
Как проверить результат
Фактический фильтр: ОтображатьЭлемент=Истина, ОтображатьВсе=Ложь.
Итератор: три элемента и имена products;product;product;.
Обход со СледующийУзел: два элемента и имена product;product;.
Книга и Ручка остаются исходными текстами; DOM не изменяется обходом.
Ожидаемый результат
Два корректных способа обхода имеют разные начальные позиции; фильтр исключает текстовые узлы и исходные данные не меняются.
На что обратить внимание
Слово По умолчанию в названии конструктора не означает нулевое число аргументов. Документ обязателен для обоих конструкторов.
Фильтр и его признаки readonly. Для другого набора узлов создайте новый фильтр с перечислениями; не присваивайте Истина его свойствам.
Корневой узел должен принадлежать переданному документу. Не передавайте внешнее дерево или неизвестный входной объект.
Подсчёт3/2 относится только к этому дереву и этим начальным операциям. Чтобы учитывать корень обхода дерева, обработайте его отдельно до СледующийУзел.
После отсоединения итератор больше не используют. У обхода дерева не придумывается несуществующий метод Отсоединить.
Ложь для сущностей не является универсальным отключением внешних ресурсов при XML-разборе. Здесь разбора XML вообще нет, документ создаётся вручную.
В установленной справке у ПараметрыОтбораУзловDOM сервер не указан, зато явно указан толстый клиент. Поэтому урок выполняется целиком толстым клиентом; не переносите его на сервер только потому, что отдельные DOM-объекты там доступны.
Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.