LINQ to XML-Как правильно использовать XDocument
теперь я начну с того, что это действительно назначение. Однако я почти закончил его, пока не столкнулся с синтаксисом Linq to XML.
у меня есть 2 класса: трек и CD теперь как часть назначения я создаю компакт-диск, а затем добавил к нему несколько треков. После поиска множества учебников, которые прекрасно объясняли, как перейти от xml к объектам, я просто не могу заставить это работать (объекты в xml).
в настоящее время у меня есть:
//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);
теперь это что дает мне объекты, которые мне нужно поместить в 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. Когда я решаю добавить еще один компакт-диск, он показывает:
An unhandled exception of type 'System.InvalidOperationException' occurred in System.Xml.Linq.dll
Duplicate Attribute (cd)
который указывает на XDocument. Помимо этого, он не работает, он чувствует себя довольно глупо (из c в cds x2), но что бы я ни пытался, я не могу остановить этот синтаксис от ненависти ко мне:
(
from c in cds
select new XAttribute("artist",c.getArtist()),
select new XAttribute("name", c.getTitle()) //No not happening!
),
буду очень рад любой вашей помощи!
3 ответов
во-первых, я предлагаю вам использовать свойства и имя стиля C# для методов. Вот как ваши классы могут быть подвергнуты рефакторингу:
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;
}
}
это Стоимость Объекта class-private setters не позволяет изменять значения свойств вне этого класса. А вот класс для трека:
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; }
}
теперь вы можете использовать свободно API для создания коллекции компакт-дисков:
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))
};
и вот создание 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)));
вы должны были здесь несколько проблем-отсутствует корневой узел и перечисление всех компакт-дисков на каждой итерации.
есть несколько проблем с вашей конструкцией XDocument.
- в XDocument должен быть ровно один корневой элемент. Ваши операторы создают корневой элемент для каждого компакт-диска.
- у вас есть странные вложенные циклы в LINQ. Сначала вы заказываете компакт-диски по исполнителю, а затем снова повторяете всю коллекцию компакт-дисков при создании атрибутов исполнителя и имени. Вы хотите создать эти атрибуты из " current" КОМПАКТ.
- вы используете "c1" и "t1" в LINQ вместо переменных итерации "cl"и " t".
попробуйте это (простите меня за превращение ваших геттеров / сеттеров в свойства:
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
(что хорошо), но затем попробуйте добавить один artist
атрибут для каждого CD в коллекцию, который почти наверняка не то, что вы хотите, и является причиной проблемы.
то есть этот код пытается сделать xml следующим образом:
<cd
artist="Dream Theater"
artist="TestArtist"
// the later stuff
который вы, вероятно, знаете, является незаконным xml.
идея вам не хватает, что здесь:
(from cl in cds orderby cl.getArtist()
вы используете LINQ, чтобы сделать цикл для вас-withint область этого from
, c1
is один конкретный компакт-диск из коллекции. Так что вам не нужно делать 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()
; применить ту же идею в создании атрибутов.