2012-03-14 83 views
3

我正在尝试使用F#处理特别大的XML文档。由于加载整个文档被排除,我正在尝试使用XmlReader来服务我的目的。我的第一步是将XML文档定义为节点序列。使用XMLReader进行F#XML解析

// Read XML as a lazy sequence 
let Read (s:string) = 
    let r = XmlReader.Create s 
    let src = seq { 
       while r.Read() 
        do 
         if XmlNodeType.Element = r.NodeType then 
          yield CreateNodeData r 
          while r.MoveToNextAttribute() 
           do 
            yield CreateNodeData r 
           done 
         else 
          yield CreateNodeData r 
        done 
       } 
    LazyList.ofSeq src 

此生成XML文档作为NODEDATA的序列(其由功能CreateNodeData创建的,并且在这里没有给出为简单起见)。懒惰列表用于使用活动模式匹配。

现在模式的解析器是通过定义语法FParsec来构造的。例如

type NodeSeq = NS of LazyList<NodeData> 

(* 
Define a generic parser that takes an XML Reader and returns a singleton 
list containing parsed element and unparsed parser. Failure is denoted by 
an empty list 
*) 

type 'a Parser = P of (NodeSeq -> list<'a * NodeSeq >) 

并添加一元构建体以创建一个monadic解析器,使得下面的代码解析NODEDATA该匹配给定条件。

let item = P (fun inp -> 
    match inp with 
    | NS(LazyList.Nil)   -> [] 
    | NS(LazyList.Cons(a,b)) -> [(a,NS(b))] 
    ) 

let nodeFilter (f: NodeData -> bool) = 
    parser { 
     let! c = item 
     if (f c) then 
      return c 
     } 

另外,操作者选择(+++)加入使得p +++ q表示替代的解析器。

,我面对的是与解析XML元素,如

<Node Color="Red" Transparency="90%" Material="Wood"/> 

这里颜色,透明度和材料所需的属性的属性,但是,他们的顺序是无关紧要的问题。另外,还可以有其他可选属性。如何创建一个组合分析器来表示

  • 序列独立的特性处理
  • 可选属性

这相当于匹配以下字符串

xabc,xacb,xbac,xbca,xcab,xcba

如何的任何一个我可以简化它吗?

回答

2

我的印象是,你正在重新发明轮子。

XmlReader是一个完整而高效的XML解析器。使用XmlReader解析属性很简单,不依赖于它们的顺序。您可以使用XmlReader在构建序列时获取必需和可选属性。查看r.HasAttributer. MoveToNextAttribute()阅读属性hereMSDN

也就是说,为任务编写解析器组合器是过度的。我怀疑使用LazyList会给你带来什么好处。您很可能会使用高阶函数来处理序列;以seq开头是一个不错的选择。

+0

解析使用由XmlReader中解析节点的数据。主要思想是将XML Schema覆盖在XmlReader之上,并为应用程序提供一个框架,以便在不调用HasAttribute和其他函数的情况下直接创建数据。 – 2012-03-14 15:34:31

4

如果你喜欢的XElement从LINQ到XML,但不希望加载整个文档到内存中,你可以从一个XmlReader流个体的XElement实例:

type XmlReader with 
    /// Returns a lazy sequence of XElements matching a given name. 
    member reader.StreamElements(name, ?namespaceURI) = 
     let readOp = 
      match namespaceURI with 
      | None -> fun() -> reader.ReadToFollowing(name) 
      | Some ns -> fun() -> reader.ReadToFollowing(name, ns) 
     seq { 
      while readOp() do 
       match XElement.ReadFrom reader with 
       | :? XElement as el -> yield el 
       | _ ->() 
     } 

然后,您可以查询每个属性元素和属性的源顺序无关紧要,但是您仍然在流式传输文档,而不是将整个内容加载到内存中。

+0

谢谢!我喜欢这个解决方案 – 2012-04-09 16:11:20