1

I'm new to programming but, I'm trying to load all entries of one single field in a XML file into a ListBox. This seems to properly load the XML file on executing the form but, I get an out of range exception with this code:

using System.Xml.Serialization;
using System.IO;
using System.Xml;
using System.Xml.Linq;

namespace TP1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            XmlDocument xmldocIntern = new XmlDocument();
            xmldocIntern.Load(@"InternList.xml");

            foreach (XmlNode node in xmldocIntern.DocumentElement)
            {
                string theTitle = node.Attributes[0].Value;

                lstBxListIntern.Items.Add(theTitle);
            }

        }

This is a sample of my XML file:

<?xml version="1.0"?>
<ArrayOfInternship xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Internship>
    <Title>Marketing</Title>
    <Start>2019-03-08T00:00:00</Start>
    <End>2019-04-12T00:00:00</End>
    <Supervisor>John Smith</Supervisor>
    <Comments>Web site makeover.</Comments>
  </Internship>  
</ArrayOfInternship>
  • 3
    Possible duplicate of [What is an IndexOutOfRangeException / ArgumentOutOfRangeException and how do I fix it?](https://stackoverflow.com/questions/20940979/what-is-an-indexoutofrangeexception-argumentoutofrangeexception-and-how-do-i-f) –  Mar 12 '19 at 13:57
  • Can you include the exception and stack trace? – yoozer8 Mar 12 '19 at 13:58
  • 1
    Can you point to 1 attribute in that whole xml document? Do you know what attributes are in terms of XML? Here's a XML tag: `...`, in this case, `optional="true"` is an attribute. You have none of those. – Lasse V. Karlsen Mar 12 '19 at 14:05
  • You are iterating over `xmldocIntern.DocumentElement`. Wrong. This is a single node, not a collection. Either use `xmldocIntern.DocumentElement.ChildNodes` (which may cause other issues, e.g. Whitespace nodes), or iterate over `DocumentElement.SelectNodes("//Internship")` which gives you a node collection of all Internship nodes. The title is not an attribute but a child node. Access its value e.g. with `node.SelectSingleNode("Title").InnerText`. If possible, specify the nodes by their names rather than using an index. You need to look deeper into XML handling. – LocEngineer Mar 12 '19 at 14:26

3 Answers3

1

I wrote some basic example and I used your XML file.. You can examine that..

XML file

<?xml version="1.0"?>
<ArrayOfInternship xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Internship>
    <Title>Marketing</Title>
    <Start>2019-03-08T00:00:00</Start>
    <End>2019-04-12T00:00:00</End>
    <Supervisor>John Smith</Supervisor>
    <Comments>Web site makeover.</Comments>
  </Internship>  
  <Internship>
    <Title>Marketing 2</Title>
    <Start>2019-03-08T00:00:00</Start>
    <End>2019-04-12T00:00:00</End>
    <Supervisor>John Smith</Supervisor>
    <Comments>Web site makeover.</Comments>
  </Internship> 
</ArrayOfInternship>

this is the model which one is you will use

public class InternshipModel
{
    public string Title { get; set; }
    public string Start { get; set; }
    public string End{ get; set; }
    public string Supervisor{ get; set; }
    public string Comments{ get; set; }
}

this part pars xml file and add to listbox

 // Loading from a file, you can also load from a stream
 var xml = XDocument.Load(@"InternList.xml");

 // Query the data and write out a subset of contacts
 var query = (from c in xml.Root.Descendants("Internship")
              select new InternshipModel
              {
                 Title = c.Element("Title").Value,
                 Start = c.Element("Start").Value,
                 End = c.Element("End").Value,
                 Supervisor = c.Element("Supervisor").Value,
                 Comments = c.Element("Comments").Value
              }).ToList();

 foreach (InternshipModel model in query)
 {
     listBox1.Items.Add("Title: " + model.Title +
                        "Start: " + model.Start +
                        "End: " + model.End +
                        "Supervisor: " + model.Supervisor +
                        "Comments: " + model.Comments);
 }
Can Çalışkan
  • 274
  • 3
  • 16
  • Thanks... your suggestion worked for me... on first execute. Second time, after having added a "contact" to the XML file I get: System.Xml.XmlException : 'Generated in French but, goes something like Unexpected XML declaration. XML should be first node of document. I'm thinking it might be my XML file when I appended second record. It starts with : Should it start with only tag? – Francois Bourgeois Mar 14 '19 at 14:11
  • @FrancoisBourgeois I updated my answer and added XML file. When you add new item to XML file, It's gonna be like that. – Can Çalışkan Mar 14 '19 at 14:41
  • That was the problem. I manualy edited out the extra tag and it works... showing me the two records in the ListBox. Now I have to figure out why the .Append method IfFile.Exists thing generates the array all over. – Francois Bourgeois Mar 14 '19 at 14:42
  • I guess your code has some logical problem.. If you tell me clearly, I can help you. – Can Çalışkan Mar 14 '19 at 14:49
1

I think you may be having an issue with trying to get the value of the Title tag because you're trying to grab the first attribute of every XML element but the only element that has an attribute is ArrayOfInternship.

There are a couple of ways that you could extract all of the titles from all Internship elements in the array.

First would be looping and uses the XmlDocument class like what your code uses

XmlDocument doc = new XmlDocument();
doc.Load(@"InternList.xml");

// Loop over child nodes of <ArrayOfInternship>...
foreach (XmlNode node in doc.DocumentElement)
{
    // Loop over the child nodes of the <Internship> nodes
    foreach (XmlNode child in node.ChildNodes)
    {
        if (child.Name.Equals("Title", StringComparison.OrdinalIgnoreCase))
        {
            lstBxListIntern.Items.Add(child.InnerText);
        }
    }
}

The second solution that I came up with and personally like better because it's more succinct. You probably want to utilize the above solution since you are a beginner and it will most likely be easier for you to read. But here's the other solution using LINQ to XML.

XElement xml = XElement.Load(@"InternList.xml");

var titles = xml.Descendants("Title")
    .Select(x => x.Value)
    .ToList();

lstBxListIntern.Items.AddRange(titles);

Let me know if you need any clarification on what I mentioned and I'll be happy to clarify.

Jeff LaFay
  • 12,882
  • 13
  • 71
  • 101
0

One possible solution

XmlDocument xmldocIntern = new XmlDocument();
xmldocIntern.Load(@"InternList.xml");

var oc = new ListBox.ObjectCollection(lstBxListIntern, xmldocIntern.DocumentElement.GetElementsByTagName("Title").Cast<XmlNode>().Select(node => node.InnerText).ToArray());
PhilH
  • 60
  • 2
  • 7
  • Thanks for suggestion. You solution is both simple and elegant. Unfortunately I get a debug error as to impossible to convert from one object type to another. Would there be a 'using System.Something' missing? – Francois Bourgeois Mar 14 '19 at 14:19
  • Could you post the error, I'd say it's probably trying to cast IEnumerable into whatever type the listbox uses to hold it's items. – PhilH Mar 14 '19 at 15:05
  • Updated, based on the assumption you're working with WinForms. – PhilH Mar 14 '19 at 16:06