ContentSerializer & Automatic XNB Serialization

Apr 8, 2010 at 5:55 PM

XNA Game Studio 3.1 offers automatic XNB serialization (without the need to have a ContentTypeWriter and ContentTypeReader) using reflection and the ContentSerializer and ContentSerializerIgnore attributes. I noticed that ContentSerializerIgnoreAttribute is available in SilverSprite, but ContentSerializerAttribute (also in the Microsoft.Xna.Framework.Content namespace) unfortunately is not, which prevents something such as [ContentSerializer(SharedResource = true)] applied to my properties not to compile. What is the best strategy to follow? Are there any plans to support automatic XNB serialization and this attribute?

Applying the attribute such as [ContentSerializer(ElementName = "MyName")] or [ContentSerializer(CollectionItemName="MySubItem")] also doesn't work of course, but that's more interesting for the IntermediateSerializer to XML.

Apr 9, 2010 at 7:02 PM

Hmmm, simplifying my code (using ContentTypeWriter and ContentTypeReader again) and just using basic stuff for my data, I noticed that even then my "Levels\Level01.xnb" can't get loaded. I now realize that it's probably impossible with the current version to load custom types. For .xnb files, right now, only spritefonts and textures seem to be supported. Alternatively, is there a way I could get my "Levels\Level01.xml" file loaded instead? I'm puzzled.. :/

Apr 11, 2010 at 11:01 PM

Do you have a simple sample that I can play with? I'd really like to support this kind of thing but need some good test cases to see if it's possible.

Apr 12, 2010 at 4:07 PM

Hey Bill! Glad to hear you're thinking about supporting custom types! That would make SilverSprite even better than it already is! And it's also the top user request on UserVoice I noticed :)

I'd gladly help out with some sample projects! I typed together some code to illustrate all this serialization behavior and learned a lot more about it in the process as well. I'm still trying to find my way around XNA and this was extremely helpful for me to do actually. I tried to include as much as possible serialization behavior in as little code as possible.

You can get all code here:

Now for some info:

There's the SerialGame solution containing:

SerialData contains all the game data: Class1, Class2 and Class3 (and its ContentReader Class3.Reader for XNB deserialization) and a SerialDataBase class for these 3 data types, as well as Shawn Hargreaves' SharedResourceList (and its ContentReader SharedResourceList.Reader for XNB deserialization). Class1 and Class2 use automatic XNB deserialization, therefore, they have no ContentReader available.

SerialPipeline contains the pipeline extensions for building the data types: Class3Writer for Class3 (Class1 and Class2 use automatic XNB serialization, hence no ContentWriter for them here: they are XNB serialized using reflection), a SharedResourceListSerializer, i.e. a ContentTypeSerializer for SharedResourceList's XML serialization (for the IntermediateSerializer) and a SharedResourceListWriter for XNB serialization.

SerialGame loads an XNB from file and displays a dump of the custom type it just read. This XNB file gets built by SerialGame's Content project containing the XML file the test data got serialized into (by the IntermediateSerializer, using reflection and that with help from that SharedResourceListSerializer from the pipeline extension). Building the XNB file uses automatic serialization (reflection) and the ContentWriters where available to get to its result.

SerialTest is the one that you should run as it demonstrates all of the above in one interface: creating the test data (shows in the text box what got created), serializing this data to XML (file gets read back and shown in the text box as well) and after the XNB got built from the XML, it allows you to start the game which loads the XNB and displays the contents.

And there's the SilverSerialGame solution containing:

SilverSerialData: which is SerialData using SilverSprite. Everything is just links to the code files of the project above.

SilverSerialGame: which is SerialGame using SilverSprite. Running this one demonstrates the fact that custom types are not supported by SilverSprite yet ;-) It also contains an empty implementation for ContentSerializerAttribute to avoid getting compilation errors because SilverSprite doesn't have the ContentSerializerAttribute yet. Everything is just links to the code files and XNB content files built by the projects above.

One of my biggest misunderstandings was the fact that I thought that the ContentReader had anything to do with deserializing my XML files (which it doesn't!) and that ContentTypeSerializers had (besides the XML serialization) anything to do with reading and writing XNB files (which they don't!). Another very tricky thing is that I needed to make sure the SharedResourceList's ContentTypeSerializer and its assembly (SerialPipeline) got referenced when serializing the XML (IntermediateSerializer) otherwise the SharedResourceList's items got dumped in the XML file without handling them as shared resources. I thought that was tricky because there's no clear "connection" in the code between the use of the IntermediateSerializer and the SharedResourceListSerializer...

