问题
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
XmlAttributeOverridesAPI 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
XmlSerializerthat you create this way; basically, if you don't do this, it will create a new dynamic assembly every time younewa 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 theXmlAttributeOverridesusage 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