使用 xslt 将简单的 markdown(string) 转换为 html

发布于 2024-09-15 19:26:05 字数 3851 浏览 8 评论 0原文

我正在将我的 XSLT 样式表转换为文档,并且我希望在每个代码块的注释节点中获得丰富的体验,因此我想将以下注释和输出转换为 xhtml:

String:

# This is a title with __bold__ text and *italic* #
This is just a normal line

- list point with some __bold__
- list point with a "link"[http://www.stackoverflow.com]

Wanted output:

<h1> This is a title with <strong>bold</strong> and <span>italic</span> </h1>
<p>This is a normal line</p>

<ul>
  <li>list point with some <strong>bold</strong></li>
  <li>list point with a <a href="http://www.stackoverflow.com">link</a></li>
</ul>

I attempts with a recursive函数从规则集中递归地使用 xsl:analyze-string,但找不到真正有效的解决方案。

最近有人这样做过吗,或者是否有一些框架具有执行此操作的功能?

提前致谢! :)

编辑:添加了一个肮脏的例子:

<!-- Output comments -->
<xsl:template match="comment()" mode="COMMENT">
    <xsl:copy-of select="ips:groupReplace(normalize-space(.), 
      '
      (.*)(\n|\r)(.*),
      (.*)\*(.*)\*(.*),
      (.*)\*\*(.*)\*\*(.*),
      (.*)__(.*)__(.*), 
      (.*)#(.*)#(.*),
      (.*)-(.*)
      ',
      '
      br,
      span.italic,
      span.bold,
      strong,
      h1,
      li
      ')" />
</xsl:template>

<!-- Initializing the iterateRegex function -->
<xsl:function name="ips:groupReplace">
  <xsl:param name="string" as="xs:string" />
  <xsl:param name="search" />
  <xsl:param name="replace" />
  <xsl:variable name="regex" select="tokenize($search, ',')" />
  <xsl:variable name="replacements" select="tokenize($replace, ',')" />
  <xsl:copy-of select="ips:iterateRegex(count($replacements), $string, $regex, $replacements)" />
</xsl:function>

<!-- Iterate each regex -->
<xsl:function name="ips:iterateRegex">
  <xsl:param name="counter" />
  <xsl:param name="string" />
  <xsl:param name="list_regex" />
  <xsl:param name="list_replace" />
  <xsl:variable name="newStr">
    <xsl:analyze-string select="$string" regex="{normalize-space($list_regex[$counter])}" flags="xm">
      <xsl:matching-substring>
            <xsl:variable name="cc" select="contains($list_replace[$counter], '.')" />
            <xsl:variable name="tag" select="normalize-space(if ($cc) then (substring-before($list_replace[$counter], '.')) else ($list_replace[$counter]))" />
            <xsl:copy-of select="regex-group(1)" />
            <xsl:choose>
              <xsl:when test="normalize-space(regex-group(2)) = ''">
                <xsl:element name="{$tag}" />
              </xsl:when>
              <xsl:otherwise>
                <xsl:element name="{$tag}" >
                  <xsl:if test="$cc">
                    <xsl:attribute name="class" select="substring-after($list_replace[$counter],'.')" />  
                  </xsl:if>
                  <xsl:copy-of select="regex-group(2)" />
                </xsl:element>
              </xsl:otherwise>
            </xsl:choose>
            <xsl:copy-of select="regex-group(3)" />
      </xsl:matching-substring>
      <xsl:non-matching-substring>
        <xsl:copy-of select="." />
      </xsl:non-matching-substring>
    </xsl:analyze-string>
  </xsl:variable>
  <xsl:variable name="count" select="number($counter) - 1" />
  <xsl:choose>
    <xsl:when test="$count &gt; 0">
      <xsl:copy-of select="ips:iterateRegex($count, $newStr, $list_regex, $list_replace)" />      
    </xsl:when>
    <xsl:otherwise>
      <xsl:copy-of select="$newStr" />
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

I'm transforming my XSLT-stylesheets into documentation, and I want a rich experience within the comment nodes for each code-chunk, therefore I want to convert the following comment and output as xhtml:

String:

# This is a title with __bold__ text and *italic* #
This is just a normal line

- list point with some __bold__
- list point with a "link"[http://www.stackoverflow.com]

Wanted output:

<h1> This is a title with <strong>bold</strong> and <span>italic</span> </h1>
<p>This is a normal line</p>

<ul>
  <li>list point with some <strong>bold</strong></li>
  <li>list point with a <a href="http://www.stackoverflow.com">link</a></li>
</ul>

I tried with a recursive function that uses xsl:analyze-string recursively from a ruleset, but can't find a solution that works really well.

Anyone have done this lately, or is there some frameworks out there that has functions to do this ?

thanx in advance! :)

