foreach looping through XmlAttribute

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • PrezWeezy
    New Member
    • May 2007
    • 11

    foreach looping through XmlAttribute

    I am attempting to create a loop to read through each XML attribute in a node and put it into a datatable. I have created my datatable with columns that are the same name as the attributes in my XML. I keep getting an error which says "Unable to cast object of type 'System.Xml.Xml Element' to type 'System.Xml.Xml Attribute'." The code follows:

    Code:
                foreach (XmlNode listItem in newBatch)
                {
                    if (listItem.NodeType == XmlNodeType.Element)
                    {
                        foreach (XmlNode subNode in listItem)
                        {
                            string XMLtoREAD = subNode.OuterXml;
                            XmlDocument AttribXML = new XmlDocument();
                            AttribXML.LoadXml(XMLtoREAD);
                            
    
                            //Find datatable
                            DataTable Items = ROV;
                            DataRow dr;
                            dr = Items.NewRow();
                            
                            //Open Xml to read attributes
                            XmlTextReader reader = new XmlTextReader(new StringReader(XMLtoREAD));
                            reader.WhitespaceHandling = System.Xml.WhitespaceHandling.None;
                            reader.Read();
                            foreach (XmlAttribute attrib in AttribXML)
                            {
    
                                string AttName = attrib.ToString();
                                reader.MoveToAttribute(AttName);
                                dr[AttName] = reader.Value;
                                MessageBox.Show(reader.Value);
    
                            }
                            
                            Items.Rows.Add(dr);
                            Items.AcceptChanges();
                            reader.Close();
                             
                        }
                    }
                }
    The XML I am trying to read is:


    Code:
    <rs:data ItemCount="1" xmlns:rs="urn:schemas-microsoft-com:rowset">
       <z:row ows_Date="2010-05-12 00:00:00" ows_Title="9" ows_Serial_x0020_Number="258" ows_Invoice_x0020_Number="254" ows_Supplier="synnex" ows_SKU="25365" ows_Manufacturer="8" ows_Description="15" ows_Qty_x002e_="1" ows_Price="235" ows_Freight="100" ows_Customer="as" ows_Total_x0020_Price="45" ows_ID="58" ows__ModerationStatus="0" ows__Level="1" ows_owshiddenversion="1" ows_UniqueId="58;#{88D9AAB5-61A9-46F7-80A4-DDBCA8C39475}" ows_FSObjType="58;#0" ows_Created="2010-05-12 11:49:01" ows_FileRef="58;#Calls/Lists/Parts/58_.000" ows_MetaInfo="58;#" xmlns:z="#RowsetSchema" />
    </rs:data>
    If you can't tell already, this is from a SharePoint query. I am not a developer, but I'm having fun playing around with .NET and creating forms to make my SharePoint more accessible. I appreciate any help people can give, and I look forward to replies.

    Moderator: Please move this to the C# Forum, I did not realize there was a better place to post. done. --insertAlias
  • Curtis Rutland
    Recognized Expert Specialist
    • Apr 2008
    • 3264

    #2
    I honestly think you are doing this the hard way.

    Can I ask what version of .NET you are using? If it is 3.0 or higher, you can use the System.Xml.Linq objects, which are much easier to work with.

    Code:
    XmlNode response = proxy.GetListItems("listName", null, query, viewFields, null, queryOptions, null); //this is where you get the xml back from the list web service, use your own values
    XElement element = XElement.Parse(response.OuterXml);
    XNamespace ns = "#RowsetSchema";
    List<XElement> elementList = element.Descendants().Where(x => x.Name == ns + "row").ToList();
    DataTable table = new DataTable();//use your own table here instead of this blank table I created.
    foreach (XElement e in elementList)
    {
        DataRow row = table.NewRow();
        foreach (XAttribute a in e.Attributes())
            row[a.Name.LocalName] = a.Value;
        table.Rows.Add(row);
    }
    Console.ReadKey();

    Comment

    • GaryTexmo
      Recognized Expert Top Contributor
      • Jul 2009
      • 1501

      #3
      Everytime insertAlias posts on an XML topic it makes me realize that darnit, I really need to get on that LINQ thing! So much eaiser :D

      That said, the problem with your code here is that you're loading the xml code again into a new xml document on line 8 and 9. Then you process it as an XmlAttribute when it's actually an XmlDocument. I think your loop online 21 might need to be changed to...

      Code:
      foreach (XmlAttribute attrib in AttribXML[B].DocumentElement.Attributes[/B])
      (Note: This next section has nothing to do with anything, really... just digressing :D)

      Now, if you're noob (or old school, whatever your perspective!!) like me and like using XML this way, recursion helps make things soooo much nicer :)

      I wrote up a little example to help you out... it's for output to a console but you get the idea, I'm sure.

      Code:
          class Program
          {
              static void Main(string[] args)
              {
                  string xmlRaw = "<rs:data ItemCount=\"1\" xmlns:rs=\"urn:schemas-microsoft-com:rowset\">" +
                                  "  <z:row ows_Date=\"2010-05-12 00:00:00\" ows_Title=\"9\" ows_Serial_x0020_Number=\"258\" ows_Invoice_x0020_Number=\"254\" ows_Supplier=\"synnex\" ows_SKU=\"25365\" ows_Manufacturer=\"8\" ows_Description=\"15\" ows_Qty_x002e_=\"1\" ows_Price=\"235\" ows_Freight=\"100\" ows_Customer=\"as\" ows_Total_x0020_Price=\"45\" ows_ID=\"58\" ows__ModerationStatus=\"0\" ows__Level=\"1\" ows_owshiddenversion=\"1\" ows_UniqueId=\"58;#{88D9AAB5-61A9-46F7-80A4-DDBCA8C39475}\" ows_FSObjType=\"58;#0\" ows_Created=\"2010-05-12 11:49:01\" ows_FileRef=\"58;#Calls/Lists/Parts/58_.000\" ows_MetaInfo=\"58;#\" xmlns:z=\"#RowsetSchema\" />" +
                                  "  <z:row attrib1=\"blah\" attrib2=\"stuff\" xmlns:z=\"#RowsetSchema\" />" + 
                                  "  <extra>" +
                                  "    <blah attrib1=\"eh?\" attrib2=\"yep\" />" +
                                  "  </extra>" +
                                  "</rs:data>";
      
                  XmlDocument xmlDoc = new XmlDocument();
                  xmlDoc.LoadXml(xmlRaw);
      
                  ProcessNode(0, xmlDoc.DocumentElement);
              }
      
              public static void ProcessNode(int indent, XmlNode node)
              {
                  if (node != null)
                  {
                      if (node.NodeType == XmlNodeType.Element)
                      {
                          // Output name
                          Output(indent, "Node: " + node.Name);
                          string sep = "".PadLeft(6 + node.Name.Length, '=');
                          Output(indent, sep);
      
                          // Output attributes
                          if (node.Attributes != null && node.Attributes.Count > 0)
                          {
                              Output(indent, "Attributes:");
                              Output(indent, "-----------");
                              foreach (XmlAttribute attrib in node.Attributes)
                              {
                                  Output(indent, "Name: " + attrib.Name);
                                  Output(indent, "Value: " + attrib.Value);
                                  Output(0, "");
                              }
                          }
      
                          // Output child nodes
                          if (node.ChildNodes != null && node.ChildNodes.Count > 0)
                          {
                              Output(indent, "Children:");
                              Output(indent, "---------");
                              foreach (XmlNode childNode in node.ChildNodes)
                              {
                                  ProcessNode(indent + 2, childNode);
                              }
                          }
                      }
                  }
              }
      
              public static void Output(int indent, string text)
              {
                  text = text.PadLeft(indent + text.Length, ' ');
                  Console.WriteLine(text);
              }
          }

      Comment

      • Curtis Rutland
        Recognized Expert Specialist
        • Apr 2008
        • 3264

        #4
        Haha, you simply must get onboard the LINQ train. Seriously, it will make your coding life so much easier. And not only for XML, but for SQL as well, and just objects in memory.

        Comment

        • GaryTexmo
          Recognized Expert Top Contributor
          • Jul 2009
          • 1501

          #5
          :D

          Next project that I get with this stuff I'll probably dive in. The real problem though is that we're stuck using VS 2005 and .NET 2.0 at work (*** sigh ***) so I typically have to go back to the System.Xml stuff anyway.

          I played around with your example a bit though. For the most part it's not terribly different other than that you can easily filter your query instead of bulk checking the whole node.

          Comment

          • PrezWeezy
            New Member
            • May 2007
            • 11

            #6
            @insertAlias:
            I am using 3.0, although I also use a Web Reference (which I understand to be 2.0?) are there any issues with mixing the two?
            I guess the reason I haven't been using the LINQ is because the standard XML syntaxes made a little more sense. But now, seeing your example, I'll start trying to play with LINQ. I am also creating a phone app for WinPhone 7, which doesn't even have 2.0 available so I'm going to have to learn it some time anyway.

            @GaryTexmo
            Thanks for the reply. That actually is what I ended up doing. I changed line 21 to read:
            Code:
            foreach (XmlAttribute attrib in AttribXML.DocumentElement.Attributes)
            Which solved the problem.

            Thanks guys!

            Comment

            • GaryTexmo
              Recognized Expert Top Contributor
              • Jul 2009
              • 1501

              #7
              It's also worth noting that you don't even need to declare that AttributeXML variable that way. You've got access to the attributes already via subNode.Attribu tes.

              Actually it might be listItem.Attrib utes... you'll have to check your code. I don't think I had the full XML and context for what you're working on, so when I used your code I had the row nodes in my first foreach loop.

              Comment

              • Curtis Rutland
                Recognized Expert Specialist
                • Apr 2008
                • 3264

                #8
                Originally posted by PrezWeezy
                @insertAlias:
                I am using 3.0, although I also use a Web Reference (which I understand to be 2.0?) are there any issues with mixing the two?
                I guess the reason I haven't been using the LINQ is because the standard XML syntaxes made a little more sense. But now, seeing your example, I'll start trying to play with LINQ. I am also creating a phone app for WinPhone 7, which doesn't even have 2.0 available so I'm going to have to learn it some time anyway.

                @GaryTexmo
                Thanks for the reply. That actually is what I ended up doing. I changed line 21 to read:
                Code:
                foreach (XmlAttribute attrib in AttribXML.DocumentElement.Attributes)
                Which solved the problem.

                Thanks guys!
                Actually, I used a Web Reference (2.0) in that example there. We had some issues using service references for some reason.

                Anyway, no, there's no problem mixing the two. Actually, you can get on the .NET 4.0 train now! Here's a link to the Visual Studio Express versions.

                Comment

                • PrezWeezy
                  New Member
                  • May 2007
                  • 11

                  #9
                  @GaryTexmo:
                  I actually have seperated out my listItem function because I am creating a query form, so depending on the type of query you make, it passes through different arguments. So I created a function for diong the parsing, which is what the code I posted is.

                  What do you mean about declaring AttributeXML? I ask because when I used the subnode.Attribu tes it gave me an error about implicitly converting something. I don't remember now.

                  Comment

                  • PrezWeezy
                    New Member
                    • May 2007
                    • 11

                    #10
                    @insertAlias:
                    Is there a way to use a service reference instead of a web reference? Since the Phone platform doesn't support web references I'm going to have to figure out a way to make it work somehow and I was wondering if there is a blanket way to make it work, specifically with sharepoint.

                    Comment

                    • Curtis Rutland
                      Recognized Expert Specialist
                      • Apr 2008
                      • 3264

                      #11
                      Umm...it's been a while since I've tried. I'm not sure off the top of my head. Let me look into it and get back to you.

                      Comment

                      • PrezWeezy
                        New Member
                        • May 2007
                        • 11

                        #12
                        I have one more quick question in regards to crossing classes, should I create a new thread or can you help me here?

                        Comment

                        • Curtis Rutland
                          Recognized Expert Specialist
                          • Apr 2008
                          • 3264

                          #13
                          <ProfessorFarns worth>Good news, everyone!</ProfessorFarnsw orth>

                          Figured it out, with the help of this site.

                          Here's what you do. Create your Service Reference using the same address as you did for the Web Reference.

                          Go into your app.config (or Web.config) and change this:
                          Code:
                          <security mode="None">
                              <transport clientCredentialType="None" proxyCredentialType="None"
                                  realm="" />
                              <message clientCredentialType="UserName" algorithmSuite="Default" />
                          </security>
                          to this:
                          Code:
                          <security mode="[B]TransportCredentialOnly[/B]">
                              <transport clientCredentialType="[B]Ntlm[/B]" proxyCredentialType="None"
                                  realm="" />
                              <message clientCredentialType="UserName" algorithmSuite="Default" />
                          </security>
                          Then in your code file:
                          Code:
                          //this is whatever your soap client is named
                          Lists.ListsSoapClient proxy = new Lists.ListsSoapClient();
                          [B]proxy.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;[/B]
                          That should do it.

                          Here's the code I used to get a list of all the XML Elements in a Sharepoint List:
                          Code:
                          Lists.ListsSoapClient proxy = new Lists.ListsSoapClient();
                          proxy.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
                          XElement query = new XElement("Query");
                          XElement queryOptions = new XElement("QueryOptions");
                          XElement viewFields = new XElement("ViewFields");
                          XElement response = proxy.GetListItems("Cafe", null, query, viewFields, null, queryOptions, null);
                          XNamespace ns = "#RowsetSchema";
                          List<XElement> elementList = response.Descendants().Where(x => x.Name == ns + "row").ToList();
                          Console.ReadKey();

                          Comment

                          • Curtis Rutland
                            Recognized Expert Specialist
                            • Apr 2008
                            • 3264

                            #14
                            Just post your new question in this thread. If I feel like it needs its own thread, I'll split it for you.

                            Comment

                            • PrezWeezy
                              New Member
                              • May 2007
                              • 11

                              #15
                              Ok, so the form I'm creating has the first form which queries the SharePoint list, then sends the info to a DataGridView. I want to then be able to double click on one of the items in the DataGridView, and have it open that item in another form. I'm having trouble finding out how to make my datatable accessible to other classes. I've tried just making it public static, public void, everything I know how. I would like to actually have a "Tables.cs" which holds all of the definitions for my tables and then all of the forms simply access the tables from those different classes. When I do that and then try to bind my datagridview to that table from another class it comes up blank. Is there a trick?

                              Comment

                              Working...