Saturday 16 October 2021

C# Saving State

In this blog I show how one can save program state easily in XML format. Existing .NET code allows this but is somewhat hard to use. I have used the method I will show in many projects.

Overview

For this to work, you need to ensure that all data you wish to persist is can be converted to XML. This is usually as simple as placing the XmlSerializable attribute on a class. Not all classes can be serialized without additional effort. Generally best to stick to non-generic types to ensure reliability.

For this example I use a single state class. I also create a # define that allows me to either save or load data accordingly. The code will make sense, hopefully.

The code

I define a simple address class. Note, the class is a simple data class and contains no methods. I do include an overridden ToString method, useful when debugging.

[Serializable]
public class Address
{
  public int Number;
  public string Street;
  public string PostCode;

  public override string ToString()
  {
    return $"{Number}, {Street}. {PostCode}";
  }
}
Top

Next I declare a customer class. Customers are everywhere so the example seems apt.

[Serializable]
public class Customer
{
  public string FirstName;
  public string LastName;
  public Address Address = new Address();

  public override string ToString()
  {
    return $"{LastName}, {FirstName}, {Address}";
  }
}
Top

Next I define the state class. This would contain state required to run your software.


[Serializable]
public class State
{
  public Customer[] Customers;

  public State()
  {
    this.Customers = new Customer[0]; 
  }

  public State XmlSave()
  {
    XmlSerializer s = new XmlSerializer(typeof(State));
    using (var sw = new StreamWriter("state.xml"))
    {
      s.Serialize(sw, this);
      return this;
    }
  }

  public static State XmlLoad()
  {
    XmlSerializer xs = new XmlSerializer(typeof(State));
    using (var sr = new StreamReader("state.xml"))
    {
      return (State)xs.Deserialize(sr);
    }
  }
}
Top

The State class, as you might expect, maintains all state required for my program to run. The XmlSave function is a member function, so that one can call state.XmlSave(). That is, you need a state instance to save.

The XmlLoad function is static. This is simply because when your program runs you will not have a state instance. So, you will typically want to load and create a state instance. Bootstrap/startup code is simplified as one can simply code "var state = State.XmlLoad();"

Top

The main code, for testing follows...

class Program
{
  static State CreateData()
  {
    return new State()
    {
      Customers= new Customer[]
      {
        new Customer() {
          FirstName = "Karl",
          LastName = "Page",
          Address = new Address() {
            Number=80,
            PostCode="KKJ486",
            Street="Arcacia Avenue" }
        },

        new Customer() {
          FirstName = "Cecilia",
          LastName = "Page",
          Address = new Address() {
            Number=80,
            PostCode="KKJ486",
            Street="Arcacia Avenue" }
        },

        new Customer() {
          FirstName = "Fred",
          LastName = "Bloggs",
          Address = new Address() {
            Number=40,
            PostCode="LLJ86K",
            Street="Nowhere Avenue" }
        }
      }
    };
  }

  static void Main(string[] args)
  {
#if SAVE
    State s = CreateData();
    s.XmlSave();
#else
    State s = State.Load();
#endif
  }
}
Top

Testing

To test run the program as is using F5 to debug.

A file, state.xml should appear under the debug folder. You may have to dig, depends upon project settings.

Make sure you add the line #define SAVE before any usings, at the top of the progam file. This should write state to the the state.XML file. Remove #define SAVE to invoke loading.

Top

My XML appears as follows...

<?xml version="1.0" encoding="UTF-8"?>
<State xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Customers>
 <Customer>
  <FirstName>Karl</FirstName>
  <LastName>Page</LastName>
  <Address>
   <Number>80</Number>
   <Street>Arcacia Avenue</Street>
   <PostCode>KKJ486</PostCode>
  </Address>
 </Customer>

 <Customer>
  <FirstName>Cecilia</FirstName>
  <LastName>Page</LastName>
  <Address>
   <Number>80</Number>
   <Street>Arcacia Avenue</Street>
    lt;PostCode>KKJ486</PostCode>
  </Address>
</Customer>

 lt;Customer>
 <FirstName>Fred</FirstName>
 <LastName>Bloggs</LastName>
 <Address>
  lt;Number>40</Number>
  <Street>Nowhere Avenue</Street>
  <PostCode>LLJ86K</PostCode>
  </Address>
 </Customer>

</Customers>
</State>

No comments:

Post a Comment