Edit: Added one dirty example:

<!-- Output comments -->
<xsl:template match="comment()" mode="COMMENT">
    <xsl:copy-of select="ips:groupReplace(normalize-space(.), 
      '
      (.*)(\n|\r)(.*),
      (.*)\*(.*)\*(.*),
      (.*)\*\*(.*)\*\*(.*),
      (.*)__(.*)__(.*), 
      (.*)#(.*)#(.*),
      (.*)-(.*)
      ',
      '
      br,
      span.italic,
      span.bold,
      strong,
      h1,
      li
      ')" />
</xsl:template>

<!-- Initializing the iterateRegex function -->
<xsl:function name="ips:groupReplace">
  <xsl:param name="string" as="xs:string" />
  <xsl:param name="search" />
  <xsl:param name="replace" />
  <xsl:variable name="regex" select="tokenize($search, ',')" />
  <xsl:variable name="replacements" select="tokenize($replace, ',')" />
  <xsl:copy-of select="ips:iterateRegex(count($replacements), $string, $regex, $replacements)" />
</xsl:function>

<!-- Iterate each regex -->
<xsl:function name="ips:iterateRegex">
  <xsl:param name="counter" />
  <xsl:param name="string" />
  <xsl:param name="list_regex" />
  <xsl:param name="list_replace" />
  <xsl:variable name="newStr">
    <xsl:analyze-string select="$string" regex="{normalize-space($list_regex[$counter])}" flags="xm">
      <xsl:matching-substring>
            <xsl:variable name="cc" select="contains($list_replace[$counter], '.')" />
            <xsl:variable name="tag" select="normalize-space(if ($cc) then (substring-before($list_replace[$counter], '.')) else ($list_replace[$counter]))" />
            <xsl:copy-of select="regex-group(1)" />
            <xsl:choose>
              <xsl:when test="normalize-space(regex-group(2)) = ''">
                <xsl:element name="{$tag}" />
              </xsl:when>
              <xsl:otherwise>
                <xsl:element name="{$tag}" >
                  <xsl:if test="$cc">
                    <xsl:attribute name="class" select="substring-after($list_replace[$counter],'.')" />  
                  </xsl:if>
                  <xsl:copy-of select="regex-group(2)" />
                </xsl:element>
              </xsl:otherwise>
            </xsl:choose>
            <xsl:copy-of select="regex-group(3)" />
      </xsl:matching-substring>
      <xsl:non-matching-substring>
        <xsl:copy-of select="." />
      </xsl:non-matching-substring>
    </xsl:analyze-string>
  </xsl:variable>
  <xsl:variable name="count" select="number($counter) - 1" />
  <xsl:choose>
    <xsl:when test="$count > 0">
      <xsl:copy-of select="ips:iterateRegex($count, $newStr, $list_regex, $list_replace)" />      
    </xsl:when>
    <xsl:otherwise>
      <xsl:copy-of select="$newStr" />
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

你没皮卡萌 2024-09-22 19:26:05

我想你需要一个解析器。因此,这个样式表实现了一个详细的样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="text" name="block">
        <xsl:param name="pString" select="."/>
        <xsl:if test="$pString != ''">
            <xsl:choose>
                <xsl:when test="starts-with($pString,'#')">
                    <xsl:call-template name="header">
                        <xsl:with-param name="pString"
                        select="substring($pString,2)"/>
                    </xsl:call-template>
                </xsl:when>
                <xsl:when test="starts-with($pString,'
')">
                    <xsl:call-template name="list">
                        <xsl:with-param name="pString"
                        select="substring($pString,2)"/>
                    </xsl:call-template>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:call-template name="paragraph">
                        <xsl:with-param name="pString"
                                              select="$pString"/>
                    </xsl:call-template>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:if>
    </xsl:template>
    <xsl:template name="header">
        <xsl:param name="pString"/>
        <xsl:variable name="vInside"
        select="substring-before($pString,'#
')"/>
        <xsl:choose>
            <xsl:when test="$vInside != ''">
                <h1>
                    <xsl:call-template name="inline">
                        <xsl:with-param name="pString" select="$vInside"/>
                    </xsl:call-template>
                </h1>
                <xsl:call-template name="block">
                    <xsl:with-param name="pString"
                    select="substring-after($pString,'#
