【问题标题】:Parsing an XML stylsheet processing instruction解析 XML 样式表处理指令
【发布时间】:2023-03-16 19:00:02
【问题描述】:

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

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

是:

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

如何将其解析为键值对列表?我已经搜索了一些有关如何执行此操作的示例,但没有找到任何示例。

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

【问题讨论】:

    标签: c++ xml qt


    【解决方案1】:

    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))
    };
    

    正如 cmets 所指出的(如您所见),这两者都受益于提前知道 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 风格的正则表达式进行解析,这些函数可能会更紧凑一些。有些人可能会觉得这种紧凑的形式更自然、更容易理解,有些人会更喜欢这里给出的不太简洁的表述。

    【讨论】:

    • 非常全面的答案 - 但是我有一个有趣的问题。将 xml-stylesheet PI 的值嵌入到伪造的 XML 标签 &lt;node foo="bar" href="http://www.w3.org/2008/09/xsd.xsl" type='text/xsl' /&gt; 中并将其解析为单独的 XML 文档,以便将标签的属性读取为 xml-stylesheet PI 的属性,是否会产生相同的结果?跨度>
    • 是的,这是一种方法;如果您查看这两个规范,您会发现xml-stylesheet PI 上的伪属性的语法与 start- 或 single- 中的属性语法相同(模实体和字符引用识别)标记。
    • 完美!感谢您提供如此全面的答案。
    【解决方案2】:

    什么编程语言?

    如果您使用 Java,可以在这里找到一些有用的代码:

    http://grepcode.com/file/repo1.maven.org/maven2/net.sourceforge.saxon/saxon/9.1.0.8/net/sf/saxon/om/ProcInstParser.java

    【讨论】:

      猜你喜欢
      • 2022-11-29
      • 2011-06-30
      • 2023-03-03
      • 1970-01-01
      • 1970-01-01
      • 2012-10-10
      • 2023-04-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多