Link to home
Start Free TrialLog in
Avatar of gjok
gjokFlag for United Kingdom of Great Britain and Northern Ireland

asked on

Read/Write/Parse XML custom settings file

Hi
I need some advice on storing and retreiving XML data for my Windows Appliction.
Ive not used XML before, and am not familiar with the existing .NET assemblies. There are plenty of good examples out there, but they all seem to be for data made up of the common elements, such as Title, Author, Publisher.

The elements in my data could be different from each other, for example:

<Element>
  <Field key="Type" value="A">
  <Field key="StartDate" value="01/01/2007">
  <Field key="ReviewDate" value="21/11/2008">
</Element>
<Element>
  <Field key="Type" value="B">
  <Field key="Text" value="Lorem ipsum dolor sit amet, consectetuer adipiscing elit.">
</Element>
<Element>
  <Field key="Type" value="C">
  <Field key="Image" value="E:\data\graphics\graph22.jpg.">
  <Field key="Height" value="240">
  <Field key="Width" value="400">
</Element>

Each element corresponds to a class. The keys represent properties of the class. Class A,B and C will have different properties.

I need to read in the XML and create an instance of the relevant class, and assign the XML data, like this:

MyClassA Apples = new MyClassA();
Apples.StartDate = [XML Data]; // 01/01/2007
Apples.ReviewDate = [XML Data]; // 21/11/2008

MyClassB Bananas= new MyClassB();
Bananas.Text = [XML Data]; // Lorem ipsum dolor...

Please help!!
Avatar of YZlat
YZlat
Flag of United States of America image

public class MyClassA
      {
            public MyClassA()
            {
                  //
                  // TODO: Add constructor logic here
                  //
            }
            private string _type;
            private DateTime _startdate;
            private DateTime _reviewdate;
      
   
            public MyClassA(string Type, DateTime StartDate, DatTime ReviewDate)
            {
                  _type=Type;
                  _startdate=StartDate;
                  _reviewdate=ReviewDate;
            }

            public int Type
            {
                  get { return _type; }
                  set { _type = value; }
            }
            public string StartDate
            {
                  get { return _startdate; }
                  set { _startdate = value; }
            }
            public int ReviewDate
            {
                  get { return _reviewdate; }
                  set { _reviewdate = value; }
            }
            
            public override string ToString()
            {
                  return _type;
            }
      }
public MyClassA()
            {
                  //
                  // TODO: Add constructor logic here
                  //
            }
            private string _type;
            private DateTime _startdate;
            private DateTime _reviewdate;
            private string _text;
            private string _image;
            private int _height;
            private int _width;
   
            public MyClassA(string Type, DateTime StartDate, DatTime ReviewDate)
            {
                  _type=Type;
                  _startdate=StartDate;
                  _reviewdate=ReviewDate;
            }

            public int Type
            {
                  get { return _type; }
                  set { _type = value; }
            }
            public string StartDate
            {
                  get { return _startdate; }
                  set { _startdate = value; }
            }
            public int ReviewDate
            {
                  get { return _reviewdate; }
                  set { _reviewdate = value; }
            }
            
            public override string ToString()
            {
                  return _type;
            }
      }

      public class MyClassB
      {
            public MyClassB()
            {
                  //
                  // TODO: Add constructor logic here
                  //
            }
            public MyClassB(string Type, string Text)
            {
                  _type=Type;
                  _text=Text;
            }

            public int Type
            {
                  get { return _type; }
                  set { _type = value; }
            }
            public string Text
            {
                  get { return _text; }
                  set { _text = value; }
            }
            
            public override string ToString()
            {
                  return _type;
            }
      }

      public class MyClassC
      {
            public MyClassC()
            {
                  //
                  // TODO: Add constructor logic here
                  //
            }
            public MyClassC(string Type, string Img, int Height, int Width)
            {
                  _type=Type;
                  _image=Img;
                  _height=Height;
                  _width=Width;
            }

            public int Type
            {
                  get { return _type; }
                  set { _type = value; }
            }
            public string Img
            {
                  get { return _image; }
                  set { _image = value; }
            }
            public string Height
            {
                  get { return _height; }
                  set { _height = value; }
            }
            public string Width
            {
                  get { return _width; }
                  set { _width = value; }
            }
            
            public override string ToString()
            {
                  return _type;
            }
      }
Avatar of gjok

ASKER

The question was "Read/Write/Parse XML custom settings file" - The classes exist already!
I need help with reading & writing the XML!
IMHO that is a very bad XML schema; the tags and attributes don't actually mean anything, you have to understand the attribute values to understand what the element means. If you are able, use a schema like:

<task StartDate="01/01/2007"  ReviewDate="21/11/2008"/>
<description>Lorem ipsum dolor...</description>
<image src="E:\data\graphics\graph22.jpg." Height="240" Width"400" />

XML is supposed to be human-readable. Use tags that actually mean something to a person reading it. That is how XML is designed to be used.

You can use .Net XML classes for any schema. Which classes to use depends on what you want to do; do you need random access to the XML data? Will you be reading all the data in one pass and assigning instances as you go? How big will the XML files you use be? Kilobytes? Megabytes? Do you need to change the XML file as you use it?
Avatar of gjok

ASKER

I have answered my own question.

This link has some nice GetValue, SetValue and removeElement examples:
http://www.codeproject.com/csharp/xmlconfig.asp

Thanks YZlat. I appreciate your help, but I already have the classes in use (currently hard-coding the values). I may post a followup question to this. As I said, this is my first xml experience and so this is unknow territory.
Avatar of gjok