')"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:call-template name="paragraph">
                    <xsl:with-param name="pString" 
                                     select="concat('#',$pString)"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template name="list">
        <xsl:param name="pString"/>
        <xsl:variable name="vCheckList" select="starts-with($pString,'- ')"/>
        <xsl:choose>
            <xsl:when test="$vCheckList">
                <ul>
                    <xsl:call-template name="listItem">
                        <xsl:with-param name="pString" select="$pString"/>
                    </xsl:call-template>
                </ul>
                <xsl:call-template name="block">
                    <xsl:with-param name="pString">
                        <xsl:call-template name="afterlist">
                            <xsl:with-param name="pString" select="$pString"/>
                        </xsl:call-template>
                    </xsl:with-param>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:call-template name="block">
                    <xsl:with-param name="pString" select="$pString"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template name="paragraph">
        <xsl:param name="pString"/>
        <xsl:choose>
            <xsl:when test="contains($pString,'
')">
                <p>
                    <xsl:value-of select="substring-before($pString,'
')"/>
                </p>
            </xsl:when>
            <xsl:otherwise>
                <p>
                    <xsl:value-of select="$pString"/>
                </p>
            </xsl:otherwise>
        </xsl:choose>
        <xsl:call-template name="block">
            <xsl:with-param name="pString"
            select="substring-after($pString,'
')"/>
        </xsl:call-template>
    </xsl:template>
    <xsl:template name="afterlist">
        <xsl:param name="pString"/>
        <xsl:choose>
            <xsl:when test="starts-with($pString,'- ')">
                <xsl:call-template name="afterlist">
                    <xsl:with-param name="pString"
                    select="substring-after($pString,'
')"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$pString"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template name="listItem">
        <xsl:param name="pString"/>
        <xsl:if test="starts-with($pString,'- ')">
            <li>
                <xsl:call-template name="inline">
                    <xsl:with-param name="pString"
                    select="substring-before(substring($pString,3),'
')"/>
                </xsl:call-template>
            </li>
            <xsl:call-template name="listItem">
                <xsl:with-param name="pString"
                select="substring-after($pString,'
')"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>
    <xsl:template name="inline">
        <xsl:param name="pString" select="."/>
        <xsl:if test="$pString != ''">
            <xsl:choose>
                <xsl:when test="starts-with($pString,'__')">
                    <xsl:call-template name="strong">
                        <xsl:with-param name="pString"
                        select="substring($pString,3)"/>
                    </xsl:call-template>
                </xsl:when>
                <xsl:when test="starts-with($pString,'*')">
                    <xsl:call-template name="span">
                        <xsl:with-param name="pString"
                        select="substring($pString,2)"/>
                    </xsl:call-template>
                </xsl:when>
                <xsl:when test="starts-with($pString,'"')">
                    <xsl:call-template name="link">
                        <xsl:with-param name="pString"
                        select="substring($pString,2)"/>
                    </xsl:call-template>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="substring($pString,1,1)"/>
                    <xsl:call-template name="inline">
                        <xsl:with-param name="pString"
                        select="substring($pString,2)"/>
                    </xsl:call-template>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:if>
    </xsl:template>
    <xsl:template name="strong">
        <xsl:param name="pString"/>
        <xsl:variable name="vInside" select="substring-before($pString,'__')"/>
        <xsl:choose>
            <xsl:when test="$vInside != ''">
                <strong>
                    <xsl:value-of select="$vInside"/>
                </strong>
                <xsl:call-template name="inline">
                    <xsl:with-param name="pString"
                    select="substring-after($pString,'__')"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="'__'"/>
                <xsl:call-template name="inline">
                    <xsl:with-param name="pString" select="$pString"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template name="span">
        <xsl:param name="pString"/>
        <xsl:variable name="vInside" select="substring-before($pString,'*')"/>
        <xsl:choose>
            <xsl:when test="$vInside != ''">
                <span>
                    <xsl:value-of select="$vInside"/>
                </span>
                <xsl:call-template name="inline">
                    <xsl:with-param name="pString"
                    select="substring-after($pString,'*')"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="'*'"/>
                <xsl:call-template name="inline">
                    <xsl:with-param name="pString" select="$pString"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template name="link">
        <xsl:param name="pString"/>
        <xsl:variable name="vInside" 
               select="substring-before($pString,'"')"/>
        <xsl:choose>
            <xsl:when test="$vInside != ''">
                <xsl:call-template name="href">
                    <xsl:with-param name="pString"
                    select="substring-after($pString,'"')"/>
                    <xsl:with-param name="pInside" select="$vInside"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="'"'"/>
                <xsl:call-template name="inline">
                    <xsl:with-param name="pString" select="$pString"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template name="href">
        <xsl:param name="pString"/>
        <xsl:param name="pInside"/>
        <xsl:variable name="vHref"
        select="substring-before(substring($pString,2),']')"/>
        <xsl:choose>
            <xsl:when test="starts-with($pString,'[') and $vHref != ''">
                <a href="{$vHref}">
                    <xsl:value-of select="$pInside"/>
                </a>
                <xsl:call-template name="inline">
                    <xsl:with-param name="pString"
                    select="substring-after($pString,']')"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="concat('"',$pInside,'"')"/>
                <xsl:call-template name="inline">
                    <xsl:with-param name="pString" select="$pString"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

