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.

  1. в XDocument должен быть ровно один корневой элемент. Ваши операторы создают корневой элемент для каждого компакт-диска.
  2. у вас есть странные вложенные циклы в LINQ. Сначала вы заказываете компакт-диски по исполнителю, а затем снова повторяете всю коллекцию компакт-дисков при создании атрибутов исполнителя и имени. Вы хотите создать эти атрибуты из " current" КОМПАКТ.
  3. вы используете "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(); применить ту же идею в создании атрибутов.