问题
My question is probably arising from a basic misunderstanding of XML serialization, but anyways...
I'm trying to serialize a class containing an object which was initialized with an array using XMLSerializer class. Minimal example:
using System;
using System.IO;
using System.Xml.Serialization;
namespace XMLSerializationTest
{
class Program
{
static void Main(string[] args)
{
try
{
string xmlFileName = Environment.CurrentDirectory + @"\somename.xml";
XmlSerializer writer = new XmlSerializer(typeof(MyClass));
FileStream file = File.Create(xmlFileName);
MyClass someclass = new MyClass();
writer.Serialize(file, someclass);
file.Close();
}
catch (Exception exc)
{
Console.WriteLine(exc);
}
Console.ReadLine();
}
}
public class MyClass
{
public object myObject;
public MyClass()
{
myObject = new string[1] { "somestring" };
}
}
}
However, this throws System.InvalidOperationException, reading the array can't be used here. It works fine, if one replaces the array within the MyClass constructor, e.g., with a simple string like myObject = "somestring";. Unfortunately, I just don't know if my object will be an array or not in advance. So is there any possibility to solve this problem e.g. with attributes or is XML just the wrong way to go in this case?
回答1:
Your difficulty arises from the fact that XmlSerializer requires all types to be serialized to be discoverable statically, through reflection. However, your type MyClass has a polymorphic object property where a instance of a subtype of object -- specifically string [] -- is being stored. When XmlSerializer encounters it, since objects of this type are not expected, the serializer throws the exception you see.
When serializing polymorphic properties such as this, it is necessary to use XML serialization attributes to declare the types that can be encountered. XmlSerializer provides two mechanisms to accomplish this.
Declare the possible polymorphic subtypes using XmlInclude(Type) attributes on the containing type. Since
stringandstring []are the possible types of yourobjectproperty, you would do:[XmlInclude(typeof(string))] [XmlInclude(typeof(string[]))] public class MyClass { public object myObject { get; set; } public MyClass() { myObject = new string[] { "somestring" }; } }And the resulting XML will look like:
<MyClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <myObject xsi:type="ArrayOfString"> <string>somestring</string> </myObject> </MyClass>Notice the xsi:type attribute? That is a w3c standard attribute that allows the element to assert its type explicitly. Its presence allows
XmlSerializerto deserialize the XML into the same type of objects as were originally serialized.(Note that
[XmlInclude(typeof(string))]seems to be unnecessary asstringis apparently a built-in known type - though I cannot find documentation confirming this.)Declare the possible polymorphic subtypes using [XmlElement(String, Type)] on the polymorphic property itself. Thus you would do something like:
public class MyClass { [XmlElement("myObjectString", typeof(string))] [XmlElement("myObjectStringArray", typeof(string[]))] public object myObject { get; set; } public MyClass() { myObject = new string[] { "somestring" }; } }And the XML generated will look like:
<MyClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <myObjectStringArray> <string>somestring</string> </myObjectStringArray> </MyClass>Notice that the name of the
myObjectelement been modified to the string passed to into the[XmlElement(String, Type)]attribute constructor. This allowsXmlSerializerto deserialize the XML into the same type of objects as were originally serialized.
来源:https://stackoverflow.com/questions/38212559/xml-serialization-of-an-object-initialized-as-array