使用此输入:

<text>
# This is a title with __bold__ text and *italic* #
This is just a normal line

- list point with some __bold__
- list point with a "link"[http://www.stackoverflow.com]
</text>

输出:

<h1> This is a title with 
    <strong>bold</strong> text and 
    <span>italic</span>
</h1>
<p>This is just a normal line</p>
<ul>
    <li>list point with some 
        <strong>bold</strong>
    </li>
    <li>list point with a 
        <a href="http://www.stackoverflow.com">link</a>
    </li>
</ul>

注意:查看有多少模板是相似的(它们遵循某种模式),因此可以对这些模板进行参数化。在这种情况下我没有这样做,因为似乎有更多问题需要某种解析器,所以到本周末我将重新发布一个实现功能解析器和解析器组合器模式的答案,这使得编写解析器变得非常容易(只是写出它的语法规则)。

编辑:XSLT 2.0 解决方案。该样式表:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="text">
        <xsl:param name="pString" select="."/>
        <xsl:analyze-string select="$pString" 
                                        regex="(#(.*)#
)|((- (.*)
)+)">
            <xsl:matching-substring>
                <xsl:choose>
                    <xsl:when test="regex-group(1)">
                        <h1>
                            <xsl:call-template name="inline">
                                <xsl:with-param name="pString" 
                                      select="regex-group(2)"/>
                            </xsl:call-template>
                        </h1>
                    </xsl:when>
                    <xsl:when test="regex-group(3)">
                        <ul>
                            <xsl:call-template name="list">
                                <xsl:with-param name="pString" 
                                      select="regex-group(3)"/>
                            </xsl:call-template>
                        </ul>
                    </xsl:when>
                </xsl:choose>
            </xsl:matching-substring>
            <xsl:non-matching-substring>
                <xsl:if test=".!='
'">
                    <p>
                        <xsl:call-template name="inline">
                            <xsl:with-param name="pString" 
                                      select="normalize-space(.)"/>
                        </xsl:call-template>
                    </p>
                </xsl:if>
            </xsl:non-matching-substring>
        </xsl:analyze-string>
    </xsl:template>
    <xsl:template name="list">
        <xsl:param name="pString"/>
        <xsl:analyze-string select="$pString" regex="- (.*)
">
            <xsl:matching-substring>
                <li>
                    <xsl:call-template name="inline">
                        <xsl:with-param name="pString" 
                                  select="regex-group(1)"/>
                    </xsl:call-template>
                </li>
            </xsl:matching-substring>
        </xsl:analyze-string>
    </xsl:template>
    <xsl:template name="inline">
        <xsl:param name="pString" select="."/>
        <xsl:analyze-string select="$pString" 
                 regex="(__(.*)__)|(\*(.*)\*)|("(.*)"\[(.*)\])">
            <xsl:matching-substring>
                <xsl:choose>
                    <xsl:when test="regex-group(1)">
                        <strong>
                            <xsl:value-of select="regex-group(2)"/>
                        </strong>
                    </xsl:when>
                    <xsl:when test="regex-group(3)">
                        <span>
                            <xsl:value-of select="regex-group(4)"/>
                        </span>
                    </xsl:when>
                    <xsl:when test="regex-group(5)">
                        <a href="{regex-group(7)}">
                            <xsl:value-of select="regex-group(6)"/>
                        </a>
                    </xsl:when>
                </xsl:choose>
            </xsl:matching-substring>
            <xsl:non-matching-substring>
                <xsl:value-of select="."/>
            </xsl:non-matching-substring>
        </xsl:analyze-string>
    </xsl:template>
</xsl:stylesheet>

输出:

<h1> This is a title with 
    <strong>bold</strong> text and 
    <span>italic</span>
</h1>
<p>This is just a normal line</p>
<ul>
    <li>list point with some 
        <strong>bold</strong>
    </li>
    <li>list point with a 
        <a href="http://www.stackoverflow.com">link</a>
    </li>
</ul>

I think you would need a parser. So this stylesheet implements a verbose one:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="text" name="block">
        <xsl:param name="pString" select="."/>
        <xsl:if test="$pString != ''">
            <xsl:choose>
                <xsl:when test="starts-with($pString,'#')">
                    <xsl:call-template name="header">
                        <xsl:with-param name="pString"
                        select="substring($pString,2)"/>
                    </xsl:call-template>
                </xsl:when>
                <xsl:when test="starts-with($pString,'
