LINQ to XML – 如何正确使用XDocument

现在我开始说这确实是一个分配。 然而,我几乎完成了它,直到我遇到了Linq to XML语法。

我有两个类:Track和CD现在作为分配的一部分我创建了一个CD,然后添加了一些轨道。 在搜索了很多完全解释如何从xml到对象的教程之后,我似乎无法使其工作(对象为xml)。

我目前有:

//My list of cds List cds = new List(); //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); 

现在,这就是我需要将其放入XML的对象。 to XML部分是:

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

这很好用(得到我需要的结果!)只需1 cd。 当我决定添加另一张CD时,它会显示:

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

这是指向XDocument。 除了这不工作之外它感觉非常愚蠢(来自cds x2中的c)但无论我尝试什么,我似乎无法阻止这种语法恨我:

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

如果您能提供任何帮助,我们将非常满意!

首先,我建议您使用属性和C#样式命名方法。 以下是您的类可以重构的方式:

 public class CD { private readonly List _tracks = new List(); public CD(string artist, string title) { Artist = artist; Title = title; } public string Artist { get; private set; } public string Title { get; private set; } public IEnumerable 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; } } 

这是Value Object类 – 私有setter不允许更改此类之外的属性值。 这里是轨道课程:

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

现在,您可以使用Fluent API创建CD集合:

 List cds = new List { 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)) }; 

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

这里有几个问题 – 缺少根节点,并在每次迭代时枚举所有CD。

您的XDocument构造存在一些问题。

  1. XDocument中必须只有一个根元素。 您的语句正在为每张CD构建根元素。
  2. 您的LINQ中有奇怪的嵌套循环。 首先,您按照艺术家的顺序订购CD,然后在生成艺术家和名称属性时再次对整个CD集合进行迭代。 您想从“当前”CD生成这些属性。
  3. 您在LINQ中使用“c1”和“t1”而不是迭代变量“cl”和“t”。

试试这个(原谅我把你的getter / setter变成属性:

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

这将创建一个名为cd的元素(这很好),但随后尝试为集合中的每张CD添加一个artist属性,这几乎肯定不是您想要的,并且是导致问题的原因。

也就是说,此代码尝试使xml像这样:

  

你可能知道的是非法的xml。

你缺少的想法是:

  (from cl in cds orderby cl.getArtist() 

你正在使用LINQ为你做循环 - 除了这个范围, c1集合中的一个特定CD 。 所以你不需要from c in cds 中使用 from c in cds ,因为你已经得到了你需要的CD对象:

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

你在选择c1.getTracks()已经有了正确的想法; 在创建属性时应用相同的想法。