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
这是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构造存在一些问题。
- XDocument中必须只有一个根元素。 您的语句正在为每张CD构建根元素。
- 您的LINQ中有奇怪的嵌套循环。 首先,您按照艺术家的顺序订购CD,然后在生成艺术家和名称属性时再次对整个CD集合进行迭代。 您想从“当前”CD生成这些属性。
- 您在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()
已经有了正确的想法; 在创建属性时应用相同的想法。