2013-05-28 51 views
7

Bây giờ tôi sẽ bắt đầu bằng cách nói điều này thực sự là một sự đảm bảo. Tuy nhiên, tôi đã gần hoàn thành nó cho đến khi tôi chạy vào cú pháp LINQ to XML.LINQ to XML - Cách sử dụng XDocument đúng cách

Tôi có 2 lớp: Theo dõi và CD bây giờ là một phần của phần đảm bảo, tôi tạo đĩa CD và sau đó thêm một số bản nhạc vào đó. Sau khi tìm kiếm rất nhiều hướng dẫn đã giải thích hoàn hảo cách đi từ xml đến các đối tượng, tôi dường như không thể làm việc này (đối tượng với xml).

Tôi hiện có:

//My list of cds 
List<CD> cds = new List<CD>(); 
//Make a new CD and add some tracks to it 
CD c1 = new CD("Awake","Dream Theater"); 
Track t1 = new Track("6:00", "Dream Theater", new TimeSpan(00, 05, 31)); 
Track t2 = new Track("Caught in a Web", "Dream Theater", new TimeSpan(00, 05, 28)); 
Track t3 = new Track("Innocence Faded", "Dream Theater", new TimeSpan(00, 05, 34)); 
c1.addTrack(t1); 
c1.addTrack(t2); 
c1.addTrack(t3); 
cds.Add(c1); 

//Make another cd and add it 
CD c2 = new CD("Second cd","TestArtist"); 
Track t4 = new Track("TrackForSecond","TestArtist",new TimeSpan(00,13,37)); 
c2.addTrack(t4); 
cds.add(c2); 

Bây giờ đây là những gì được cho tôi những đồ vật tôi cần phải đưa vào XML. Phần XML là:

XDocument xmlOutput = new XDocument (
    new XDeclaration("1.0","utf-8","yes"), 
    (from cl in cds orderby cl.getArtist() 
     select new XElement("cd", /*From new to the end of this is the error*/ 
      (
       from c in cds 
        select new XAttribute("artist",c.getArtist()) 
      ), 
      (
       from c in cds 
        select new XAttribute("name", c.getTitle()) 
      ), 
      new XElement("tracks", 
       (
        from t in c1.getTracks() 
         select new XElement("track", 
          new XElement("artist",t1.getArtist()),  
          new XElement("title",t1.getTitle()), 
          new XElement("length",t1.getLength()) 
         )   
       )      
      ) 
     ) 
    )     
); 
Console.WriteLine(xmlOutput); 

Điều này làm việc tuyệt vời (cho tôi kết quả tôi cần!) Chỉ với 1 cd. Khi tôi quyết định thêm một đĩa CD khác, nó hiển thị:

An unhandled exception of type 'System.InvalidOperationException' occurred in System.Xml.Linq.dll 
Duplicate Attribute (cd) 

Chỉ trỏ vào XDocument. Ngoài này không làm việc nó cảm thấy khá ngu ngốc (từ c trong CD x2) nhưng bất cứ điều gì tôi cố gắng tôi dường như không thể ngăn chặn cú pháp này từ ghét tôi:

(
from c in cds 
    select new XAttribute("artist",c.getArtist()),  
    select new XAttribute("name", c.getTitle()) //No not happening! 
), 

Sẽ rất hài lòng với bất kỳ giúp bạn có thể cung cấp!

+0

Tại sao bạn không chỉ sử dụng lớp 'XmlSerializer' và sau đó phân tích kết quả thành' XDocument'? – LukeHennerley

+0

@LukeHennerley Tôi chưa từng nghe về nó, giáo viên đang trưng bày những loại cấu trúc này. –

+2

Điều này có vẻ là một cách hay để hỏi một câu hỏi về bài tập về nhà .. dường như bạn đã bỏ lỡ những gì mà ngoại lệ xảy ra? – Sayse

Trả lời

7

Trước tiên, tôi đề nghị bạn sử dụng tài sản và C# phong cách đặt tên cho các phương pháp Đây là cách các lớp học của bạn có thể được refactored. :

public class CD 
{ 
    private readonly List<Track> _tracks = new List<Track>(); 

    public CD(string artist, string title) 
    { 
     Artist = artist; 
     Title = title; 
    } 

    public string Artist { get; private set; } 
    public string Title { get; private set; } 

    public IEnumerable<Track> Tracks 
    { 
     get { return _tracks; } 
    } 

    public void AddTrack(Track track) 
    { 
     _tracks.Add(track); 
    } 

    public CD WithTrack(string title, TimeSpan length) 
    { 
     AddTrack(new Track(Artist, title, length)); 
     return this; 
    } 
} 

đây là Value Object lớp - setters tin không cho phép thay đổi giá trị tài sản bên ngoài của lớp này Và đây là lớp học cho ca khúc:

