views:

246

answers:

2

I'd like to do this the right way if possible. I have XML data as follows:

<?xml version="1.0" encoding="utf-8"?>
    <XnaContent>
        <Asset Type="PG2.Dictionary">
            <Letters TotalInstances="460100">
                <Letter Count="34481">&#97;</Letter>
                ...
                <Letter Count="1361">&#122;</Letter>
            </Letters>
            <Words Count="60516">
                <Word>aardvark</Word>
                ...
                <Word>zebra</Word>
            </Words>
        </Asset>
    </XnaContent>

and I'd like to load this in (using Content.Load< Dictionary >) into one of these

namespace PG2
{
    public class Dictionary
    {
        public class Letters
        {
            public int totalInstances;

            public List<Character> characters;

            public class Character
            {
                public int count;
                public char character;
            }
        }

        public class Words
        {
            public int count;
            public HashSet<string> words;
        }

        Letters letters;
        Words words;
    }
}

Can anyone help with either instructions or pointers to tutorials? I've found a few which come close but things seem to have changed slightly between 3.1 and 4.0 in ways which I don't understand and a lot of the documentation assumes knowledge I don't have. My understanding so far is that I need to make the Dictionary class Serializable but I can't seem to make that happen. I've added the XML file to the content project but how do I get it to create the correct XNB file?

Thanks! Charlie.

A: 

This may help http://blogs.msdn.com/b/shawnhar/archive/2009/03/25/automatic-xnb-serialization-in-xna-game-studio-3-1.aspx. I found it useful to work the other way round to check that my xml data was correctly defined. Instantate your dictionary class set all the fields then serialize it to xml using a XmlSerializer to check the output.

Grant
Thanks, this is interesting, but I need to be able to deal with XML data in a format which might not quite match the object. It looks like I'll need to use the ContentReader/Writer pattern but does this still exist in 4.0? Seems to docs are catching up a bit.
cskilbeck
A: 

You need to implement a ContentTypeSerializer for your Dictionary class. Put this in a content extension library and add a reference to the content extension library to your content project. Put your Dictionary class into a game library that is reference by both your game and the content extension project.

See: http://blogs.msdn.com/b/shawnhar/archive/2008/08/26/customizing-intermediateserializer-part-2.aspx

Here is a quick ContentTypeSerializer I wrote that will deserialize your Dictionary class. It could use better error handling.

using System;
using System.Collections.Generic;
using System.Xml;
using Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate;

namespace PG2
{
    [ContentTypeSerializer]
    class DictionaryXmlSerializer : ContentTypeSerializer<Dictionary>
    {
        private void ReadToNextElement(XmlReader reader)
        {
            reader.Read();

            while (reader.NodeType != System.Xml.XmlNodeType.Element)
            {
                if (!reader.Read())
                {
                    return;
                }
            }
        }

        private void ReadToEndElement(XmlReader reader)
        {
            reader.Read();

            while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
            {
                reader.Read();
            }
        }

        private int ReadAttributeInt(XmlReader reader, string attributeName)
        {
            reader.MoveToAttribute(attributeName);
            return int.Parse(reader.Value);
        }

        protected override Dictionary Deserialize(IntermediateReader input, Microsoft.Xna.Framework.Content.ContentSerializerAttribute format, Dictionary existingInstance)
        {
            Dictionary dictionary = new Dictionary();
            dictionary.letters = new Dictionary.Letters();
            dictionary.letters.characters = new List<Dictionary.Letters.Character>();
            dictionary.words = new Dictionary.Words();
            dictionary.words.words = new HashSet<string>();

            ReadToNextElement(input.Xml);
            dictionary.letters.totalInstances = ReadAttributeInt(input.Xml, "TotalInstances");

            ReadToNextElement(input.Xml);

            while (input.Xml.Name == "Letter")
            {
                Dictionary.Letters.Character character = new Dictionary.Letters.Character();

                character.count = ReadAttributeInt(input.Xml, "Count");

                input.Xml.Read();
                character.character = input.Xml.Value[0];

                dictionary.letters.characters.Add(character);
                ReadToNextElement(input.Xml);
            }

            dictionary.words.count = ReadAttributeInt(input.Xml, "Count");

            for (int i = 0; i < dictionary.words.count; i++)
            {
                ReadToNextElement(input.Xml);
                input.Xml.Read();
                dictionary.words.words.Add(input.Xml.Value);
                ReadToEndElement(input.Xml);
            }

            ReadToEndElement(input.Xml);    // read to the end of words
            ReadToEndElement(input.Xml);    // read to the end of asset

            return dictionary;
        }

        protected override void Serialize(IntermediateWriter output, Dictionary value, Microsoft.Xna.Framework.Content.ContentSerializerAttribute format)
        {
            throw new NotImplementedException();
        }
    }
}
Empyrean