2

So i have a class, Texture2DProcessor, that inherits IXmlSerializable and Implicitly casts to and from Texture2D

public static implicit operator Texture2D(Texture2DProcessor o)
{
    return o.Data;
}
public static implicit operator Texture2DProcessor(Texture2D o)
{
    return o == null ? null : new Texture2DProcessor(o);
}

I then have a struct, GunProperties, that contains a Texture2D property with an XmlElement attribute with the type set to Texture2DProcessor

Texture2D _sideSprite;
[XmlElement(typeof(Texture2DProcessor))]
public Texture2D SideSprite
{
    get { return _sideSprite; }
    set { _sideSprite = value; }
}

I get the following runtime error

Cannot serialize member '...GunProperties.SideSprite' of type 'Microsoft.Xna.Framework.Graphics.Texture2D'

why is XmlSerializer not using Texture2DProcessor to read and write the Xml data?

Also I know that my ReadXml and WriteXml methods work because this works fine and i am able to use the texture.

Texture2D texture;
XmlSerializer serializer = new XmlSerializer(typeof(Texture2DProcessor));
serializer.Deserialize(new FileStream(path, FileMode.Open), texture);

The reason I'm going through this trouble is I'm using monogame and the content pipeline is pretty messed up especially for custom types and other than this issue i have it all working.

Daniel Johnson
  • 691
  • 4
  • 14

1 Answers1

1

It looks like this may be a Mono XmlSerializer limitation. I have a small test app which works under .NET, but not under Mono 3.0.6.

But this looks fairly simple to work around:

[XmlIgnore]
public Texture2D SideSprite { get; set; }

[XmlElement("SideSprite")]
[EditorBrowsable(EditorBrowsableState.Never)]
public Texture2DProcessor SideSpriteProcessor
{
    get { return SideSprite; }
    set { SideSprite = value; }
}

We have the same issue in my Noda Time project, as XML serialization doesn't mix well with immutable types. I've given pretty much the same advice there.

EDIT: Okay, so here's some sample code which does work:

using System;
using System.IO;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;

public class Person
{
    public string Name { get; set; }
}

public class PersonWrapper : IXmlSerializable
{
    public Person Person { get; set; }

    public static implicit operator Person(PersonWrapper wrapper)
    {
        return wrapper == null ? null : wrapper.Person;
    }

    public static implicit operator PersonWrapper(Person person)
    {
        return new PersonWrapper { Person = person };
    }

    public XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        string name = reader.ReadString();
        reader.ReadEndElement();
        Person = new Person { Name = name };
    }

    public void WriteXml(XmlWriter writer)
    {
        writer.WriteString(Person.Name);
    }
}

public class Company
{
    [XmlElement(typeof(PersonWrapper))]
    public Person Director { get; set; }

    [XmlElement(typeof(PersonWrapper))]
    public Person Manager { get; set; }
}