ASKER

Thanks photowhiz. I was about to close of this question, but Im glad I didnt.

The file will only have about 10 or so different elements (there are about 10 corresponding classes)
I want to read in the entire data when a project is loaded into the application, and assign the data values to the class properties there and then.
Likewise, there needs to be a 'save project' button that the user can click to run through all instances of the different classes and save the settings to XML in one go.
Random I/O access in not required.

Also thanks for the suggestion on the schema. In your example, is that one element? not sure how that would be nested - bearing in mind, not all elements share the same fields. However there could be 3 or 4 elements of the same  type.

[points still up for grabs]
Those were examples of three different elements based on your first example. You could make them into an XML file by putting them in a root element:

<?xml version="1.0"?>
<settings>
    <task StartDate="01/01/2007"  ReviewDate="21/11/2008"/>
    <description>Lorem ipsum dolor...</description>
    <image src="E:\data\graphics\graph22.jpg." Height="240" Width"400" />
</settings>

The point I am trying to make is that you should define your own tags and attributes to make the XML self-documenting. Tags like "Element" and "Field" don't mean anything.

If there are only going to be 10 or so elements, you might as well load the whole file into memory and pull out the elements and attributes you're interested in. You can do that with the System.Xml.XmlDocument class. You could even use the XML document directly and not bother with creating your own classes. There is a Microsoft support document giving an example of this at http://support.microsoft.com/kb/301233
Avatar of gjok

ASKER

Many thanks photowhiz.
Thanks for the example link. However, this is what I was talking about in my initial question. all the nodes are the same (all of type 'book'). Using this analogy, my xml file would have 1 book, 2 notepads, 1 diary and maybe 7 magazines. Also the child nodes of book are all the same (author,title,genre, price, etc).
so each node is not neccasserily follow the same format as the previous or next one.

Although this is 'not' a web application, it needs to be more along the lines of a web.config file. How would I read in that kind of structure ?
Here is a pretty good sample using the book analagy: 3 books, 1 mag, 4 diaries.

<book>
    <Code ISBN10="0101010101" ISBN13="0101010101-222"/>
    <Pages src="111"/>
    <Cover>Paperback</Cover>
</book>
<book>
    <Code ISBN10="2323232323" ISBN13="2323232323-333"/>
    <Pages src="222"/>
    <Cover>Hardback</Cover>
</book>
<book>
    <Code ISBN10="4545454545" ISBN13="4545454545-444"/>
    <Pages src="333"/>
    <Cover>Hardback</Cover>
</book>
<Magazine>
    <Title>My Magazine<Title/>
    <Date Printed="01/01/2007" Published="21/11/2008"/>
    <description>consectetuer adipiscing elit.</description>
    <Date Printed="01/01/2007" Published="21/11/2008"/>
    <Price>3.95</Price>
</Magazine>
<Diary>
    <year>2007</year>
</Diary>
<Diary>
    <year>2008</year>
</Diary>
<Diary>
    <year>2009</year>
</Diary>
<Diary>
    <year>2010</year>
</Diary>
Avatar of gjok

ASKER

OK, you have a good sample of the kind of data I need to store. Is this a valid format? or should I group similar elements togethe? I want to be able to parse the entire doucument in one go, assigning the values to classes on the fly. Please advise - thanks
ASKER CERTIFIED SOLUTION
Avatar of photowhiz
photowhiz
Flag of Canada image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of gjok

ASKER

Thanks, but dont forget! Im not storing data on books. I used books just as an analagy to the different types of objects I need to create.
All I am actually looking for is an example or a description of the techniques required, for reading the above book data into my classes using C#.
In other words, I need to put the following code (from my original post) into some kind of loop.
MyClassA Apples = new MyClassA();
Apples.StartDate = [XML Data]; // 01/01/2007
Apples.ReviewDate = [XML Data]; // 21/11/2008

My problem is, that I need to understand the best way to read the different element types in. If all the elements were of the same type, then it would be easy, but I have "Books" and "Magazines", etc. This is my problem.

So, do I need a loop for each different type of element (books, magazines, diaries). ..???

I appreciate your help very much, but maybe you could read my initial question again, as I feel this discussion has deviated from what I originally asked for.


Avatar of gjok

ASKER

I've got it!
As I know the types of elements I am storing, I can run a function for each type. Below I have a GetDiaries function.  I wsimply need to have similar funtion for GetBooks() and GetMagazines().
These functions can instantiate the relative objects as it loops, while giving me a count.

private void GetDiaries()
{
      XmlDocument document = new XmlDocument();
      document.Load("App2.xml");

      string s = null;
      int cnt = 0;

      XmlNodeList list = null;
      list = document.GetElementsByTagName("Diary");
      foreach (XmlNode n in list)
      {
          Year = n["year"].InnerText;
          // Create new class here and set properties with 'Year' value.
          cnt++;
      }
      Debug.WriteLine("There are " + cnt + " Diaries");
}

photowhiz, Thanks for your help, This is exactly what I need, but please let me know if there is any reason why this method is bad in any way.
The only thing bad about XmlDocument is that it loads the entire data set into memory before returning. This makes it bad for large (multi-megabyte) files, and streams. If you are loading a small local file, it it fine.

A benefit of XmlDocument is that you can access, change, and store data directly without making classes. In your instance, you could define a subclass DiaryNode of XmlNode and just use it instead of making up a new class.
Avatar of gjok

ASKER

Luckily the XML file is only about 2KB. The subclass sounds useful, I will have to experiment with that.
Thanks again for your advice.