0

I am attempting to set up a reliable method of updating my software in the future by assuring I can smoothly update my user's files whenever I release a program update. The software is written in C#.

Say I have a person object:

 - Name
 - DoB
 - Nationality

So that's version 0.1 of my person class. In the future, for the sake of example, I introduce some new fields to the person object (v0.2).

 - Name
 - DoB
 - Nationality
 - Temperament <- NEW
 - Job <- NEW

Obviously I need to update the old object to the new one. What I am currently doing is the following:

  1. Read the 0.1 object into a versioned class (like Person01.cs).
  2. Populate the new object with the fields that the old object had.
  3. Populate the new fields of the new object with default values.
  4. Write out the file.

This seems tremendously inefficient. It requires a great deal of custom coding every time I make any change. There must be a better way to do this, but as this is my first time really working with XML I'm not sure how to go about it. Any help you can provide would be greatly appreciated. I have searched for a solution to this problem but I am only finding unrelated results.

bit
  • 4,407
  • 1
  • 28
  • 50
Aaron Goselin
  • 634
  • 7
  • 19

3 Answers3

1

You could cut short the steps 1 & 2 by deserializing the old xml into the new object e.g. Assuming you had a person v1 like:

[XmlRoot("Person")]
public class PersonV1
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

Then you upgraded it to person v2 like:

[XmlRoot("Person")]
public class PersonV2
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string MiddleName { get; set; }
    public DateTime BirthDate { get; set; }
}

Now you could populate the person v2 with data you read for person v1 like:

// read from person v1
var p1 = new PersonV1();
p1.FirstName = "Jeff";
p1.LastName = "Price";
var xmlSerializer = new XmlSerializer(typeof(PersonV1));
var stream = new MemoryStream();
xmlSerializer.Serialize(stream, p1);
var xmlStringP1 = Encoding.ASCII.GetString(stream.ToArray());

// populate to person v2
var deserializer = new XmlSerializer(typeof(PersonV2));
TextReader reader = new StringReader(xmlStringP1);
var p2 = (PersonV2)deserializer.Deserialize(reader);

// further, set defaults for person v2

You could explore more customizations for xml serializations here: http://www.codeproject.com/Articles/483055/XML-Serialization-and-Deserialization-Part-1

bit
  • 4,407
  • 1
  • 28
  • 50
  • I'm trying this idea but it doesn't seem to be working out for me. I am getting an error that the deserializer doesn't seem to like the fact that the classes have a different name "InvalidOperationException: was not expected" – Aaron Goselin Apr 26 '16 at 06:46
  • Yes, that is why there is an *XmlRoot* attribute in the place. Did you use it to nullify the effect of different class names? Also check out the other options in the link at the bottom – bit Apr 26 '16 at 06:54
  • You're right, that was my error. I went with what Alex said but your answer was good info thanks. – Aaron Goselin Apr 26 '16 at 07:02
1

We had a Person object:

public class Person
{
    public string Name { get; set; }
    public DateTime DoB { get; set; }
    public string Nationality { get; set; }
}

Then we add new properties. No need to have different versions of the class! Just add properties and default constructor, in which set new properties values. Deserialization will set these values, instead of missing.

public class Person
{
    public Person()
    {
        // set default value
        Temperament = "hot";
        Job = "office slave";
    }

    public string Name { get; set; }
    public DateTime DoB { get; set; }
    public string Nationality { get; set; }

    public string Temperament { get; set; }
    public string Job { get; set; }
}
Alexander Petrov
  • 13,457
  • 2
  • 20
  • 49
  • Fantastic thank you! That's so simple. Exactly what I was hoping to find. Now all I need to do when I update is add default values for each new variable. – Aaron Goselin Apr 26 '16 at 07:01
1

A good simple solution is to use XSL Transformation. You can simply apply a transformation to an XML file or string-with-XML-data, and add the new properties. An example how to do this in C# can be found here.

An example XML file:

<?xml version="1.0" encoding="utf-8"?>
<person>
    <name>John</name>
    <DoB>2016-01-01</DoB>
</person>

An example XSL file:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
    <xsl:output method="xml" encoding="UTF-8" indent="no"/>
    <xsl:template match="/">
        <person>
            <xsl:element name="name">
                <xsl:value-of select="/person/name"/>
            </xsl:element>
            <xsl:element name="DoB">
                <xsl:value-of select="/person/DoB"/>
            </xsl:element>
            <Temperament>Quick to anger</Temperament>
            <Job>Unemployed</Job>
        </person>
    </xsl:template>
</xsl:stylesheet>

And the result would be:

<?xml version="1.0" encoding="UTF-8"?>
<person>
    <name>John</name>
    <DoB>2016-01-01</DoB>
    <Temperament>Quick to anger</Temperament>
    <Job>Unemployed</Job>
</person>
Community
  • 1
  • 1
Maarten
  • 22,527
  • 3
  • 47
  • 68