.
public class Track 
{ 
    public Track(string artist, string title, TimeSpan length) 
    { 
     Artist = artist; 
     Title = title; 
     Length = length; 
    } 

    public string Artist { get; set; } 
    public string Title { get; private set; } 
    public TimeSpan Length { get; private set; } 
} 

Bây giờ bạn có thể sử dụng Fluent API để tạo ra bộ sưu tập của CD:

List<CD> cds = new List<CD> 
    { 
     new CD("Awake", "Dream Theater") 
      .WithTrack("6:00", new TimeSpan(00, 05, 31)) 
      .WithTrack("Caught in a Web", new TimeSpan(00, 05, 28)) 
      .WithTrack("Innocence Faded", new TimeSpan(00, 05, 34)), 
     new CD("Second cd", "TestArtist") 
      .WithTrack("TrackForSecond", new TimeSpan(00, 13, 37)) 
    }; 

Và đây là sự sáng tạo XML:

var xDoc = new XDocument(
    new XDeclaration("1.0", "utf-8", "yes"), 
    new XElement("cds", 
      from cd in cds 
      orderby cd.Artist 
      select new XElement("cd", 
       new XAttribute("artist", cd.Artist), 
       new XAttribute("name", cd.Title), 
       from t in cd.Tracks 
       select new XElement("track", 
        new XElement("artist", t.Artist), 
        new XElement("title", t.Title), 
        new XElement("length", t.Length))); 

Bạn có một số vấn đề ở đây - thiếu nút gốc, và liệt kê trên tất cả các đĩa CD trên mỗi lần lặp.

1
select new XElement("cd", /*From new to the end of this is the error*/ 
     (
      from c in cds 
       select new XAttribute("artist",c.getArtist()) 
     ), 

này sẽ tạo ra một yếu tố được đặt tên cd (đó là tốt), nhưng sau đó cố gắng thêm một artist thuộc tính cho mỗi CD trong bộ sưu tập, đó là gần như chắc chắn không phải những gì bạn muốn, và là nguyên nhân của vấn đề.

Đó là, mã này cố gắng để làm cho xml như thế này:

<cd 
    artist="Dream Theater" 
    artist="TestArtist" 
// the later stuff 

mà bạn có thể biết là xml bất hợp pháp.

Ý tưởng bạn đang thiếu là ở đây:

(from cl in cds orderby cl.getArtist() 

bạn đang sử dụng LINQ để làm vòng lặp cho bạn - mình trong vòng phạm vi from này, c1một đặc biệt CD từ bộ sưu tập. Vì vậy, bạn không cần phải làm from c in cdstrong này, bởi vì bạn đã có đối tượng CD bạn cần:

select new XElement("cd", /*From new to the end of this is the error*/ 
     select new XAttribute("artist",c1.getArtist()), 
     select new XAttribute("name", c1.getTitle()), 
     new XElement("tracks", 
      (
       from t in c1.getTracks() 
        select new XElement("track", 
         new XElement("artist",t1.getArtist()),  
         new XElement("title",t1.getTitle()), 
         new XElement("length",t1.getLength()) 
        )   
      )      
     ) 
    ) 
) 

Bạn đã có ý tưởng đúng trong việc lựa chọn trên c1.getTracks(); áp dụng cùng một ý tưởng trong việc tạo các thuộc tính.

3

Có một số vấn đề với cấu trúc XDocument của bạn.

  1. Phải có chính xác một phần tử gốc trong XDocument. Các câu lệnh của bạn đang xây dựng một phần tử gốc cho mỗi đĩa CD.
  2. Bạn có vòng lặp lồng nhau lạ trong LINQ của mình. Trước tiên, bạn đang đặt hàng các đĩa CD của nghệ sĩ, và sau đó bạn đang lặp lại một lần nữa trên toàn bộ bộ sưu tập đĩa CD khi sản xuất các thuộc tính nghệ sĩ và tên. Bạn muốn tạo các thuộc tính này từ CD "hiện tại".
  3. Bạn đang sử dụng "c1" và "t1" trong LINQ thay vì biến lặp "cl" và "t".

Hãy thử điều này (tha thứ cho tôi để chuyển thu khí của bạn/setters vào thuộc tính:

var xmlOutput = new XDocument(
    new XDeclaration("1.0", "utf-8", "yes"), 
    new XElement(
     "cds", 
     from cd in cds 
     orderby cd.Artist.ToUpperInvariant() 
     select new XElement(
      "cd", 
      new XAttribute("title", cd.Title), 
      new XAttribute("artist", cd.Artist), 
      new XElement(
       "tracks", 
       from t in cd.Tracks 
       select new XElement(
        "track", 
        new XAttribute("artist", t.Artist), 
        new XAttribute("title", t.Title), 
        new XAttribute("length", t.Length)))))); 
Các vấn đề liên quan