属性"{http://www.w3.org/2001/XMLSchema-instance}type",通常带有前缀“xsi:type”,是多态元素显式断言其类型的标准方式。 XmlSerializer 将其用于determine the type to deserialize -- 并因此在序列化期间将其写入以供以后使用。
没有简单的方法可以抑制类型的输出,同时为每个多态类型保持相同的元素名称。 (如果AParty 的每个子类都有不同的元素名称,这会很容易,但你没有。)最好的选择是ManifestHeader 实现IXmlSerializable。您没有完全指定 ManifestHeader,因此请考虑以下示例:
[XmlRoot("ManifestHeader", Namespace = ManifestHeader.XmlNamespace)]
public class ManifestHeader : IXmlSerializable
{
public const string XmlNamespace = "MyNamespace";
public static XmlSerializerNamespaces GetXmlSerializerNamespaces()
{
var ns = new XmlSerializerNamespaces();
ns.Add("", ManifestHeader.XmlNamespace);
return ns;
}
// Some example properties
public string AProperty { get; set; }
public string ZProperty { get; set; }
// The list of parties.
public AParty[] Parties { get; set; }
#region IXmlSerializable Members
XmlSchema IXmlSerializable.GetSchema()
{
return null;
}
void IXmlSerializable.ReadXml(XmlReader reader)
{
throw new NotImplementedException();
}
void IXmlSerializable.WriteXml(XmlWriter writer)
{
var ns = GetXmlSerializerNamespaces();
writer.WriteElementString("ZProperty", ZProperty);
foreach (var value in Parties)
{
XmlSerializationHelper.SerializeElementTo(value, "Party", ManifestHeader.XmlNamespace, writer, ns);
}
writer.WriteElementString("AProperty", AProperty);
}
#endregion
}
public abstract class AParty
{
[XmlAttribute]
public abstract string Role { get; set; } // Returns a constant string; setter does nothing.
}
这会手动序列化ManifestHeader(如果有)的属性,循环遍历Party 数组的元素并序列化每个元素,将它们的元素名称替换为"Party"。
它使用以下辅助方法。请注意,如果要使用 XmlSerializer(Type, XmlRootAttribute) 构造函数覆盖根元素名称,则 必须 cache the serializer in a hash table to avoid a horrible memory leak:
public static class XmlSerializationHelper
{
public static void SerializeElementTo<T>(T value, string elementName, string elementNamespace, XmlWriter writer, XmlSerializerNamespaces ns)
{
var serializer = XmlSerializerRootAttributeCache.DemandSerializer(value.GetType(), elementName, elementNamespace);
serializer.Serialize(writer, value, ns);
}
public static string GetXml<T>(this T obj)
{
return GetXml(obj, false);
}
public static string GetXml<T>(this T obj, bool omitNamespace)
{
return GetXml(obj, new XmlSerializer(obj.GetType()), omitNamespace);
}
public static string GetXml<T>(this T obj, XmlSerializer serializer)
{
return GetXml(obj, serializer, false);
}
public static string GetXml<T>(T obj, XmlSerializer serializer, bool omitStandardNamespaces)
{
XmlSerializerNamespaces ns = null;
if (omitStandardNamespaces)
{
ns = new XmlSerializerNamespaces();
ns.Add("", ""); // Disable the xmlns:xsi and xmlns:xsd lines.
}
return GetXml(obj, serializer, ns);
}
public static string GetXml<T>(T obj, XmlSerializer serializer, XmlSerializerNamespaces ns)
{
using (var textWriter = new StringWriter())
{
var settings = new XmlWriterSettings() { Indent = true, IndentChars = " " }; // For cosmetic purposes.
using (var xmlWriter = XmlWriter.Create(textWriter, settings))
serializer.Serialize(xmlWriter, obj, ns);
return textWriter.ToString();
}
}
public static string GetXml<T>(this T obj, XmlSerializerNamespaces ns)
{
return GetXml(obj, new XmlSerializer(obj.GetType()), ns);
}
}
public static class XmlSerializerRootAttributeCache
{
readonly static Dictionary<Tuple<Type, string, string>, XmlSerializer> cache;
readonly static object padlock = new object();
static XmlSerializerRootAttributeCache()
{
cache = new Dictionary<Tuple<Type, string, string>, XmlSerializer>();
}
static XmlSerializer CreateSerializer(Type rootType, string rootName, string rootNamespace)
{
return new XmlSerializer(rootType, new XmlRootAttribute { ElementName = rootName, Namespace = rootNamespace });
}
public static XmlSerializer DemandSerializer(Type rootType, string rootName, string rootNamespace)
{
var key = Tuple.Create(rootType, rootName, rootNamespace);
lock (padlock)
{
XmlSerializer serializer;
if (!cache.TryGetValue(key, out serializer))
serializer = cache[key] = CreateSerializer(rootType, rootName, rootNamespace);
return serializer;
}
}
}
这是一个简单的测试用例:
[XmlRoot("ChargeParty", Namespace = ManifestHeader.XmlNamespace)]
[XmlType("ChargeParty", Namespace = ManifestHeader.XmlNamespace)]
public sealed class ChargeParty : AParty
{
[XmlAttribute]
public override string Role
{
get
{
return "CHARGE";
}
set
{
}
}
public bool IsCharging { get; set; }
}
[XmlRoot("SenderParty", Namespace = ManifestHeader.XmlNamespace)]
[XmlType("SenderParty", Namespace = ManifestHeader.XmlNamespace)]
public sealed class SenderParty : AParty
{
[XmlAttribute]
public override string Role
{
get
{
return "SENDER";
}
set
{
}
}
public string SenderName { get; set; }
}
public static class TestClass
{
public static void Test()
{
var manifest = new ManifestHeader
{
AProperty = "A property",
ZProperty = "Z Property",
Parties = new AParty[] { new SenderParty { SenderName = "Sender Name" }, new ChargeParty { IsCharging = true }, new SenderParty { SenderName = "Another Sender Name" }, new SenderParty { SenderName = "Yet Another Sender Name" }, new ChargeParty { IsCharging = false } }
};
var xml = manifest.GetXml(ManifestHeader.GetXmlSerializerNamespaces());
Debug.WriteLine(xml);
}
}
产生:
<ManifestHeader xmlns="MyNamespace">
<ZProperty>Z Property</ZProperty>
<Party Role="SENDER">
<SenderName>Sender Name</SenderName>
</Party>
<Party Role="CHARGE">
<IsCharging>true</IsCharging>
</Party>
<Party Role="SENDER">
<SenderName>Another Sender Name</SenderName>
</Party>
<Party Role="SENDER">
<SenderName>Yet Another Sender Name</SenderName>
</Party>
<Party Role="CHARGE">
<IsCharging>true</IsCharging>
</Party>
<AProperty>A property</AProperty>
</ManifestHeader>