Ошибка рекурсии шаблона - PullRequest
1 голос
/ 19 ноября 2009

Я нахожусь в процессе обновления своих знаний по XST и решил заняться созданием таблицы стилей XSLT 1.0, которая преобразует файлы XMLHelp из компилятора C # в более отформатированную форму.

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

Для начала я хочу включить что-то вроде:

<member name="T:PrimeNumbers.Properties.Resources">

в нечто вроде:

<member type="T">
  <Properties>
    <Resources />
  </Properties>
</member>

Теперь я дошел до написания таблицы стилей, которая выглядит так, как будто она должна выводить другой XML-документ, но, к сожалению, при обработке данных происходит сбой XML Notepad 2007, и IE7 и Firefox 3.5.5 дают мне ошибка в том, что там происходит бесконечная рекурсия.

Буду признателен, если кто-нибудь скажет мне, что я сделал неправильно.

<xsl:transform version="1.0">

  <xsl:output 
    method="xml" version="4.0" encoding="iso-8859-1" 
    indent="yes" media-type="text/xml" 
  />

  <xsl:variable name="AssemblyName" />

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

  <xsl:template match="assembly/name">
    <xsl:variable name="AssemblyName" select="text()" />
    <assembly name="{$AssemblyName}">
      <xsl:apply-templates select="/doc" />
    </assembly>
  </xsl:template>

  <xsl:template match="/doc/members/member">
    <!-- This gives you a single letter 
         (T=Type P=Property M=Method F=Field) -->
    <member type="{substring-before(@name,':')}">
      <xsl:call-template name="RecurseName">
        <!-- This gives you the type name from the beginning 
             of the Namespace to the final local name. -->
        <xsl:with-param name="Path" select="
          substring-after(substring-after(@name,':'),'.')
        " />
      </xsl:call-template>
    </member>
  </xsl:template>

  <xsl:template name="RecurseName">
    <xsl:param name="Path" select="'default'" />
    <xsl:variable name="PathRemainder" select="substring-after($Path,'.')" />
    <xsl:value-of select="$PathRemainder" />
    <xsl:element name="{substring-before($Path,'.')}">
      <xsl:if test="$PathRemainder != ''">
        <xsl:call-template name="RecurseName">
          <xsl:with-param name="Path" select="$PathRemainder" />
        </xsl:call-template>
      </xsl:if>
    </xsl:element>
  </xsl:template>

</xsl:transform>

[Позже я преобразую этот документ так, чтобы, если элементы имеют совпадающие элементы, они объединялись вместе.]

По сути, происходит сбой XML Notepad 2007, если шаблон RecurseName даже существует - без элементов. Обратите внимание, что <xsl:value-of select="$PathRemainder"/> является чистой отладкой.

Есть идеи?

Приложение A: Тест XML

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="XmlDocTemplate3.xml" ?>
<doc>
  <assembly>
    <name>PrimeNumbers</name>
  </assembly>
  <members>
    <member name="T:PrimeNumbers.Properties.Resources">
      <summary>
              A strongly-typed resource class, for looking up localized strings, etc.
            </summary>
    </member>
    <member name="P:PrimeNumbers.Properties.Resources.ResourceManager">
      <summary>
              Returns the cached ResourceManager instance used by this class.
            </summary>
    </member>
    <member name="P:PrimeNumbers.Properties.Resources.Culture">
      <summary>
              Overrides the current thread's CurrentUICulture property for all
              resource lookups using this strongly typed resource class.
            </summary>
    </member>
    <member name="M:PrimeNumbers.Program.Main">
      <summary>
            The main entry point for the application.
            </summary>
    </member>
    <member name="M:PrimeNumbers.PrimeNumberForm.CalculatePrimeNumbers(System.Int32)">
      <summary>
            Calculates the prime numbers between 1 and the (count)th prime number.
            </summary>
      <param name="count">The number of prime numbers to return.</param>
      <returns>List of integers</returns>
      <exception cref="T:System.ArgumentOutOfRangeException">Thrown if <paramref name="count" /> is negative.</exception>
    </member>
    <member name="F:PrimeNumbers.PrimeNumberForm.components">
      <summary>
            Required designer variable.
            </summary>
    </member>
    <member name="M:PrimeNumbers.PrimeNumberForm.Dispose(System.Boolean)">
      <summary>
            Clean up any resources being used.
            </summary>
      <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    </member>
    <member name="M:PrimeNumbers.PrimeNumberForm.InitializeComponent">
      <summary>
            Required method for Designer support - do not modify
            the contents of this method with the code editor.
            </summary>
    </member>
  </members>
