Разбор данных XSLT / Xpath для получения списка узлов XML - PullRequest
1 голос
/ 02 марта 2012

Я ищу библиотеку или инструмент или даже какой-то простой код, который может анализировать данные Xpath / XSLT в наших файлах XSLT, чтобы создать словарь / список / дерево всех узлов XML, с которыми XSLT ожидает работать или находить. К сожалению, все, что я нахожу, имеет дело с использованием XSLT для анализа XML, а не с разбором XSLT. И действительно сложная часть, с которой я сталкиваюсь, это насколько гибкий XPath.

Например, в нескольких файлах XSLT, с которыми мы работаем, можно выбрать

nodeX/nodeY/nodeNeeded;

OR

../nodeNeeded;

OR

выберите nodeX, затем выберите nodeY, затем выберите nodeNeeded; и пр.

То, что мы хотели бы сделать, - это иметь возможность проанализировать этот документ XSLT и получить структуру данных такого рода, которая явно говорит нам о том, что XSLT ищет nodeNeeded в path nodeX, nodeY, чтобы мы могли настраивать сборку XML данные в стиле минимализма

Спасибо!

Вот макет подмножества данных для целей визуализации:

<server_stats>
    <server name="fooServer">
        <uptime>24d52m</uptime>
        <userCount>123456</userCount>
        <loggedInUsers>
            <user name="AnnaBannana">
                <created>01.01.2012:00.00.00</created>
                <loggedIn>25</loggedIn>
                <posts>3</posts>
             </user>
         </loggedInUsers>
         <temperature>82F</temperature>
         <load>72</load>
         <mem_use>45</mem_use>
         <visitors>
             <current>42</current>
             <browsers name="mozilla" version="X.Y.Z">22</browsers>
             <popular_link name="index.html">39</popular_link>
             <history>
                 <max_visitors>789</max_visitors>
                 <average_visitors>42</average_visitors>
             </history>
         </visitors>
     </server>
 </server_stats>

Исходя из этого, один из клиентов может просто захотеть создать HTML-страницу администратора, где он извлекает статистику оборудования из дерева и, возможно, запускает некоторые расчеты нагрузки из числа посетителей. Другой клиент может просто захотеть получить информацию о количестве посетителей для отображения на своем общедоступном сайте. Чтобы система загрузки каждого из этих клиентов была как можно меньше, мы хотели бы проанализировать их статистику, выбирая XSLT, и предоставить им только те данные, которые им нужны (что было запрошено). Очевидно, проблема заключается в том, что один клиент может выполнить прямой выбор на узле подсчета посетителей, а другой может выбрать узел посетителей и выбрать каждый из дочерних узлов, которые он хочет, и т. Д.

У двух гипотетических клиентов, ищущих «текущий» узел в «посетителях», XSLT может выглядеть следующим образом:

<xsl:template match="server_stats/server/visitors">
    <xsl:value-of select="current"/>
</xsl:template>

OR

<xsl:template match="server_stats">
     <xsl:for-each select="server">
          <xsl:value-of select="visitors/current"/>
          <xsl:value-of select="visitors/popular_link"/>
     </xsl:for-each>
</xsl:template>

В этом примере оба пытаются выбрать один и тот же узел, но способ, которым они это делают, отличается, и «current» не настолько специфичен, поэтому нам также нужен путь, который они использовали для этого, поскольку «current» может быть узлами для несколько предметов. Это мешает нам просто искать «current» в их XSLT, и поскольку способ доступа к пути может сильно отличаться, мы не можем просто искать весь путь.

Итак, мы хотели бы проанализировать их XSLT и дать нам список статистики:

Customer 1:
visitors/current
Customer 2:
visitors/current
visitors/popular_link

и т.д.

В некоторых примерах выбирается решение, приведенное ниже, над которым мы будем работать:

<xsl:variable name="fcolor" select="'Black'"/> results in a /'Black' entry
<xsl:for-each select="server"> we get the entry, but its children don't show it anymore
<xsl:value-of select="../../@name"/>  This was kind of expected, we can try to figure out how to skip attribute based selections but the relative paths show up as I thought they would
<xsl:when test="substring(someNode,1,2)=0 and substring(someNode,4,2)=0 and substring(someNode,7,2)>30">  This one is kind of throwing me, because this shows up as a path item, it's due to the when check in the solution but I don't see any nice solution since the same basic statement could have been checking for a branching path, so this might just be one of those cases we need to post-process or something of that nature.

Ответы [ 2 ]

0 голосов
/ 02 марта 2012

Нереально пытаться реконструировать структуру исходного XML-документа, просто взглянув на XSLT-преобразование, которое работает с этим документом .

Большинство XSLT-преобразований работают с классом XMLдокументы - до одного конкретного типа документа.

Например, ниже приведено одно из наиболее часто используемых XSLT-преобразований :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="node()|@*">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

