views:

1853

answers:

3

Hi,

I would like to reflect the XML tree in my object structure, but I am quite a beginner in LINQ to XML

I have an XML with following structure:

<questions>
<question id="q1">
  <number>1</number>
  <text>some text11</text>
  <answers>
     <answer>
      <v>some text11</v>
    </answer>
    <answer>
      <v>some text11</v>
    </answer>
  </answers>
</question>
<question id="q2">
  <number>2</number>
  <text>some text2</text>

<answers>
    <answer>
      <v>some text22</v>
    </answer>
    <answer>
      <v>some text22</v>
    </answer>
  </answers>
</question>
<question id="q3">
  <number>3</number>
  <text>some text3</text>
  <answers>
    <answer>
      <v>some text33</v>
    </answer>
    <answer>
      <v>some text33</v>
    </answer>
    <answer>
      <v>some text33</v>
      <addDescription>some text333</addDescription>
      <textBox/>
    </answer>
  </answers>
</question>
</questions>

...and I have following classes:

public class Question
{
    public string text { get; set; }
    public IList<Anwser> anwsers = new List<Anwser>();
}

public class Anwser
{
    public string content { get; set; }
}

... and I have build a following (wrong) Linq query:

        List<Question> questions = (from xml in xdoc.Element("survey").Elements("questions").Elements("question")
                                    select new Question()
                                               {
                                                   text = xml.Element("text").Value,
                                                   anwsers =
                                                       (from anwsers in
                                                            xdoc.Element("survey").Elements("questions").Elements("question").Elements(
                                                            "answers").Elements(
                                                            "answer")
                                                        select new Anwser()
                                                                   {
                                                                       content = anwsers.Element("v").Value
                                                                   }

                                                       ).ToList()
                                            }).ToList();

Of course this way I get each time all anwsers from all questions added to each list. How to solve this? I can imagine that this is simple but I have no idea :)

Thank you in advance!

+3  A: 

Your code isn't working because you are getting back all the answer elements because you didn't restrict them based on the question they came from. You could add this restriction or instead of a subquery based on the document, you can make the subquery based on the question element itself.

List<Question> questions = (from question in xdoc.Element("survey").Element("questions").Elements("question")
         select new Question
         {
           text = question.Element("text").Value,
           anwsers = (from answer in question.Element("answers").Elements("answer")
                select new Anwser
                {
                  content = answer.Element("v").Value
                }).ToList()
         }).ToList();
Samuel
+1  A: 

The problem appears to be you are starting with xdoc in your inner select if you were to change it to:

from answer in xml.Elements("answers").Elements("answer")

You should be fine. This should work because xml contains the question element.

Duncan
+3  A: 

You were very close. In the select new parts you don't need () after the class names. Also you want to use .Descendents() instead of .Elements(). The only other part was the answers should be using the xml var not going back to the original document, this gives you the answers associated with the question.

List<Question> questions = (from xml in xdoc.Descendants("question")
                                    select new Question
                                    {
                                        text = xml.Element("text").Value,
                                        answers =
                                            (from anwsers in xml.Descendants("answer")
                                             select new Answer
                                             {
                                                 Content = anwsers.Element("v").Value
                                             }

                                            ).ToList()
                                    }).ToList();
Alexander Kahoun
What do you mean by your last statement? Creating a new reference to the same SelectIterator won't cause it to execute the query.
Samuel
Different approach +1
Jose Basilio
Samuel, to assign it properly the line would look something like this: List<Questions> myQuestions = questions.ToList()Only by calling .ToList() or using the query in a foreach will it execute.
Alexander Kahoun
You should drop that statement then. Because it suggests for you to get it to execute you have to do List<Question> temp = (...).ToList(); and then List<Question> questions = temp;.
Samuel
Good catch, thanks Samual. I'm so used to using var and getting the value later I completely forgot the .ToList() was already in the statement.
Alexander Kahoun