</doc>

Ответы [ 3 ]

2 голосов
/ 19 ноября 2009

Вы столкнулись с ошибкой на линии:

<xsl:element name="{substring-before($Path,'.')}">

когда достигнута последняя часть пространства имен, в которой нет точки '.'

Вместо этого вы можете использовать этот фрагмент для шаблона RecurseName:

<xsl:template name="RecurseName">
  <xsl:param name="Path" select="'default'"/>
  <xsl:choose>
    <xsl:when test="contains($Path, '.')">
      <xsl:element name="{substring-before($Path,'.')}">
        <xsl:call-template name="RecurseName">
          <xsl:with-param name="Path" select="substring-after($Path,'.')"/>
        </xsl:call-template>
      </xsl:element>
    </xsl:when>
    <xsl:otherwise>
      <xsl:element name="{$Path}" />
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

Я проверил это. Сгенерировано:

<member type="T">
  <Properties>
    <Resources />
  </Properties>
</member>
1 голос
/ 19 ноября 2009

Я думаю, что эта бесконечная ошибка рекурсии на самом деле вызвана этим шаблоном здесь

<xsl:template match="assembly/name">
  <xsl:variable name="AssemblyName" select="text()"/>
  <assembly name="{$AssemblyName}">
    <xsl:apply-templates select="/doc"/>
  </assembly>
</xsl:template>

Где вы делаете в этом шаблоне, это будет соответствовать самому шаблону, поскольку элемент assembly находится под документ элемент. Попробуйте вместо этого выбрать в / doc / members

<xsl:template match="assembly/name">
  <xsl:variable name="AssemblyName" select="text()"/>
  <assembly name="{$AssemblyName}">
    <xsl:apply-templates select="/doc/members"/>
  </assembly>
</xsl:template>

Это должно по крайней мере остановить бесконечные ошибки рекурсии.

0 голосов
/ 19 ноября 2009

Тим С получил правильный ответ, однако есть несколько других ошибок, которые я исправляю исключительно для потомков. Круглые скобки в именах методов также сломают мой XSLT. Поэтому, прежде чем мы начнем рекурсию, мне нужно сохранить все между '(' и ')' как элемент и отправить строку имени перед первым '(' в рекурсивный шаблон.

<xsl:transform version="1.0">
 <xsl:output method="xml" version="1.0" encoding="iso-8859-1" indent="yes" media-type="text/xml"/>
 <xsl:variable name="AssemblyName"/>
<xsl:template match="/">
 <xsl:apply-templates select="/doc/assembly"/>
 </xsl:template>
<xsl:template match="assembly/name">
 <xsl:variable name="AssemblyName" select="text()"/>
<assembly name="{$AssemblyName}">
 <xsl:apply-templates select="/doc/members"/>
 </assembly>
 </xsl:template>
<xsl:template match="member">
<member type="{substring-before(@name,':')}">
<xsl:choose>
<xsl:when test="contains(@name,'(')">
<params>
 <xsl:value-of select="substring-before(substring-after(@name,'('),')')"/>
 </params>
<xsl:call-template name="RecurseName">
 <xsl:with-param name="Path" select="substring-after(substring-before(substring-after(@name,':'),'('),'.')"/>
 </xsl:call-template>
 </xsl:when>
<xsl:otherwise>
<xsl:call-template name="RecurseName">
 <xsl:with-param name="Path" select="substring-after(substring-after(@name,':'),'.')"/>
 </xsl:call-template>
 </xsl:otherwise>
 </xsl:choose>
 </member>
 </xsl:template>
<xsl:template name="RecurseName">
 <xsl:param name="Path" select="'default'"/>
<xsl:choose>
<xsl:when test="contains($Path,'.')">
<xsl:element name="{substring-before($Path,'.')}">
<xsl:call-template name="RecurseName">
 <xsl:with-param name="Path" select="substring-after($Path,'.')"/>
 </xsl:call-template>
 </xsl:element>
 </xsl:when>
<xsl:otherwise>
 <xsl:element name="{$Path}"/>
 </xsl:otherwise>
 </xsl:choose>
 </xsl:template>
 </xsl:transform>
...