')">
                    <xsl:call-template name="list">
                        <xsl:with-param name="pString"
                        select="substring($pString,2)"/>
                    </xsl:call-template>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:call-template name="paragraph">
                        <xsl:with-param name="pString"
                                              select="$pString"/>
                    </xsl:call-template>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:if>
    </xsl:template>
    <xsl:template name="header">
        <xsl:param name="pString"/>
        <xsl:variable name="vInside"
        select="substring-before($pString,'#
')"/>
        <xsl:choose>
            <xsl:when test="$vInside != ''">
                <h1>
                    <xsl:call-template name="inline">
                        <xsl:with-param name="pString" select="$vInside"/>
                    </xsl:call-template>
                </h1>
                <xsl:call-template name="block">
                    <xsl:with-param name="pString"
                    select="substring-after($pString,'#
')"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:call-template name="paragraph">
                    <xsl:with-param name="pString" 
                                     select="concat('#',$pString)"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template name="list">
        <xsl:param name="pString"/>
        <xsl:variable name="vCheckList" select="starts-with($pString,'- ')"/>
        <xsl:choose>
            <xsl:when test="$vCheckList">
                <ul>
                    <xsl:call-template name="listItem">
                        <xsl:with-param name="pString" select="$pString"/>
                    </xsl:call-template>
                </ul>
                <xsl:call-template name="block">
                    <xsl:with-param name="pString">
                        <xsl:call-template name="afterlist">
                            <xsl:with-param name="pString" select="$pString"/>
                        </xsl:call-template>
                    </xsl:with-param>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:call-template name="block">
                    <xsl:with-param name="pString" select="$pString"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template name="paragraph">
        <xsl:param name="pString"/>
        <xsl:choose>
            <xsl:when test="contains($pString,'
')">
                <p>
                    <xsl:value-of select="substring-before($pString,'
')"/>
                </p>
            </xsl:when>
            <xsl:otherwise>
                <p>
                    <xsl:value-of select="$pString"/>
                </p>
            </xsl:otherwise>
        </xsl:choose>
        <xsl:call-template name="block">
            <xsl:with-param name="pString"
            select="substring-after($pString,'
')"/>
        </xsl:call-template>
    </xsl:template>
    <xsl:template name="afterlist">
        <xsl:param name="pString"/>
        <xsl:choose>
            <xsl:when test="starts-with($pString,'- ')">
                <xsl:call-template name="afterlist">
                    <xsl:with-param name="pString"
                    select="substring-after($pString,'
')"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$pString"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template name="listItem">
        <xsl:param name="pString"/>
        <xsl:if test="starts-with($pString,'- ')">
            <li>
                <xsl:call-template name="inline">
                    <xsl:with-param name="pString"
                    select="substring-before(substring($pString,3),'
')"/>
                </xsl:call-template>
            </li>
            <xsl:call-template name="listItem">
                <xsl:with-param name="pString"
                select="substring-after($pString,'
')"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>
    <xsl:template name="inline">
        <xsl:param name="pString" select="."/>
        <xsl:if test="$pString != ''">
            <xsl:choose>
                <xsl:when test="starts-with($pString,'__')">
                    <xsl:call-template name="strong">
                        <xsl:with-param name="pString"
                        select="substring($pString,3)"/>
                    </xsl:call-template>
                </xsl:when>
                <xsl:when test="starts-with($pString,'*')">
                    <xsl:call-template name="span">
                        <xsl:with-param name="pString"
                        select="substring($pString,2)"/>
                    </xsl:call-template>
                </xsl:when>
                <xsl:when test="starts-with($pString,'"')">
                    <xsl:call-template name="link">
                        <xsl:with-param name="pString"
                        select="substring($pString,2)"/>
                    </xsl:call-template>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="substring($pString,1,1)"/>
                    <xsl:call-template name="inline">
                        <xsl:with-param name="pString"
                        select="substring($pString,2)"/>
                    </xsl:call-template>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:if>
    </xsl:template>
    <xsl:template name="strong">
        <xsl:param name="pString"/>
        <xsl:variable name="vInside" select="substring-before($pString,'__')"/>
        <xsl:choose>
            <xsl:when test="$vInside != ''">
                <strong>
                    <xsl:value-of select="$vInside"/>
                </strong>
                <xsl:call-template name="inline">
                    <xsl:with-param name="pString"
                    select="substring-after($pString,'__')"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="'__'"/>
                <xsl:call-template name="inline">
                    <xsl:with-param name="pString" select="$pString"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template name="span">
        <xsl:param name="pString"/>
        <xsl:variable name="vInside" select="substring-before($pString,'*')"/>
        <xsl:choose>
            <xsl:when test="$vInside != ''">
                <span>
                    <xsl:value-of select="$vInside"/>
                </span>
                <xsl:call-template name="inline">
                    <xsl:with-param name="pString"
                    select="substring-after($pString,'*')"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="'*'"/>
                <xsl:call-template name="inline">
                    <xsl:with-param name="pString" select="$pString"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template name="link">
        <xsl:param name="pString"/>
        <xsl:variable name="vInside" 
               select="substring-before($pString,'"')"/>
        <xsl:choose>
            <xsl:when test="$vInside != ''">
                <xsl:call-template name="href">
                    <xsl:with-param name="pString"
                    select="substring-after($pString,'"')"/>
                    <xsl:with-param name="pInside" select="$vInside"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="'"'"/>
                <xsl:call-template name="inline">
                    <xsl:with-param name="pString" select="$pString"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template name="href">
        <xsl:param name="pString"/>
        <xsl:param name="pInside"/>
        <xsl:variable name="vHref"
        select="substring-before(substring($pString,2),']')"/>
        <xsl:choose>
            <xsl:when test="starts-with($pString,'[') and $vHref != ''">
                <a href="{$vHref}">
                    <xsl:value-of select="$pInside"/>
                </a>
                <xsl:call-template name="inline">
                    <xsl:with-param name="pString"
                    select="substring-after($pString,']')"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="concat('"',$pInside,'"')"/>
                <xsl:call-template name="inline">
                    <xsl:with-param name="pString" select="$pString"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

