LINQ to XML - How to Use XDocument Properly

Now I will start by saying that this is really an assistant. However, I almost finished it until I came across the Linq to XML syntax.

I have 2 classes: the track and the CD are now part of the set, I create a CD, and then add several tracks to it. After searching for a large number of tutorials that perfectly explained how to switch from xml to objects, I just can't get this to work (objects in xml).

I currently have:

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

Now this is what gets me the objects that I need to put in XML. XML part:

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

This works great (gets me the result I need!) In just 1 CD. When I decide to add another CD, it shows:

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

Which points to XDocument. Other than that it doesn't work, it feels pretty stupid (from c to cds x2), but all I try, I can’t stop this syntax from hating me:

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

I would be very happy for any help you can provide!

+7
source share
3 answers

First, I suggest you use C # style properties and naming for methods. Here's how to reorganize your classes:

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

This is the Value class - private setters do not allow property values ​​to be changed outside this class. And here is the class for the track:

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

Now you can use the Fluent API to create a collection of CDs:

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

And here is the XML creation:

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

Here you had several problems: missing root node and listing all the CDs at each iteration.

+7
source

There are several problems with your XDocument construct.

  • XDocument must have exactly one root element. Your instructions build the root element for each CD.
  • You have weird nested loops in LINQ. First, you order CDs by artist, and then you repeat the entire CD collection again when creating artist and name attributes. You want to get these attributes from the "current" CD.
  • You use "c1" and "t1" in your LINQ instead of the iteration variables "cl" and "t".

Try this (forgive me for turning your getters / setters into properties:

 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)))))); 
+3
source
 select new XElement("cd", /*From new to the end of this is the error*/ ( from c in cds select new XAttribute("artist",c.getArtist()) ), 

This will create an element named cd (which is good), but then try adding one artist attribute for each CD in the collection, which is almost certainly not what you want, and is causing the problem.

That is, this code is trying to make xml as follows:

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

which you probably know is illegal xml.

The idea you are missing is this:

  (from cl in cds orderby cl.getArtist() 

you use LINQ to loop for you - with that in mind, c1 is one particular CD from the collection. Therefore, you do not need to do from c in cds inside this, because you already have the cd object that you need:

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

You already have the right idea when choosing more c1.getTracks() ; apply the same idea when creating attributes.

+1
source

All Articles