问题
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
string
andstring []
are the possible types of yourobject
property, 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
XmlSerializer
to deserialize the XML into the same type of objects as were originally serialized.(Note that
[XmlInclude(typeof(string))]
seems to be unnecessary asstring
is 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
myObject
element been modified to the string passed to into the[XmlElement(String, Type)]
attribute constructor. This allowsXmlSerializer
to 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