With this input:

<text>
# This is a title with __bold__ text and *italic* #
This is just a normal line

- list point with some __bold__
- list point with a "link"[http://www.stackoverflow.com]
</text>

Output:

<h1> This is a title with 
    <strong>bold</strong> text and 
    <span>italic</span>
</h1>
<p>This is just a normal line</p>
<ul>
    <li>list point with some 
        <strong>bold</strong>
    </li>
    <li>list point with a 
        <a href="http://www.stackoverflow.com">link</a>
    </li>
</ul>

Note: Look how many templates are similar (they follow a pattern), so these could be parametrized. I didn't do that in this case because there seems to be more questions which need some sort of parser, so by the end of the week I will repost an answer implementing functional parser and parser combinators pattern that make very easy to write parsers (just writing its grammar rules).

Edit: XSLT 2.0 solution. This stylesheet:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="text">
        <xsl:param name="pString" select="."/>
        <xsl:analyze-string select="$pString" 
                                        regex="(#(.*)#
)|((- (.*)
)+)">
            <xsl:matching-substring>
                <xsl:choose>
                    <xsl:when test="regex-group(1)">
                        <h1>
                            <xsl:call-template name="inline">
                                <xsl:with-param name="pString" 
                                      select="regex-group(2)"/>
                            </xsl:call-template>
                        </h1>
                    </xsl:when>
                    <xsl:when test="regex-group(3)">
                        <ul>
                            <xsl:call-template name="list">
                                <xsl:with-param name="pString" 
                                      select="regex-group(3)"/>
                            </xsl:call-template>
                        </ul>
                    </xsl:when>
                </xsl:choose>
            </xsl:matching-substring>
            <xsl:non-matching-substring>
                <xsl:if test=".!='
'">
                    <p>
                        <xsl:call-template name="inline">
                            <xsl:with-param name="pString" 
                                      select="normalize-space(.)"/>
                        </xsl:call-template>
                    </p>
                </xsl:if>
            </xsl:non-matching-substring>
        </xsl:analyze-string>
    </xsl:template>
    <xsl:template name="list">
        <xsl:param name="pString"/>
        <xsl:analyze-string select="$pString" regex="- (.*)
">
            <xsl:matching-substring>
                <li>
                    <xsl:call-template name="inline">
                        <xsl:with-param name="pString" 
                                  select="regex-group(1)"/>
                    </xsl:call-template>
                </li>
            </xsl:matching-substring>
        </xsl:analyze-string>
    </xsl:template>
    <xsl:template name="inline">
        <xsl:param name="pString" select="."/>
        <xsl:analyze-string select="$pString" 
                 regex="(__(.*)__)|(\*(.*)\*)|("(.*)"\[(.*)\])">
            <xsl:matching-substring>
                <xsl:choose>
                    <xsl:when test="regex-group(1)">
                        <strong>
                            <xsl:value-of select="regex-group(2)"/>
                        </strong>
                    </xsl:when>
                    <xsl:when test="regex-group(3)">
                        <span>
                            <xsl:value-of select="regex-group(4)"/>
                        </span>
                    </xsl:when>
                    <xsl:when test="regex-group(5)">
                        <a href="{regex-group(7)}">
                            <xsl:value-of select="regex-group(6)"/>
                        </a>
                    </xsl:when>
                </xsl:choose>
            </xsl:matching-substring>
            <xsl:non-matching-substring>
                <xsl:value-of select="."/>
            </xsl:non-matching-substring>
        </xsl:analyze-string>
    </xsl:template>
