2012-09-10 64 views
1

如何正确解析XML样式表处理指令?据我了解,如XML处理指令的值:解析XML样式表处理指令

<?xml-stylesheet type="application/xsl" src="style.xsl" version="1.0"?> 

是:

type="application/xsl" src="style.xsl" version="1.0" 

我如何解析成键 - 值对的列表?我搜索了一些如何做到这一点的例子,但一直没有找到。

这里的关键词是正确 ...我不想只写一个简单的正则表达式,可能会在某些情况下失败,我想确保我解析这完全符合你如何正确解析一个XML样式表指令。

回答

2

XML样式表PI的语法在the spec中给出,所以如果你想正确地做,只需要为该语法编写解析器。由于语言实际上是规则的,因此可以使用正则表达式正确解析它。最大的难题可能是由于XML规范不需要字符引用或预定义的实体引用在处理指令中被识别,因此您可能有责任自行处理这些引用。

至于你应该怎么做,这取决于你在什么环境下工作。作为一个例子,这里是一个XQuery函数,它执行这个工作并返回一个由伪属性创建的元素列表处理指令;如果PI与规范中给出的语法不匹配,则返回名为error的单个元素。

declare function bmt:parse-sspi($s as xs:string) 
    as element()* { 

    if (bmt:check-sspi($s)) then 
    let $s1 := substring-after($s,"<?xml-stylesheet"), 
     $s2 := substring-before($s1,"?>") 
    return bmt:parse-pseudoatts($s2) 
    else <error/> 
}; 

该函数把手拿开解析所述伪属性,其解析断开每个呼叫中​​的一个属性值对一个单独的递归函数的实际工作:

declare function bmt:parse-pseudoatts($s as xs:string) 
    as element()* { 

    (: We know that $s is a syntactically legal sequence 
    of pseudo-attribute value specifications. So we 
    can get by with simpler patterns than we would 
    otherwise need. 
    :) 

    let $s1 := replace($s,"^\s+","") 
    return if ($s1 = "") then() else 
     let $s2 := substring-before($s, '='), 
      $Name := normalize-space($s2), 
      $s3 := substring-after($s, '='), 
      $s4 := replace($s3,"^\s+",""), 
      $Val := if (starts-with($s4,'"')) then 
         substring-before(
          substring($s4,2), 
          '"') 
        else if (starts-with($s4,"'")) then 
         substring-before(
          substring($s4,2), 
          "'") 
        else <ERROR/>, 
      $sRest := if (starts-with($s4,'"')) then 
         substring-after(
          substring($s4,2), 
          '"') 
        else if (starts-with($s4,"'")) then 
         substring-after(
          substring($s4,2), 
          "'") 
        else "" 

    return (element {$Name} { $Val }, 
      bmt:parse-pseudoatts($sRest)) 
}; 

作为评论表明(与正如你所看到的那样),这两方面都从预先知道PI实际上是合法的而受益。所以我们可以通过从字符串中的第一个“=”之前的空白字符中去除空格来解析伪属性名称,依此类推。

正确性的保证由一个单独的check-sspi函数给出,该函数系统地构造一个正则表达式,以便于比较函数与规范中的语法,以检查该函数是否正确。

declare function bmt:check-sspi($s as xs:string) 
    as xs:boolean { 

    let $pio := "<\?", 
     $kw := "xml-stylesheet", 
     $pic := "\?>", 
     $S := "\s+", 
     $optS := "\s*", 
     $Name := "\i\c*", 
     $CharRef := "&amp;#[0-9]+;|&amp;#x[0-9a-fA-F]+;", 
     $PredefinedEntityRef := concat("&amp;amp;", 
            "|&amp;lt;", 
            "|&amp;gt;", 
            "|&amp;quot;", 
            "|&amp;apos;"), 
     $dq := '"', 
     $sq := "'", 
     $dqstring := concat($dq, 
          "(", 
          "[^", $dq, "&lt;&amp;]", 
          "|", 
          "$CharRef", 
          "|", 
          "$PredefinedEntityRef", 
          ")*", 
          $dq), 
     $sqstring := concat($sq, 
          "(", 
          "[^",$sq,"&lt;&amp;]", 
          "|", 
          "$CharRef", 
          "|", 
          "$PredefinedEntityRef", 
          ")*", 
          $sq), 
     $psAttVal := concat("(",$dqstring,"|",$sqstring,")"), 
     $pseudoAtt := concat("(", 
          $Name, 
          $optS, "=", $optS, 
          $psAttVal, 
          ")"), 
     $sspi := concat($pio, 
         $kw, 
         "(", $S, $pseudoAtt, ")*", 
         $optS, 
         $pic), 
     $sspi2 := concat("^", $sspi, "$") 
     return if (matches($s,$sspi2)) then true() else false() 
}; 

对于测试字符串

<?xml-stylesheet foo="bar" 
     href="http://www.w3.org/2008/09/xsd.xsl" 
     type='text/xsl' 
?> 

顶级parse-sspi函数返回

<foo>bar</foo> 
<href>http://www.w3.org/2008/09/xsd.xsl</href> 
<type>text/xsl</type> 

这些功能可能会略有更紧凑,如果我们只是做了一个单Perl-解析风格正则表达式。有些人可能会发现这样一种紧凑的形式更自然,更容易遵循,有些人会喜欢这里给出的不太简洁的形式。

+0

非常全面的答案 - 但是我有一个有趣的问题。将xml样式表PI的值嵌入到假XML标记中'并将其解析为单独的XML文档,以便将标记的属性作为xml-stylesheet PI的属性读取,具有相同的结果? –

+0

是的,这将是一种方法;如果你看看这两个规范,你可以看到'xml-stylesheet'上的伪属性的语法与开始时属性的语法是相同的(模实体和字符引用识别),或者唯一的标签。 –

+0

完美!感谢这个非常全面的答案。 –