In the end I realized that for SilverSprite to handle custom types it needs to be able to load XNB files and deserialize custom types using either reflection or by using a ContentReader when available. All the other XML (de)serialization stuff (albeit all very handy for level editors and such, i.e. in order to avoid having to edit XML files by hand) was just obscuring what really happens on the run-time (SilverSprite) side: loading the XNB files.

Hope this helps... It sure did help me understand XML serialization and the XNA content pipeline a bit better. 

Apr 13, 2010 at 1:14 PM

Wow this is a great test program and it really should help me make this work. Thank you for putting it together, hopefully I should have something in the next week or so. 

Apr 25, 2010 at 12:20 PM

Ok I have checked in support for this, there is still a bit more work to be done but most of it works. Please try it out and let me know.

May 1, 2010 at 7:21 PM
Edited May 2, 2010 at 2:42 PM

I'm not clear about this. Does silversprite support custom content? I have run into a problem where I am getting a "TypeReader not found" error and didn't know if that is something I did or something silversprite doesn't support.

Some additional details;

Microsoft.Xna.Framework.Content.ContentTypeReaderManager.GetReaderType(string readerTypeString)

Seems to be unable to resolve the type, which appears to be correctly defined.

Potential Solution;

As per above the method Type.GetType() is not able to resolve my custom type. I don't know why. Normally I would just iterate through the assemblies in the current appdomain to find it but AppDomain.CurrentDomain does not have a GetAssemblies method in Silverlight; Instead you need to loop through the Deployment.Current.Parts collection. The following replaces the code in the above indicated method;

Type type = null;
foreach (System.Windows.AssemblyPart part in System.Windows.Deployment.Current.Parts)
    System.Windows.Resources.StreamResourceInfo info = System.Windows.Application.GetResourceStream(new Uri(part.Source, UriKind.Relative));
    Assembly assembly = new System.Windows.AssemblyPart().Load(info.Stream);
    type = assembly.GetType(readerTypeString);
    if (type != null) return type;
return null;

Also note, I ran into a problem using the AssemblyQualifiedName in the GetRuntimeReader method. The reason is my standard game library is in one assembly and the silverlight version is in another (classes are linked). When I compile my content in the standard version of my game library it referenced the standard assembly not the silverlight assembly. By using the FullName instead, the assembly.GetType(readerTypeString) method call ignored the assembly name.

May 2, 2010 at 2:27 PM

Would you be able to provide a stripped down version of your project that reproduces the problem?



May 2, 2010 at 2:46 PM

It may take a while. We are looking at two solutions and five projects so it is a fair amount of work. I'll see what I can do.

May 3, 2010 at 12:32 AM

I uploaded the code to the issue tracker thread. From now on lets use that thread rather than this one. Thanks

May 3, 2010 at 1:08 AM

Ok I'll take a look, one thing I ran into when implementing this feature is that your Silverlight assembly name that contains the class you're serializing MUST match the XNA name of the assembly. This is becuase the assembly name is part of the type identifier. From a quick look at the issue it looks like that might not be the case?

May 15, 2010 at 2:36 PM

Hey Bill,

First of all, I want to apologize for not being there in time to try out your modifications, but life has been preventing me from what I actually want to be doing. But today I finally could make some time.

A lot changed during my absence it seems: I needed to get the latest SilverSprite revision (47441), Silverlight 4.0 Runtime, Silverlight 4.0 SDK and using the Web Platform Installer also got .NET Framework 4.0, Visual Studio 2010 Web Developer Express, Silverlight 4 Toolkit for Visual Studio 2010 and whatnot else. Then I converted the SilverSerialGame solution, added the updated SilverSprite sources and updated the references. Lots of time later, I was ready for a first test run. However, I got a ContentLoadException: "Bad XNB", "Unable to read beyond the end of the stream." at Microsoft.Xna.Framework.Content.ContentReader.ReadAsset[T]().

Unfortunately, I don't have the time to investigate this further today nor anytime soon, but I'm pretty sure I oversaw something silly. But it doesn't matter. Most importantly I wanted to express my thanks for implementing custom types in SilverSprite! I'll do some more testing if I've got some more time...

May 17, 2010 at 3:39 PM

The error indicates that your are trying to read more bytes than there are in the file. Check to make sure you are adding items correctly to your xnb file. I get those errors when I try to read assets that, for one reason or another, were missed during the write operation.