2010-02-22 34 views
13

F # người mớiParsing tập tin XML với F # LINQ to XML

Tôi có 2 file XML trong 2 thư mục c:\root\a\file.xmlc:\root\b\file.xml

Họ có cấu trúc giống hệt nhau:

<parent> 
    <property name="firstName">Jane</property> 
    <property name="lastName">...</property> 
    <property name="dateOfBirth">...</property>> 
</parent> 

tôi cần phải chọn các tập tin nút thuộc tính có tên firstName có giá trị Jane.

Trong F # (có thể bằng cách sử dụng System.Xml.Linq) Tôi đã thử một số giải pháp nhưng không hoạt động cho đến thời điểm này. Bất cứ ai sẵn sàng giúp đỡ?

Trả lời

36

Sẽ hữu ích nếu bạn có thể hiển thị một số mã mà bạn đã thử - sau đó ai đó có thể tìm hiểu thêm . Dù sao, bạn sẽ cần phải tham khảo một số hội đồng với các System.Xml.Linq và mở không gian tên đầu tiên. Trong F # tương tác, bạn có thể viết nó như thế này (trong # dự án F, chỉ cần sử dụng Add Reference thoại):

#r "System.Core.dll" 
#r "System.Xml.Linq.dll" 
open System.Xml.Linq 

Khi sử dụng XLinq trong F #, bạn cần một chức năng tiện ích đơn giản để chuyển đổi chuỗi để XName đối tượng (đại diện cho tên phần tử/thuộc tính). Có một chuyển đổi tiềm ẩn trong C#, nhưng điều này thật đáng buồn không hoạt động trong F #.

let xn s = XName.Get(s) 

Sau đó, bạn có thể tải tài liệu XML của bạn bằng cách sử dụng lớp XDocument và sử dụng Element phương pháp để có được một yếu tố "cha mẹ" duy nhất. Sau đó, bạn có thể gọi Elements để có được tất cả các lồng nhau "sở hữu" các yếu tố:

let xd = XDocument.Load("file.xml") 
let props = xd.Element(xn "parent").Elements(xn "property") 

Bây giờ bạn có thể tìm kiếm các yếu tố để tìm ra một phần tử với giá trị thuộc tính xác định. Ví dụ sử dụng Seq.tryFind (mà cũng cho phép bạn xử lý các trường hợp khi nguyên tố này không được tìm thấy):

let nameOpt = props |> Seq.tryFind (fun xe -> 
    xe.Attribute(xn "name").Value = "firstName") 

Bây giờ, giá trị nameOpt là loại option<XElement> để bạn có thể mô hình phù hợp vào nó để xem nếu phần tử là được tìm thấy (ví dụ: Some(el)) hoặc nếu nó không được tìm thấy (None).

EDIT: Một cách khác để viết những dòng này là sử dụng biểu thức chuỗi và sau đó chỉ cần lấy phần tử đầu tiên (điều này không xử lý các trường hợp khi yếu tố không tìm thấy):

let nameEl = 
    seq { for el in xd.Element(xn "parent").Elements(xn "property") do 
      if xe.Attribute(xn "name").Value = "firstName" then yield xe } 
    |> Seq.head 
+1

Bây giờ tôi cảm thấy ngu ngốc! Tôi nghĩ rằng điểm của việc sử dụng các biểu thức f # '' query {} '' và linq là, mà bạn có thể trừu tượng từ nguồn dữ liệu.Vì vậy, không thể như tôi thấy nó - là không có cách nào để làm một tiêu chuẩn '' truy vấn {cho foo trong xmlBar do ...} '' loại điều? – BitTickler

+0

Lưu ý rằng bạn cũng cần tham chiếu System.Xml nếu nó chưa được tham chiếu – byteit101

12

Bạn không cần sử dụng LINQ cho việc này. Dưới đây là một cách để thực hiện:

open System.Xml.XPath 

let getName (filename:string) = 
    let xpath = XPathDocument(filename).CreateNavigator() 
    let node = xpath.SelectSingleNode(@"parent/property[@name='firstName']") 
    node.Value 

let files = [@"c:\root\a\file.xml"; @"c:\root\b\file.xml"] 
let fileForJane = files |> List.find (fun file -> getName file = "Jane") 
6

Ngoài ra, bạn có thể kết hợp giải pháp kvb với nhà điều hành (?):

let (?) (fname: string) (nodeName: string) : string = 
    let xpath = XPathDocument(fname).CreateNavigator() 
    let node = xpath.SelectSingleNode(@"parent/property[@name='"^nodeName^"']") 
    node.Value;; 

val (?) : string -> string -> string 

> "i:/a.xml"?firstName;; 
val it : string = "Jane" 
+0

Không hiểu. Tôi không thể thấy toán tử nào trong biểu tượng và toán tử tham chiếu này. Bạn có thể cung cấp tài liệu tham khảo? – dudeNumber4

2

tôi thích phương pháp này tốt hơn

#r "System.Core.dll" 
#r "System.Xml.Linq.dll" 
open System.Xml.Linq 

let xn s = XName.Get(s) 

let xd = XDocument.Load("file.xml") 
let fnp = xd.Descendants(xn "property") 
     .Where(fun (p : XElement) -> p.Attribute(xn "name").Value = "firstName") 
     .Single() 
+0

đẹp nhất @ A-Dubb – scarpacci

+0

Cảm ơn bro. Cảm kích điều đó.. –