Во-первых, требования:
(a) в вашем примере нет ни одного случая, когда существует более одного элемента или атрибута с одним и тем же путем, поэтому неясно, хотите ли вы удалить дубликаты.
(b) ваш выходной XML довольно сложно обработать, потому что пути к элементам и имена атрибутов не связаны, кроме как относительной позицией.Было бы лучше добавить элемент обертки, скажем, <PATH>
.
(c) в ваш вывод, число раз, когда путь к элементу появляется, равно N, где N - это количество найденных атрибутов, за исключением того, что онравен 1, когда нет атрибутов.Это кажется немного противоречивым для меня.Я бы предложил вывод вида
<ROOT>
<PATH>
<ELEMENT>/BOOK</ELEMENT>
<ATTRIBUTE>id</ATTRIBUTE>
<ATTRIBUTE>chapters</ATTRIBUTE>
</PATH>
<PATH>
<ELEMENT>/BOOK/AUTHOR</ELEMENT>
<ATTRIBUTE>gender<ATTRIBUTE>
<ATTRIBUTE>age</ATTRIBUTE>
<PATH>
<ELEMENT>/BOOK/AUTHOR/NAME</ELEMENT>
</PATH>
<PATH>
<ELEMENT>/BOOK/AUTHOR/TITLE</ELEMENT>
</PATH>
</ROOT>
Если я прав, думая, что вы хотите исключить дубликаты, тогда это проблема группировки, и поэтому в XSLT 2.0+ это намного проще.На самом деле вы не указали никаких ограничений на версию XSLT, но я собираюсь предположить, что XSLT 2.0.Обратите внимание, что существует множество процессоров XSLT, которые поддерживают только 1.0, хотя 2.0 уже более десяти лет.
Сначала нам нужна функция, которая дает путь к элементу:
<xsl:function name="f:path" as="xs:string">
<xsl:param name="node" as="node()"/>
<xsl:choose>
<xsl:when test="exists($node/..)">
<xsl:sequence select="concat(f:path($node/..), '/', local-name($node)"/>
</xsl:when>
<xsl:otherwise>/</xsl:otherwise>
</xsl:choose>
</xsl:function>
Теперь начинается группировка:
<xsl:for-each-group select="//*" group-by="f:path(.)">
<PATH>
<ELEMENT><xsl:value-of select="current-grouping-key()"/></ELEMENT>
<xsl:for-each-group select="current-group()/@*" group-by="local-name()">
<ATTRIBUTE><xsl:value-of select="local-name()"/></ATTRIBUTE>
</xsl:for-each-group>
</PATH>
</xsl:for-each-group>