1

I have the following xml:

<bookstore>
    <book IMDB="11-023-2022">
        <title>Hamlet 2</title>
        <comments>
            <user rating="2">good enough</user>
            <user rating="1">didnt read it</user>
            <user rating="5">didnt read it but title is good</user>
        </comments>
    </book>
</bookstore>

I have an AverageUserRating property which i supposed to fill while parsing in the following format, I also have no idea how to cast comments into list. I tried everything, I can't use nuget packages like xpath. Thank you for your help.

return xdoc.Descendants("book").Select(n => new Books()
            {      
                    IMDB = n.Attribute("IMDB").Value,
                    Title = n.Element("title").Value,
                    //Comments = (List<string>)(n.Elements("user")),  ???
                    //AverageUserRating=         ???
             }).ToList();
user9630194
  • 388
  • 2
  • 10
  • 1
    You can't just cast them. `n.Element("comments").Elements("user")` will return an enumerable of elements: you'll need to Select out the bit your interested in, and then you can ToList the result of the Select for the comments or .Average the result when you've got the rating attributes and individually parsed them into integers. I assume this is an exercise and you know enough LINQ to do that, since you've written an outer Select and ToList. If not, what have you been taught? – Rup Oct 24 '19 at 11:53
  • Comments = n.Elements("user").Select(x => (string)x).ToList() AverageUserRating= n.Elements("user").Select(x => (int)x.Attribute("rating")).Average() – jdweng Oct 24 '19 at 12:13

3 Answers3

4
Comments = n.Element("comments").Elements("user").Select(u => u.Value).ToList(),

Explation:

1) Element("comments"), returns the child html element named "comments"

2) Elements("user"), returns all childrens elements named "user"

3) .Select(u => u.Value), select from every user element the value, that is the comment that you need

4) .ToList() converts into a list of strings

 AverageUserRating = n.Element("comments").Elements("user").Select(u => u.Attribute("rating").Value).Select(r => Convert.ToInt32(r)).Average()

Explation:

1) Element("comments"), returns the child html element named "comments"

2) Elements("user"), returns all childrens elements named "user"

3) .Select(u => u.Attribute("rating").Value), selects from any element the value of the attribute "rating"

4) .Select(r => Convert.ToInt32(r)) converts the string value of the attribute into an int32 (pay attention, if the value is not a number, it throws an exception)

5) .Average() It calculates the aritmetic average and returns a double

Stefano Balzarotti
  • 1,760
  • 1
  • 18
  • 35
0

Maybe, you should process original XML with XSLT to get the data you need automatically. Then, resulting doc could be easier to parse. Take a look here as an example Calculate average with xslt

It uses HTML as output format, you can do the same with XML.

Yury Schkatula
  • 5,291
  • 2
  • 18
  • 42
  • I don't think that's necessary. C# can do this without too much hassle, and OP is nearly there. – Rup Oct 24 '19 at 12:26
0

Another option can be to create the classes with the same structure as your original XML so then you could employ automatic deserialization. Then, use LINQ or any other way to get the stats.

Yury Schkatula
  • 5,291
  • 2
  • 18
  • 42