Это сложно, потому что это включает в себя взгляд вперед.Рассмотрим
<a>
<b/>
<c/>
<d/>
<z>23</z>
</a>
Вы не знаете, следует ли удалять элемент <a>
, пока не увидите элемент <z/>
.Так что это определенно не чисто трансформируемое преобразование.
То, что вы могли бы сделать за один проход, это составить список всех элементов, которые должны быть удалены.
Это было бы полезнознать, ожидаете ли вы устранить очень много или очень мало элементов;в первом случае первый проход должен собирать ссылки на элементы, которые должны быть сохранены, во втором случае он должен собирать ссылки на элементы, которые должны быть отброшены.
Я думаю, что это другой способ выраженияваше требование: исключить любой элемент, который не является предком хотя бы одного непробельного текстового узла.
В преобразовании XSLT 3.0 с достаточно легко собирать пути всех предков непробельных текстовых узлов:
//text()[normalize-space()] ! ancestor::* ! path(.)
Единственная проблема заключается в том, что без каких-либо объемов я не знаю, является ли этот список невероятно большим.Вы можете удалять дубликаты по ходу дела, помещая это в выражение карты:
map:merge(//text()[normalize-space()] ! ancestor::* ! path(.) ! map{.:1},
map{'duplicates':'use-first'})
После построения этого списка достаточно просто выполнить потоковое преобразование, исключающее элементы, которых нет в списке:
<xsl:mode streamable="yes" on-no-match="shallow-copy"/>
<xsl:template match="*[not(map:contains($retained-path, path(.))]"/>
Как я уже сказал, проблема в том, что список сохраняемых узлов может стать очень большим.
Другой подход состоит в том, чтобы попытаться создать список путей элементов, которые должны быть отброшены.Алгоритм для этого может быть следующим: при обнаружении начального тега элемента добавьте элемент в список кандидатов на исключение;когда вы встретите текстовый узел без пробелов, удалите всех его предков из списка.Проблема заключается в том, что, как выражено здесь, для списка требуется изменяемая структура данных.Это делает его кандидатом на аккумуляторы XSLT 3.0:
<xsl:accumulator name="dropped-elements" as="map(xs:string, xs:integer)">
<xsl:accumulator-rule match="*" select="map:merge($value, map{path(.), 1}"/>
<xsl:accumulator-rule match="text()[normalize-space()]
select="map:remove($value, ancestor::*!path(.))"/>
</xsl:accumulator>
, а затем в конце обработки map:keys(accumulator-after('dropped-elements'))
дает вам пути элементов, которые должны быть отброшены.
Все непроверенные: я надеюсьэто дает вам некоторые идеи.