Вот метод XSLT 2.0.
Предполагая, что $docs
содержит последовательность узлов документов, которые вы хотите отсканировать, вы хотите создать одну строку для каждого элемента, который появляется в документах. Вы можете использовать <xsl:for-each-group>
для этого:
<xsl:for-each-group select="$docs//*" group-by="name()">
<xsl:sort select="current-group-key()" />
<xsl:variable name="name" as="xs:string" select="current-grouping-key()" />
<xsl:value-of select="$name" />
...
</xsl:for-each-group>
Затем вы хотите узнать статистику по этому элементу среди документов. Сначала найдите документы, в которых есть элемент с таким названием:
<xsl:variable name="docs-with" as="document-node()+"
select="$docs[//*[name() = $name]" />
Во-вторых, вам нужна последовательность количества элементов с таким именем в каждом из документов:
<xsl:variable name="elem-counts" as="xs:integer+"
select="$docs-with/count(//*[name() = $name])" />
И теперь вы можете делать расчеты. Среднее, минимальное и максимальное значения можно рассчитать с помощью функций avg()
, min()
и max()
. Процент - это просто количество документов, содержащих элемент, разделенное на общее количество форматированных документов.
Собираем это вместе:
<xsl:for-each-group select="$docs//*" group-by="name()">
<xsl:sort select="current-group-key()" />
<xsl:variable name="name" as="xs:string" select="current-grouping-key()" />
<xsl:variable name="docs-with" as="document-node()+"
select="$docs[//*[name() = $name]" />
<xsl:variable name="elem-counts" as="xs:integer+"
select="$docs-with/count(//*[name() = $name])" />
<xsl:value-of select="$name" />
<xsl:text>* </xsl:text>
<xsl:value-of select="format-number(avg($elem-counts), '#,##0.0')" />
<xsl:text> </xsl:text>
<xsl:value-of select="format-number(min($elem-counts), '#,##0')" />
<xsl:text> </xsl:text>
<xsl:value-of select="format-number(max($elem-counts), '#,##0')" />
<xsl:text> </xsl:text>
<xsl:value-of select="format-number((count($docs-with) div count($docs)) * 100, '#0')" />
<xsl:text>%</xsl:text>
<xsl:text>
</xsl:text>
</xsl:for-each-group>
То, что я не сделал здесь, это отступ строк в соответствии с глубиной элемента. Я только что упорядочил элементы в алфавитном порядке, чтобы дать вам статистику. Для этого есть две причины: во-первых, значительно сложнее (например, слишком сложно писать здесь) отображать статистику элементов в некой структуре, отражающей их вид в документах, не в последнюю очередь потому, что разные документы могут иметь разные структуры. Во-вторых, во многих языках разметки точная структура документов не может быть известна (поскольку, например, разделы могут вкладываться в разделы любой глубины).
Я надеюсь, что это полезно, тем не менее.
UPDATE:
Нужна оболочка XSLT и некоторые инструкции для запуска XSLT? ХОРОШО. Во-первых, возьмите в руки саксонский 9B .
Вам нужно будет поместить все файлы, которые вы хотите проанализировать, в каталог. Saxon позволяет вам получить доступ ко всем файлам в этом каталоге (или его подкаталогам), используя коллекцию, используя специальный синтаксис URI . Стоит взглянуть на этот синтаксис, если вы хотите выполнять рекурсивный поиск или фильтровать файлы, по которым вы просматриваете, по имени файла.
Теперь полный XSLT:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:param name="dir" as="xs:string"
select="'file:///path/to/default/directory?select=*.xml'" />
<xsl:output method="text" />
<xsl:variable name="docs" as="document-node()*"
select="collection($dir)" />
<xsl:template name="main">
<xsl:for-each-group select="$docs//*" group-by="name()">
<xsl:sort select="current-group-key()" />
<xsl:variable name="name" as="xs:string" select="current-grouping-key()" />
<xsl:variable name="docs-with" as="document-node()+"
select="$docs[//*[name() = $name]" />
<xsl:variable name="elem-counts" as="xs:integer+"
select="$docs-with/count(//*[name() = $name])" />
<xsl:value-of select="$name" />
<xsl:text>* </xsl:text>
<xsl:value-of select="format-number(avg($elem-counts), '#,##0.0')" />
<xsl:text> </xsl:text>
<xsl:value-of select="format-number(min($elem-counts), '#,##0')" />
<xsl:text> </xsl:text>
<xsl:value-of select="format-number(max($elem-counts), '#,##0')" />
<xsl:text> </xsl:text>
<xsl:value-of select="format-number((count($docs-with) div count($docs)) * 100, '#0')" />
<xsl:text>%</xsl:text>
<xsl:text>
</xsl:text>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
И чтобы запустить его, вы должны сделать что-то вроде:
> java -jar path/to/saxon.jar -it:main -o:report.txt dir=file:///path/to/your/directory?select=*.xml
Это говорит Saxon запустить процесс с шаблоном с именем main
, установить для параметра dir
значение file:///path/to/your/directory?select=*.xml
и отправить вывод в report.txt
.