class Test
{
    static void Main()
    {
        var serializer = new XmlSerializer(typeof(Company));

        var original = new Company
        {
            Director = new Person { Name = "Holly" },
            Manager = new Person { Name = "Jon" }
        };

        var writer = new StringWriter();
        serializer.Serialize(writer, original);
        Console.WriteLine("XML:");
        Console.WriteLine(writer.ToString());

        var reader = new StringReader(writer.ToString());
        var deserialized = (Company) serializer.Deserialize(reader);

        Console.WriteLine("Deserialized:");
        Console.WriteLine("Director: {0}", deserialized.Director.Name);
        Console.WriteLine("Manager: {0}", deserialized.Manager.Name);
    }
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Wow I didn't think about having a seperate property for my textureprocessor. Does this "[EditorBrowsable(EditorBrowsableState.Never)]" make it so where it won't show up in intelisence? – Daniel Johnson May 18 '13 at 07:10
  • @DanielJohnson: Yup. You *can* still refer to it from code, but hopefully people won't :) – Jon Skeet May 18 '13 at 07:12
  • It would really be nice if i could just have one attribute to go on a variable to make it use the processor. It worked in this [link](http://stackoverflow.com/questions/20084/xml-serialization-and-inherited-types) – Daniel Johnson May 18 '13 at 07:12
  • @DanielJohnson: I confess I don't understand how that version works but yours doesn't. I'm more surprised that that works than that yours doesn't though. Hmm. I'll see if I can investigate a bit further later, as if we *could* solve it that way for Noda Time, it would be useful for me too. – Jon Skeet May 18 '13 at 07:29
  • I was thinking it has something to do with it being a generic class and the implicit conversions use the generic type so when you put it in the XmlElement it treats it as a child class – Daniel Johnson May 18 '13 at 07:33
  • @DanielJohnson: Nope, I've just managed to get an implicit conversion working myself. Does `Texture2DProcessor` implement `IXmlSerializable`? (My quick sample does, but it may not be required.) – Jon Skeet May 18 '13 at 07:37
  • @DanielJohnson: Perhaps this is a Mono `XmlSerializer` limitation? (When I run my little test app under Mono it looks like it's *trying* to do the right thing, but not quite managing.) – Jon Skeet May 18 '13 at 08:02
  • No because i'm not using any mono dlls for Xml – Daniel Johnson May 18 '13 at 08:10
  • @DanielJohnson: You say you're using MonoGame. Doesn't that mean you're running in a Mono environment? (The Mono implementation of the BCL etc, rather than the Microsoft one.) – Jon Skeet May 18 '13 at 08:12
  • I don't think so. I'm using Visual Studio 2010 and if i look at the properties for my system and system.xml dlls they come from the .NET 4.0 libraries. Plus i'm calling the methods from a windows form (my gun editor) that references the dll i have for my content management and the Game that holds the GunProperties class. Edit: It's getting late so I'll be back in the morning. – Daniel Johnson May 18 '13 at 08:17
  • @DanielJohnson: You can have references to the normal assemblies, but it's what *runs* the code that matters. How is the code *running*? – Jon Skeet May 18 '13 at 08:26
  • I don't know what you mean by how is the code running. I'm running it with the debugger but that doesn't make too much sense since this works [link](http://stackoverflow.com/questions/20084/xml-serialization-and-inherited-types) – Daniel Johnson May 18 '13 at 14:35
  • @DanielJohnson: Does that work *in a MonoGame project* though? I don't know anything about MonoGame, but judging by the name I'd *expect* it to be running in Mono... – Jon Skeet May 18 '13 at 14:53
  • Yes abstractxmlserializer works in monogame. I'm not 100 percent sure ill have to test it but I'm sure I've use it in a lot of my other classes and it works fine. The project seems to use all of the .net framework libraries. The only mono libraries referenced is the monogame.dll which includes all of the namespaces that xna used. There are no system or XML classes in monogame besides its content pipeline which is a mess and the whole reason I'm doing this. – Daniel Johnson May 18 '13 at 16:57
  • @DanielJohnson: I think you're still missing my point. I didn't suggest you were using mono-specific libraries. I suspect you're using the Mono implementation of the standard libraries (at execution time). – Jon Skeet May 18 '13 at 17:06
  • Yeah I understand that mono would have its own implementation of standard libraries and just looking at the references it would still look as if it is referencing System and System.Xml libraries. but if I click on the system or the system.xml reference then in the properties tab for the dll file location it is not in the mono folder it is in the .NETFramework folder meaning it is using the standard implementation right? Or am I still missing something? Edit: The exact location of the System dll is C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.dll – Daniel Johnson May 18 '13 at 17:16
  • When your creating a monogame project you have to choose the platform your wanting to code for. The API is all the same but the dll it references i believe is platform specific. The platform my game is being coded for right now is Windows so i believe it automatically chose the standard library DLLs rather than any mono implementations for a different platform – Daniel Johnson May 18 '13 at 17:22
  • @DanielJohnson: That doesn't matter. You can still run your application under Mono and it would still pick up the Mono implementation. Really and truly. – Jon Skeet May 18 '13 at 17:22
  • I'm going to try and create another project that doesn't run under mono and copy the exact code and see if it works. – Daniel Johnson May 18 '13 at 17:24
  • @DanielJohnson: That would be good. A short but complete console app that reproduces the problem would be ideal. – Jon Skeet May 18 '13 at 17:24
  • One way of detecting whether or not you're running under Mono is to see whether `Type.GetType("Mono.Runtime")` returns `null` – Jon Skeet May 18 '13 at 17:25
  • I ran this at the start of my program and it returned false Console.WriteLine(Type.GetType("Mono.Runtime") != null); In my standalone console application I'm going to use the XNA refrences rather than the monogame refrences and see if there is a difference – Daniel Johnson May 18 '13 at 17:29
  • @DanielJohnson: Okay. In that case it may be a different problem. A small console app would be great - there's hope yet :) – Jon Skeet May 18 '13 at 17:48
  • ok so i finished a console app. unfortunately i had to go back to the MonoGame library because i didn't want to have to rewrite how i get the GraphicsDevice, but its still returning false on the Mono.Runtime so it shouldn't be a problem. I have it zipped up how should i send it to you? I can upload it to my website if you want Edit: I uploaded it to my website temporarily [DL Link](http://cracked-ice.com/GunPropertiesSerializationTest.zip) – Daniel Johnson May 18 '13 at 17:54
  • OK! I made a breakthrough. it seams that the reason it is not working is because Texture2D is not serializable. It doesn't matter what i put in the type attribute because it looks at the actual type to make sure it is serializable before it actually looks at the attribute. but why does it do this? that doesn't make sense because i would think the main point of being able to specify the type in an element attribute would be to change the type it serializes to. – Daniel Johnson May 20 '13 at 02:14
  • @DanielJohnson: Hmmm... that's not what my tests showed. I'll include a short console app which shows it working, so that you can play with that. – Jon Skeet May 20 '13 at 05:33
  • Ok but the problem here is that person can be serialized without the PersonWrapper. If you put a Contrustor in person "public Person(string name)" It would give you the error. – Daniel Johnson May 20 '13 at 20:27
  • @DanielJohnson: Ah, yes. That's interesting - because the conversions were definitely being invoked. Very odd. – Jon Skeet May 20 '13 at 22:41
  • The documentation states that the Type parameter is meant for inheriting classes so i guess the thinking behind the designers was that if it is not serializable than the Type class won't be either. Should we bring this to someone elses attention? – Daniel Johnson May 20 '13 at 23:08
  • @DanielJohnson: Right, that could be it. Odd though. Given the fairly sparse documentation, I'm not sure it's worth raising anywhere, to be honest. – Jon Skeet May 20 '13 at 23:11
  • i'm not too familiar about how to use the reader and writer of the IXmlSerializable interface, but what could be done is implement a generic class like AbstractXmlSerializer that would look at each field and find the XmlElementAttribute and cast it to the Type before it serializes it then when we want to serialize a class with it we put in "serializer.Serialize(new XmlTypeSerializer(objecttoserilize));" but i wouldn't know how to go about writing the XmlTypeSerializer. – Daniel Johnson May 20 '13 at 23:22
  • @DanielJohnson: At that point you're effectively opting out of *all* the automated XML serialization - so you might as well not bother with IXmlSerializable. – Jon Skeet May 20 '13 at 23:28
  • OK so I started making my own XmlSerializer for a different reason than this but while i was at it i decided to add the functionality of implicitly casting. I actually think I've figured out why it doesn't work because when i try and implicitly cast something through reflection it doesn't work. BUT I've found the solution. All you have to do is create a TypeConverter. It would be interesting to see if creating a TypeConverter will fix it for the built in serializer. – Daniel Johnson Jul 10 '13 at 22:16