2013-05-28 15 views
7

Teraz zacznę od stwierdzenia, że ​​to rzeczywiście jest przypisanie. Jednak prawie skończyłem go, aż wpadłem na składnię Linq do XML.LINQ to XML - Jak korzystać z XDocument we właściwy sposób

Mam 2 klasy: Track i CD teraz w ramach przypisania Tworzę płytę CD, a następnie dodałem do niej kilka ścieżek. Po wyszukaniu wielu tutoriali, które wyjaśniłyby doskonale, jak przejść z xml do obiektów, po prostu nie mogę uzyskać tego działającego (obiekty do xml).

Obecnie mam:

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

Teraz to, co dostaje mi przedmioty muszę umieścić na XML. Część XML to:

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

Działa to świetnie (daje mi wynik, którego potrzebuję!) Za jedyne 1 cd. Kiedy decyduję się dodać kolejną płytę CD, pokazuje:

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

Wskazuje na XDocument. Poza tym nie działa ona czuje się całkiem głupi (od c CDS x2), ale cokolwiek próbuję Nie mogę się powstrzymać tę składnię od nienawidzących mnie:

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

byłby bardzo zadowolony z jakiejkolwiek pomocy można zapewnić!

+0

Dlaczego po prostu nie użyjesz klasy 'XmlSerializer', a następnie przeanalizuje wynik do' XDocument'? – LukeHennerley

+0

@LukeHennerley Nie słyszałem o tym, nauczyciel wyświetlał tego rodzaju struktury. –

+2

To wydaje się być dobrym sposobem zadawania pytań o pracę domową. Wygląda na to, że przegapiłeś linię, na której pojawia się wyjątek. – Sayse

Odpowiedz

7

pierwsze, proponuję użyć właściwości i C# styl nazewnictwa metod Oto w jaki sposób ćwiczenia mogą być 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; 
    } 
} 

to Value Object klasa - prywatne ustawiaczy nie pozwala na zmianę wartości nieruchomości poza tej klasy I tu jest klasa dla utworu:

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

Teraz można użyć Fluent API stworzyć kolekcję płyt:

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

I tu jest stworzenie 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))); 

Miałeś kilka problemów tutaj - brakujące węzeł główny, a wyliczający ponad wszystkie dyski CD w każdej iteracji.

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()) 
     ), 

Stworzy to element o nazwie cd (co jest dobre), ale potem próbować dodać jeden artist atrybut dla każdej płyty w kolekcji, która jest prawie na pewno nie to, co chcesz, i jest przyczyną problemu.

Oznacza to, że ten kod próbuje dokonać xml jak poniżej:

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

który zapewne wiecie jest nielegalne xml.

Pomysł brakuje jest to, że tutaj:

(from cl in cds orderby cl.getArtist() 

używasz LINQ zrobić pętlę dla Ciebie - withint zakresem niniejszego from, c1 jest jeden szczególny CD z kolekcji. Więc nie trzeba robić from c in cdsw tym, bo masz już przedmiot CD trzeba:

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()) 
        )   
      )      
     ) 
    ) 
) 

Masz już prawo pomysł w wyborze ponad c1.getTracks(); zastosuj ten sam pomysł przy tworzeniu atrybutów.

3

Istnieje kilka problemów z konstrukcją XDocument.

  1. Na XDocumentie musi znajdować się dokładnie jeden element główny. Twoje instrukcje konstruują element główny dla każdej płyty CD.
  2. Masz dziwne zagnieżdżone pętle w swoim LINQ. Najpierw zamawiasz płyty CD według wykonawców, a następnie wykonujesz iteracje po całej kolekcji CD, tworząc atrybuty artysty i nazwy. Chcesz produkować te atrybuty z "aktualnej" płyty CD.
  3. Używasz "c1" i "t1" w swoim LINQ zamiast zmiennych iteracyjnych "cl" i "t".

Spróbuj tego (wybacz mi odwracać się pobierające/ustawiające się właściwościami:

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)))))); 
Powiązane problemy