XML Loop through attribute help

This is a discussion on XML Loop through attribute help within the C# Programming forums, part of the General Programming Boards category; I have a simple form that connects to the WOW Armory, pulls down the xml stream and is parsing through ...

  1. #1
    Registered User
    Join Date
    Mar 2005
    Posts
    37

    XML Loop through attribute help

    I have a simple form that connects to the WOW Armory, pulls down the xml stream and is parsing through some values, I have almost everything working except for each of the items. I can pull the first item, but not the subsequent ones.

    Here's the code:

    Code:
            private void armory_update_Click(object sender, EventArgs e)
            {
                int ilvl = 0;
    
                WebClient webClient = new WebClient();
                webClient.QueryString.Add("r", realmName.Text);
                webClient.QueryString.Add("n", characterName.Text);
                webClient.Headers.Add("user-agent", "MSIE 7.0");
    
                XmlReaderSettings xmlReaderSettings = new XmlReaderSettings();
                xmlReaderSettings.IgnoreComments = true;
                xmlReaderSettings.IgnoreWhitespace = true;
    
                XmlReader xmlReader = XmlReader.Create(webClient.OpenRead("http://www.wowarmory.com/character-sheet.xml"), xmlReaderSettings);
    
                while (xmlReader.Read())
                {
                    if (xmlReader.NodeType == XmlNodeType.Element && xmlReader.Name == "item")
                    {
                        //for (int i = 0; i < 18; i++ )
                        while (xmlReader.MoveToNextAttribute())
                        {
                            ilvl = ilvl + int.Parse(xmlReader.GetAttribute("level"));
    
                            ilvlBox1.Text = ilvl.ToString();
    
                         }                 
                   }
     
                }
            }
    Sample of the XML data:
    Code:
    <items>
      <item displayInfoId="63690" durability="60" level="251"/>
      <item displayInfoId="64272" durability="0" level="264"/>
    </items>
    The idea is I want to take all of the "level" values from all of the items and add them together, currently my ilvlBox1 always shows 251 for the first item. I've tried a while loop, a for loop, a foreach (which is confusing! lol). Also tried to do a MovetoNextAttribute and it doesn't seem to go past the first item.

    Ultimately I'd like it so instead of showing 251, it should show 515 for the total level value.
    Last edited by Striph; 05-04-2010 at 11:09 AM.

  2. #2
    ...and never returned. StainedBlue's Avatar
    Join Date
    Aug 2009
    Posts
    168
    I sure do love linq

    Code:
    
    using System;
    using System.Linq;
    using System.Xml.Linq;
    
    namespace GetSumFromXmlItems
    {
        class Program
        {
            static void Main(string[] args)
            {
                XDocument xDocument = XDocument.Parse(
                    "<items>" +
                        "<item displayInfoId=\"63690\" durability=\"60\" level=\"251\" />" +
                        "<item displayInfoId=\"64272\" durability=\"0\" level=\"264\"/>" +
                    "</items>"
                );
    
                var levels = xDocument.Element("items").Elements("item")
                            .Select(i => int.Parse(i.Attribute("level").Value));
    
                int levelsSum = levels.Sum();
    
                Console.WriteLine(levelsSum);
            }
        }
    }
    The important namespace are
    Code:
    
    using System.Linq;
    using System.Xml.Linq;
    System.Xml.Linq exposes the XDocument, XElement, and XNode classes, which are newer (and far superior) to the older classes for XML parsing/manipulation like XmlReader.

    Code:
    
    var levels = xDocument.Element("items").Elements("item")
    returns a sequence (of type IEnumerable<XElement>) that we can use the goodies in System.Linq on (for this example, Linq to Objects).

    the var keyword stands for an anonymous type, meaning giving it an explicit type is deferred until it's needed (in this case it's never specified, so it stays an IEnumerable<int>) which is inferred from this line:
    Code:
    .Select(i => int.Parse(i.Attribute("level").Value));
    At this point in the code, we've exposed an anonymous type (of type var) that we're defining as an int. The type of int is inferred by the type of return value from the int.Parse() method.


    Code:
    
    int levelsSum = levels.Sum();
    Console.WriteLine(levelsSum);
    When using .Net, never attempt to hack together a solution to a trivial problem without checking if one already exists. This time, we can thank the Sum() method, which sums together any (IEnumerable) sequence of numerical data.
    Last edited by StainedBlue; 05-04-2010 at 03:19 PM.
    goto( comeFrom() );

  3. #3
    Cat
    Cat is offline
    Registered User
    Join Date
    May 2003
    Posts
    1,571
    You can also look at XPath, which is another quick way to parse XML. In fact this entire thing could be done as a single XPath query (using System.Xml.XPath):

    Code:
                string str = @"
    <items>
      <item displayInfoId=""63690"" durability=""60"" level=""251""/>
      <item displayInfoId=""64272"" durability=""0"" level=""264""/>
    </items>";
    
                // XPathDocument needs a Stream to read the information from
                byte[] strData = Encoding.UTF8.GetBytes(str);
                MemoryStream ms = new MemoryStream(strData);
    
                // Create the document and an XPath navigator
                XPathDocument document = new XPathDocument(ms);
                XPathNavigator nav = document.CreateNavigator();
    
                // Perform the XPath query.  Note that all numeric operators, like sum(), return a double.
                double d = (double)nav.Evaluate(@"sum(//item/@level)");
    
                MessageBox.Show(d.ToString());

    The expression "sum(//item/@level)" will look for any item tag with a level attribute, wherever it is in the tree. If you want to restrict it to only those items directly under the root tag 'items', you would use "sum(/items/item/@level)".

    Of course StainedBlue's linq method is a perfectly fine way to go about this, just a matter of which you prefer. Both linq and xpath are useful tools to learn.
    Last edited by Cat; 05-04-2010 at 11:55 PM.
    You ever try a pink golf ball, Wally? Why, the wind shear on a pink ball alone can take the head clean off a 90 pound midget at 300 yards.

  4. #4
    ...and never returned. StainedBlue's Avatar
    Join Date
    Aug 2009
    Posts
    168
    Cat brings up a good point here by introducing xpath to the discussion. Xpath is really good too, and i didnt mean to lump it in with what i called "older and inferior classes", really adding xpath to your tool box is good bcuz its a w3c recommendation and xpath api is available in many other languages like java, javascript, and python to name a few. Xpath is definately worth learning, and is probably the only other xml tool id reach for (besides linq) in a c# program.

    Definately a matter of personal pref., im a linq junkie, so its normally my first choice.
    goto( comeFrom() );

  5. #5
    Registered User
    Join Date
    Mar 2005
    Posts
    37
    Thanks for the ideas, I'll look into Linq and Xpath, haven't had a chance to use either of those methods yet. Guess its time.

  6. #6
    ...and never returned. StainedBlue's Avatar
    Join Date
    Aug 2009
    Posts
    168
    Quote Originally Posted by Striph View Post
    Thanks for the ideas, I'll look into Linq and Xpath, haven't had a chance to use either of those methods yet. Guess its time.
    Or combine Linq and XPath...
    Code:
    
    var levels = xDocument.XPathSelectElements("/items/item")
                      .Select(i => int.Parse(i.Attribute("level").Value));
    unfortunately there is no XPath-Linq method that returns an attribute, though attributes can still be used in filters, and the XPathEvalue() method (I'm guessing a little here, but I believe that's correct)
    goto( comeFrom() );

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. funny-looking while loop
    By Aisthesis in forum C++ Programming
    Replies: 3
    Last Post: 08-31-2009, 12:54 AM
  2. My loop within loop won't work
    By Ayreon in forum C Programming
    Replies: 3
    Last Post: 03-18-2009, 11:44 AM
  3. Personal Program that is making me go wtf?
    By Submeg in forum C Programming
    Replies: 20
    Last Post: 06-27-2006, 01:13 AM
  4. loop issues
    By kristy in forum C Programming
    Replies: 3
    Last Post: 03-05-2005, 09:14 AM
  5. Need help with this xml tree example
    By kzar in forum C Programming
    Replies: 1
    Last Post: 11-22-2004, 11:23 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21