Из этого ничего не может быть выведеноэто преобразование структуры XML-документа (ов), которые он обрабатывает.

Существует огромное разнообразие преобразований, которые просто переопределяют шаблон из приведенного выше преобразования.

Например,это полезное преобразование, которое переименовывает любой элемент, имеющий определенное имя, указанное во внешнем параметре :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:param name="pName"/>
 <xsl:param name="pNewName"/>

 <xsl:template match="node()|@*" name="identity">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="*">
  <xsl:if test="not(name() = $pName)">
   <xsl:call-template name="identity"/>
  </xsl:if>

  <xsl:element name="{$pNewName}">
   <xsl:apply-templates select="node()|@*"/>
  </xsl:element>
 </xsl:template>
</xsl:stylesheet>

Еще раз, абсолютно ничего нельзя сказать об именах и структуреисходный XML-документ.

ОБНОВЛЕНИЕ :

Возможно, что-то вроде этого:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="xsl:template[@match]">
  <xsl:variable name="vPath" select="string(@match)"/>

  <xsl:value-of select="concat('&#xA;', $vPath)"/>

  <xsl:apply-templates select="*">
   <xsl:with-param name="pPath" select="$vPath"/>
  </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="*">
  <xsl:param name="pPath"/>

  <xsl:apply-templates select="*">
   <xsl:with-param name="pPath" select="$pPath"/>
  </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="xsl:for-each">
  <xsl:param name="pPath"/>

  <xsl:variable name="vPath">
   <xsl:choose>
    <xsl:when test="starts-with(@select, '/')">
      <xsl:value-of select="@select"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="concat($pPath, '/', @select)"/>
    </xsl:otherwise>
   </xsl:choose>
  </xsl:variable>

  <xsl:value-of select="concat('&#xA;', $vPath)"/>

  <xsl:apply-templates select="*">
   <xsl:with-param name="pPath" select="$vPath"/>
  </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="xsl:if | xsl:when">
  <xsl:param name="pPath"/>

  <xsl:variable name="vPath">
   <xsl:choose>
    <xsl:when test="starts-with(@test, '/')">
      <xsl:value-of select="@test"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="concat($pPath, '/', @test)"/>
    </xsl:otherwise>
   </xsl:choose>
  </xsl:variable>

  <xsl:value-of select="concat('&#xA;', $vPath)"/>

  <xsl:apply-templates select="*">
   <xsl:with-param name="pPath" select="$pPath"/>
  </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="*[@select]">
  <xsl:param name="pPath"/>

  <xsl:variable name="vPath">
   <xsl:choose>
    <xsl:when test="starts-with(@select, '/')">
      <xsl:value-of select="@select"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="concat($pPath, '/', @select)"/>
    </xsl:otherwise>
   </xsl:choose>
  </xsl:variable>

  <xsl:value-of select="concat('&#xA;', $vPath)"/>

  <xsl:apply-templates select="*">
   <xsl:with-param name="pPath" select="$pPath"/>
  </xsl:apply-templates>
 </xsl:template>
</xsl:stylesheet>

Когда это преобразование применяется к следующей таблице стилей XSLT :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

    <xsl:template match="/">
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="server_stats">
        <xsl:for-each select="server">
            <xsl:value-of select="visitors/current"/>
            <xsl:value-of select="visitors/popular_link"/>

            <xsl:for-each select="site">
              <xsl:value-of select="defaultPage/Url"/>
            </xsl:for-each>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

, получается следующий требуемый результат :

/
server_stats
server_stats/server
server_stats/visitors/current
server_stats/visitors/popular_link
server_stats/site
server_stats/defaultPage/Url

Примечание : Такой анализ не только неполон, но и его следует рассматривать с точки зрения соли.Это результаты статического анализа .На практике может случиться так, что из 100 путей только 5-6 из них будут доступны в 99% случаев.Статический анализ не может дать вам такую ​​информацию.Инструменты динамического анализа (похожие на профилировщики) могут возвращать гораздо более точную и полезную информацию.

0 голосов
/ 02 марта 2012

Это будет непросто, потому что XSLT зависит от контекста. Вы правильно называете это "разбором", потому что вам придется дублировать большую часть логики, которая будет использоваться в синтаксическом анализаторе.

Мое предложение состояло бы в том, чтобы начать с подхода грубой силы и усовершенствовать его, когда вы найдете больше тестовых случаев, которые он не может обработать. Посмотрите на пару файлов XSLT и напишите код, который может найти структуры, которые вы ищете. Посмотрите еще немного, и если появятся какие-либо новые структуры, уточните свой код, чтобы найти их тоже.

Это не найдет все возможные способы использования XSLT и XPath, как это может сделать чисто эмпирический подход к анализу этих файлов, но это будет гораздо меньший проект, и он найдет структуры, которые, как правило, разработчики будут использовать ,

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...