</xsl:stylesheet>

Output:

<h1> This is a title with 
    <strong>bold</strong> text and 
    <span>italic</span>
</h1>
<p>This is just a normal line</p>
<ul>
    <li>list point with some 
        <strong>bold</strong>
    </li>
    <li>list point with a 
        <a href="http://www.stackoverflow.com">link</a>
    </li>
</ul>
新人笑 2024-09-22 19:26:05

此转换(111行):

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns:my="my:my"
 exclude-result-prefixes="xml xsl xs my">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
  <xsl:variable name="vLines" select="tokenize(., '\n')"/>

  <xsl:sequence select="my:parse-lines($vLines)"/>
 </xsl:template>

 <xsl:function name="my:parse-lines" as="element()*">
  <xsl:param name="pLines" as="xs:string*"/>

  <xsl:sequence select=
       "my:parse-line($pLines, 1, count($pLines))"/>
 </xsl:function>

 <xsl:function name="my:parse-line" as="element()*">
  <xsl:param name="pLines" as="xs:string*"/>
  <xsl:param name="pLineNum" as="xs:integer"/>
  <xsl:param name="pTotalLines" as="xs:integer"/>

  <xsl:if test="not($pLineNum gt $pTotalLines)">
    <xsl:variable name="vLine" select="$pLines[$pLineNum]"/>
    <xsl:variable name="vLineLength"
         select="string-length($vLine)"/>
      <xsl:choose>
       <xsl:when test=
        "starts-with($vLine, '#')
        and
         ends-with($vLine, '#')
        ">
        <xsl:variable name="vInnerString"
         select="substring($vLine, 2, $vLineLength -2)"/>
        <h1>
         <xsl:sequence select="my:parse-string($vInnerString)"/>
        </h1>
        <xsl:sequence select=
        "my:parse-line($pLines, $pLineNum+1, $pTotalLines)"/>
       </xsl:when>
       <xsl:when test=
        "starts-with($vLine, '- ')
       and
         not(starts-with($pLines[$pLineNum -1], '- '))
        ">
        <ul>
          <li>
            <xsl:sequence select="my:parse-string(substring($vLine, 2))"/>
          </li>
          <xsl:sequence select=
           "my:parse-line($pLines, $pLineNum+1, $pTotalLines)"/>
        </ul>
       </xsl:when>
       <xsl:when test="starts-with($vLine, '- ')">
          <li>
            <xsl:sequence select="my:parse-string(substring($vLine, 2))"/>
          </li>
          <xsl:sequence select=
           "my:parse-line($pLines, $pLineNum+1, $pTotalLines)"/>
       </xsl:when>
       <xsl:otherwise>
        <p>
          <xsl:sequence select="my:parse-string($vLine)"/>
        </p>
        <xsl:sequence select=
           "my:parse-line($pLines, $pLineNum+1, $pTotalLines)"/>
       </xsl:otherwise>
      </xsl:choose>
  </xsl:if>
 </xsl:function>

 <xsl:function name="my:parse-string" as="node()*">
  <xsl:param name="pS" as="xs:string"/>

  <xsl:analyze-string select="$pS" flags="x" regex=
  '(__(.*?)__)
  |
   (\*(.*?)\*)
  |
   ("(.*?)"\[(.*?)\])

  '>
   <xsl:matching-substring>
    <xsl:choose>
     <xsl:when test="regex-group(1)">
        <strong>
          <xsl:sequence select="my:parse-string(regex-group(2))"/>
        </strong>
     </xsl:when>
     <xsl:when test="regex-group(3)">
        <span>
          <xsl:sequence select="my:parse-string(regex-group(4))"/>
        </span>
     </xsl:when>
     <xsl:when test="regex-group(5)">
      <a href="{regex-group(7)}">
       <xsl:sequence select="regex-group(6)"/>
      </a>
     </xsl:when>
    </xsl:choose>
   </xsl:matching-substring>

   <xsl:non-matching-substring>
    <xsl:value-of select="."/>
   </xsl:non-matching-substring>
  </xsl:analyze-string>
 </xsl:function>
</xsl:stylesheet>

应用于此 XML 文档时(提供的文本与嵌套结构复杂并包含在元素中):

<t># This is a title with __bold__ text and *italic* #
This is just a normal line

