2

I'm working on a level editor currently in XNA/Monogame and I'm running into an issue whenever I want to improve my "Level" class. Because my XML file is serialized as an object of my Level class, whenever I make changes to it I can no longer deserialize the file.

For example, here's my Level class:

public class Level {
    public int width;
    public int height;
    public Tile[] tiles;

    public Level(Tile[,] tileArray) {
        width = tileArray.GetLength(0);
        height = tileArray.GetLength(1);
        tiles = new Tile[width * height];
        int i = 0;
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                tiles[i] = tileArray[x, y];
                i++;
            }
        }
    }
    public Level() {} // to allow for serialization
}

Now, if I were to change one line, say, add a string "name," I would get errors saying that I am unable to cast certain types to each other or get an "end of stream" error message:

public class Level {
    public int width;
    public int height;
    public Tile[] tiles;
    public string name;
    
    //...
}
An unhandled exception of type 'Microsoft.Xna.Framework.Content.ContentLoadException' occurred in MonoGame.Framework.dll
Content reader could not be found for System.String type.

Here's my XML Serializer and Deserializer functions:

public static void Save<T>(string path, T obj) {
    XmlWriterSettings settings = new XmlWriterSettings();
    settings.Indent = true;

    using (XmlWriter writer = XmlWriter.Create(path, settings)) {
        IntermediateSerializer.Serialize(writer, obj, path);
    }
    Debug.Print(path);
}

// I use the XNA content manager to load my XML file as the <XNAContent> header is added to the file.

It almost seems to be random which error I get when I do this. Is there any way I can convert my XML files to be compatible with the updated classes?

Connor
  • 51
  • 5
  • Take a look at the documentation. Is there a way you can make the field optional? Maybe you could set up different classes for each version and create a converted system to upgrade the data to the most modern version? – Timothy Stepanski Feb 23 '21 at 16:33
  • What serialization mechanism are you using? – Matthew Watson Feb 23 '21 at 16:33
  • I'm using a generic load function that uses TextReader and the XmlSerializer's Deserialize function. I added my save and load functions to my post. – Connor Feb 23 '21 at 16:39
  • 1
    Look into decoupling your `Level` class from your saved data. It would require writing a converter to take the xml input and instantiate your object but any future class changes could then be accounted for and mapped accordingly. Also read up on the open/closed principle. – Chris Pickford Feb 23 '21 at 16:39
  • I usually first serialize same data to XML and then compare the new XML against the XML your are trying to Parse (load) – jdweng Feb 23 '21 at 16:49
  • What happens if you apply a `[ContentSerializer(Optional = true)]` attribute to the new field? – Matthew Watson Feb 23 '21 at 16:50
  • @MatthewWatson I tried adding the attribute but it didn't seem to have an effect – Connor Feb 23 '21 at 16:56
  • @ChrisPickford Unless I'm misunderstanding what you said, are you saying I should serialize my XML file to a collection of variables instead of an object of my Level class? – Connor Feb 23 '21 at 17:01
  • @Connor that's one possible option. However you choose to do it, by uncoupling your class from your serialized data will give you far greater flexibility. – Chris Pickford Feb 23 '21 at 17:04
  • @ChrisPickford I'm not sure exactly what you mean by decoupling/uncoupling, can you send an example of what you mean? – Connor Feb 23 '21 at 17:09
  • @Connor https://en.wikipedia.org/wiki/SOLID – Chris Pickford Feb 23 '21 at 17:14
  • I can't reproduce your problem. If I use your `Load(string path)` method to deserialize an XML file that does not include `name`, them the file is deserialized successfully and `name` is null. Demo here: https://dotnetfiddle.net/Oxc8OF. In fact `XmlSerializer` has no way to mark a member as required, see [C# xml serialization required field](https://stackoverflow.com/q/24239500/3744182). Can you please [edit] your question to share a [mcve]? – dbc Feb 23 '21 at 21:44
  • @dbc Ah, I understand my problem now. I'm using XNA's content manager to load the XML file as it adds the field in the file. I forgot that I wasn't using my own load function for it and mistakenly put it in the question. I'll have to see if there's a way to generate the file without that header and still load it from the Content directory. Thank you for the demo, that helped a lot. I'll update my question to remove my load function. – Connor Feb 24 '21 at 02:19

0 Answers0