2010-08-23 90 views
9

我正在将我的XSLT样式表转换为文档,并且我希望为每个代码块在注释节点中提供丰富的体验,因此我希望将以下注释和输出如XHTML:使用xslt将简单降价(字符串)转换为html

字符串:

 
# 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] 

通缉输出:

<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> 

我试图与使用XSL递归函数:从规则分析串递归设置,但无法找到一个很好的解决方案。

任何人最近都这样做了,还是有一些框架有功能可以做到这一点?

thanx提前! :)

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

<!-- 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> 
+2

为什么要尝试使用XSLT转换非XML内容?这完全是这份工作的错误工具。 – 2010-08-23 16:58:49

+1

@Greg:这种类型取决于您可以使用的XSLT版本。对于XSLT 1.0来说,确实非常困难。但借助XSLT 2.0及其广泛的正则表达式和其他字符串处理支持以及任意的文本文档加载功能,它变得几乎微不足道。 – Abel 2010-08-23 17:00:45

+0

@Sveisvei:您使用的是XSLT的哪个版本或处理器?你可以使用扩展方法或函数,如果是,使用什么语言? – Abel 2010-08-23 17:03:40

回答

9

该转化(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的正则表达式机制是解决这个问题的适当

+0

+1不错!我知道创建基本模板不应该太困难! – Abel 2010-08-24 08:45:43

+0

再次谢谢Dimitre。 Ill在github上完成了开源文档生成工作,因此它适合其他人使用:) – Sveisvei 2010-08-24 10:07:20

+0

@Dimitre:+1使用'function'而不是'template',肯定会使样式表更紧凑。但是,我认为你的'parse-line'和'parse-string'(以及我自己的''block''和'inline')作为语法产生(所以解析)是OP尝试中缺少的基本概念。 – 2010-08-24 12:38:59

13

我认为你需要一个解析器。所以这样式表实现了一个详细的一个:

<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,'&#xA;')"> 
        <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,'#&#xA;')"/> 
     <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,'#&#xA;')"/> 
       </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,'&#xA;')"> 
       <p> 
        <xsl:value-of select="substring-before($pString,'&#xA;')"/> 
       </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,'&#xA;')"/> 
     </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,'&#xA;')"/> 
       </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),'&#xA;')"/> 
       </xsl:call-template> 
      </li> 
      <xsl:call-template name="listItem"> 
       <xsl:with-param name="pString" 
       select="substring-after($pString,'&#xA;')"/> 
      </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,'&quot;')"> 
        <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,'&quot;')"/> 
     <xsl:choose> 
      <xsl:when test="$vInside != ''"> 
       <xsl:call-template name="href"> 
        <xsl:with-param name="pString" 
        select="substring-after($pString,'&quot;')"/> 
        <xsl:with-param name="pInside" select="$vInside"/> 
       </xsl:call-template> 
      </xsl:when> 
      <xsl:otherwise> 
       <xsl:value-of select="'&quot;'"/> 
       <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('&quot;',$pInside,'&quot;')"/> 
       <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="(#(.*)#&#xA;)|((- (.*)&#xA;)+)"> 
      <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=".!='&#xA;'"> 
        <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="- (.*)&#xA;"> 
      <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="(__(.*)__)|(\*(.*)\*)|(&quot;(.*)&quot;\[(.*)\])"> 
      <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> 
+2

+1对于XSLT 1.0中的一个复杂的示例(以及大量的工作!)(即使提交者只需要2.0,这会使它更容易和更小......) – Abel 2010-08-23 21:29:46

+0

@Abel:只有几件事情可以简化XSLT 2.0。 RegExp不是解决整个问题的方法,因为它需要解析器功能。最大的改进应该是参数化所有类似的模板。但是,这将是实现功能解析器的一半。 – 2010-08-23 21:48:06

+0

@Alejandro:可能。但是正则表达式并不是XSLT 2.0中唯一的改进......但除非我想出解决方案,否则我不应该尝试比较。 – Abel 2010-08-23 21:57:38