- list point with some __bold__
- list point with a __*"link"[http://www.stackoverflow.com]*__</t>

生成所需的正确内容输出

<h1> This is a title with <strong>bold</strong> text and <span>italic</span> 
</h1>
<p>This is just a normal line</p>
<p/>
<ul>
   <li> list point with some <strong>bold</strong>
   </li>
   <li> list point with a <strong>
         <span>
            <a href="http://www.stackoverflow.com">link</a>
         </span>
      </strong>
   </li>
</ul>

请注意:XPath 2.0 和 XSLT 2.0 的 RegEx 机制足以解决这个问题。

This transformation (111 lines):

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns:my="my:my"
 exclude-result-prefixes="xml xsl xs my">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
  <xsl:variable name="vLines" select="tokenize(., '\n')"/>

  <xsl:sequence select="my:parse-lines($vLines)"/>
 </xsl:template>

 <xsl:function name="my:parse-lines" as="element()*">
  <xsl:param name="pLines" as="xs:string*"/>

  <xsl:sequence select=
       "my:parse-line($pLines, 1, count($pLines))"/>
 </xsl:function>

 <xsl:function name="my:parse-line" as="element()*">
  <xsl:param name="pLines" as="xs:string*"/>
  <xsl:param name="pLineNum" as="xs:integer"/>
  <xsl:param name="pTotalLines" as="xs:integer"/>

  <xsl:if test="not($pLineNum gt $pTotalLines)">
    <xsl:variable name="vLine" select="$pLines[$pLineNum]"/>
    <xsl:variable name="vLineLength"
         select="string-length($vLine)"/>
      <xsl:choose>
       <xsl:when test=
        "starts-with($vLine, '#')
        and
         ends-with($vLine, '#')
        ">
        <xsl:variable name="vInnerString"
         select="substring($vLine, 2, $vLineLength -2)"/>
        <h1>
         <xsl:sequence select="my:parse-string($vInnerString)"/>
        </h1>
        <xsl:sequence select=
        "my:parse-line($pLines, $pLineNum+1, $pTotalLines)"/>
       </xsl:when>
       <xsl:when test=
        "starts-with($vLine, '- ')
       and
         not(starts-with($pLines[$pLineNum -1], '- '))
        ">
        <ul>
          <li>
            <xsl:sequence select="my:parse-string(substring($vLine, 2))"/>
          </li>
          <xsl:sequence select=
           "my:parse-line($pLines, $pLineNum+1, $pTotalLines)"/>
        </ul>
       </xsl:when>
       <xsl:when test="starts-with($vLine, '- ')">
          <li>
            <xsl:sequence select="my:parse-string(substring($vLine, 2))"/>
          </li>
          <xsl:sequence select=
           "my:parse-line($pLines, $pLineNum+1, $pTotalLines)"/>
       </xsl:when>
       <xsl:otherwise>
        <p>
          <xsl:sequence select="my:parse-string($vLine)"/>
        </p>
        <xsl:sequence select=
           "my:parse-line($pLines, $pLineNum+1, $pTotalLines)"/>
       </xsl:otherwise>
      </xsl:choose>
  </xsl:if>
 </xsl:function>

 <xsl:function name="my:parse-string" as="node()*">
  <xsl:param name="pS" as="xs:string"/>

  <xsl:analyze-string select="$pS" flags="x" regex=
  '(__(.*?)__)
  |
   (\*(.*?)\*)
  |
   ("(.*?)"\[(.*?)\])

  '>
   <xsl:matching-substring>
    <xsl:choose>
     <xsl:when test="regex-group(1)">
        <strong>
          <xsl:sequence select="my:parse-string(regex-group(2))"/>
        </strong>
     </xsl:when>
     <xsl:when test="regex-group(3)">
        <span>
          <xsl:sequence select="my:parse-string(regex-group(4))"/>
        </span>
     </xsl:when>
     <xsl:when test="regex-group(5)">
      <a href="{regex-group(7)}">
       <xsl:sequence select="regex-group(6)"/>
      </a>
     </xsl:when>
    </xsl:choose>
   </xsl:matching-substring>

   <xsl:non-matching-substring>
    <xsl:value-of select="."/>
   </xsl:non-matching-substring>
  </xsl:analyze-string>
 </xsl:function>
</xsl:stylesheet>

when applied on this XML document (the provided text complicated with nested constructs and wrapped in an element):

<t># This is a title with __bold__ text and *italic* #
This is just a normal line

- list point with some __bold__
- list point with a __*"link"[http://www.stackoverflow.com]*__</t>

produces the wanted, correct output:

<h1> This is a title with <strong>bold</strong> text and <span>italic</span> 
</h1>
<p>This is just a normal line</p>
<p/>
<ul>
   <li> list point with some <strong>bold</strong>
   </li>
   <li> list point with a <strong>
         <span>
            <a href="http://www.stackoverflow.com">link</a>
         </span>
      </strong>
   </li>
</ul>

Do note: The RegEx mechanism of XPath 2.0 and XSLT 2.0 is adequate for solving this problem.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文