XmlSerializer - Ignoring inherited unserializable member

﹥>﹥吖頭↗ 提交于 2020-01-21 14:57:09

问题


Assume you have two classes, one inherits the other and the child needs to be serialized / deserialized with XmlSerializer. However, the parent contains a member that is not serializeable, say a dictionary.

public class Parent {

    public Dictionary<string, int> dictionary;
}

The parent class is a library used for many other scripts. It cannot be modified. Now the child class contains only serializable members:

public class Child : Parent {

    [XmlElement]
    public int foo;

}

When trying to call the serializer, I receive an error saying that the dictionary is not serializable. When trying to serialize in JSON, I managed to get away by the price of a warning. I just created another member with the same name and type, and used ScriptIgnore:

public class Child : Parent {

    public int foo;

    [ScriptIgnore]
    public Dictionary<string, int> dictionary;

}

I tried the same trick again here (by using XmlIgnore) but that didn't work on very well, the error was the same. The only way I managed to go through this is create separate classes that will only serve xml de/serializing and then copy the values back into the appropriate place.

Does anyone know a better way around this? Can I make XmlSerializer forget about the parent dictionary in any way?


回答1:


The very first thing I would say, and always say: if serializing an existing model gets tricky - even remotely awkward, then stop doing that. Take 2 minutes to create a separate DTO model, i.e. a model created solely for the purposes of serialization (and indeed, perhaps even tailored to a specific serializer). Now you put the exact right types, right members, right attributes, and right layout. All you need to do is add some conversion methods - static conversion operators work great here. So what I would say is: create a ParentDto and ChildDto (your names may vary); it'll take 3 minutes, and it'll work great.

Now, back to the question...

XmlSerializer looks at the declaring class for input; for both attributes and conditional serialization, no: we can't add those into the type model at this point. But there is another option - you can use XmlAttributeOverrides to pretend that there was an [XmlIgnore] on the dictionary member. However, some important caveats:

  • the XmlAttributeOverrides API is a bit of a faff to use (see MSDN for an example)
  • it is critical that you only do this once, and then store and re-use the XmlSerializer that you create this way; basically, if you don't do this, it will create a new dynamic assembly every time you new a serializer, and assemblies never unload, so you will haemorrhage memory; note that the simple usage (new XmlSerializer(someType) etc) has an inbuilt cache for this; but the XmlAttributeOverrides usage does not

But again, all this messing with XmlAttributeOverrides is a lot more work than just creating a basic DTO

Example of using XmlAttributeOverrides:

using System;
using System.Collections.Generic;
using System.Xml.Serialization;
public class Parent {
    public Dictionary<string, int> WantToIgnoreThis { get; set; }
}    
public class Child : Parent {
    public int Foo { get; set; }
}
static class Program
{
    static readonly XmlSerializer customSerializer;

    static Program()
    {
        var xao = new XmlAttributeOverrides();
        xao.Add(typeof(Parent), "WantToIgnoreThis", new XmlAttributes {
            XmlIgnore = true
        });
        customSerializer = new XmlSerializer(typeof(Child), xao);
    }
    static void Main()
    {
        //var ser = new XmlSerializer(typeof(Child));
        // ^^ this would fail

        customSerializer.Serialize(Console.Out, new Child {
            Foo = 123
        });
    }
}

Note in particular how the static field is used to cache the serializer.




回答2:


You could implement IXmlSerializable yourself and handle the specifics in ReadXml(XmlReader reader) and WriteXml(XmlWriter writer). The XmlSerializer will call those methods if your class implements them instead of generating its own serializer.

public class Child : Parent, IXmlSerializable
{
    public int Foo { get; set; }
    public Dictionary<string, int> Dictionary { get; set; }

    public void WriteXml(XmlWriter writer)
    {
        writer.WriteStartElement("Foo");
        writer.WriteValue(this.Foo);
        writer.WriteEndElement();
    }

    void ReadXml(XmlReader reader)
    {            
        var wasEmpty = reader.IsEmptyElement;
        reader.Read();

        if (wasEmpty)
        {
            return;
        }

        reader.ReadStartElement("Foo");
        this.Foo = reader.ReadContentAsInt();
        reader.ReadEndElement();
    }
}


来源:https://stackoverflow.com/questions/18588083/xmlserializer-ignoring-inherited-unserializable-member

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!