我在我的模型上使用Xml属性来处理序列化我的模型。
基类是:
public class RequestRoot<T>
{
[XmlElement("Action")]
public T ActionNode { get; set; }
}
ActionNode
的类型为T,可以是从字符串到复杂对象集合的任何内容。
示例:
<?xml version="1.0" encoding="UTF-8"?>
<RequestRoot>
<Action Type="TypeValue A">
<SomeData>Some data</SomeData>
</Action>
</RequestRoot>
<?xml version="1.0" encoding="UTF-8"?>
<RequestRoot>
<Action Type="TypeValue B">
<MyObjects>
<MyObject>
<ObjectValue1>Object Value 1-1</ObjectValue1>
<ObjectValue2>Object Value 2-1</ObjectValue2>
</MyObject>
<MyObject>
<ObjectValue1>Object Value 1-2</ObjectValue1>
<ObjectValue2>Object Value 2-2</ObjectValue2>
</MyObject>
</MyObjects>
</Action>
</RequestRoot>
我的问题是:是否可以在我的模型上使用Xml属性来编写Type="TypeValue A"
或Type="TypeValue B"
,具体取决于T
是什么?
如果没有,我有哪些替代方案?
答案 0 :(得分:1)
开箱即用XmlSerializer
无法执行此操作。这是因为您的RequestRoot
类是通用的,XmlSerializer
根据XML元素名称和可能的"xsi:type"
属性确定要创建的对象类型。但是,您的类型信息嵌入在根元素的子元素Action
中,该元素在必须分配根时无法访问。
您需要做的是手动读取和写入RequestRoot
包装,然后使用XmlSerializer
作为内容。例如:
public abstract class RequestRootBase
{
[XmlIgnore]
public abstract Type RequestType { get; }
[XmlIgnore]
public abstract Object RequestObject { get; }
}
public class RequestRoot<T> : RequestRootBase
{
public RequestRoot() { }
public RequestRoot(T ActionNode) { this.ActionNode = ActionNode; }
[XmlElement("Action")]
public T ActionNode { get; set; }
public override Type RequestType
{
get { return typeof(T); }
}
public override object RequestObject
{
get { return ActionNode; }
}
}
public static class RequestRootHelper
{
public static RequestRootBase CreateBase(object action)
{
if (action == null)
throw new ArgumentNullException();
var type = action.GetType();
return (RequestRootBase)Activator.CreateInstance(typeof(RequestRoot<>).MakeGenericType(type), new [] { action });
}
public static RequestRoot<T> Create<T>(T action)
{
return new RequestRoot<T> { ActionNode = action };
}
}
public abstract class RequestRootXmlSerializerBase
{
const string RequestRootElementName = "RequestRoot";
const string ActionElementName = "Action";
const string TypeAttributeName = "Type";
protected abstract Type BindToType(string name);
protected abstract string BindToName(Type type);
static string DefaultRootXmlElementNamespace(Type type)
{
var xmlType = type.GetCustomAttribute<XmlRootAttribute>();
if (xmlType != null && !string.IsNullOrEmpty(xmlType.Namespace))
return xmlType.Namespace;
return null;
}
public void Serialize(RequestRootBase root, XmlWriter writer)
{
writer.WriteStartDocument();
writer.WriteStartElement(RequestRootElementName);
writer.WriteStartElement(ActionElementName);
var typeName = BindToName(root.RequestType);
writer.WriteAttributeString(TypeAttributeName, typeName);
var serializer = new XmlSerializer(root.RequestType);
var rootNameSpace = DefaultRootXmlElementNamespace(root.RequestType);
var ns = new XmlSerializerNamespaces();
if (string.IsNullOrEmpty(rootNameSpace))
ns.Add("", "");
else
ns.Add("", rootNameSpace);
serializer.Serialize(writer, root.RequestObject, ns);
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndDocument();
}
public RequestRootBase Deserialize(XmlReader reader)
{
if (!reader.ReadToFollowing(RequestRootElementName))
return null;
if (!reader.ReadToFollowing(ActionElementName))
return null;
var typeName = reader[TypeAttributeName];
if (typeName == null)
return null;
var type = BindToType(typeName);
if (type == null)
throw new InvalidDataException(); // THROW AN EXCEPTION in this case
reader.ReadStartElement();
var serializer = new XmlSerializer(type);
var action = serializer.Deserialize(reader);
if (action == null)
return null;
return RequestRootHelper.CreateBase(action);
}
public string SerializeToString(RequestRootBase root)
{
using (var textWriter = new StringWriter())
{
var settings = new XmlWriterSettings() { Indent = true, IndentChars = " " }; // For cosmetic purposes.
using (var xmlWriter = XmlWriter.Create(textWriter, settings))
Serialize(root, xmlWriter);
return textWriter.ToString();
}
}
public RequestRootBase DeserializeFromString(string xml)
{
using (var sr = new StringReader(xml))
using (var xmlReader = XmlReader.Create(sr))
{
return Deserialize(xmlReader);
}
}
}
public class RequestRootXmlSerializer : RequestRootXmlSerializerBase
{
readonly Dictionary<string, Type> nameToType = new Dictionary<string, Type>();
readonly Dictionary<Type, string> typeToName = new Dictionary<Type, string>();
const string ListPrefix = "ArrayOf";
const string ListPostFix = "";
protected override string BindToName(Type type)
{
return typeToName[type];
}
protected override Type BindToType(string name)
{
return nameToType[name];
}
public RequestRootXmlSerializer(IEnumerable<Type> types)
{
if (types == null)
throw new ArgumentNullException();
foreach (var type in types)
{
if (type.IsInterface || type.IsAbstract)
throw new ArgumentException();
var name = DefaultXmlElementName(type);
nameToType.Add(name, type);
typeToName.Add(type, name);
}
}
static string DefaultXmlElementName(Type type)
{
if (type.IsGenericType
&& type.GetGenericTypeDefinition() == typeof(List<>))
{
var elementType = type.GetGenericArguments()[0];
return ListPrefix + DefaultXmlElementName(elementType) + ListPostFix;
}
else
{
var xmlRoot = type.GetCustomAttribute<XmlRootAttribute>();
if (xmlRoot != null && !string.IsNullOrEmpty(xmlRoot.ElementName))
return xmlRoot.ElementName;
var xmlType = type.GetCustomAttribute<XmlTypeAttribute>();
if (xmlType != null && !string.IsNullOrEmpty(xmlType.TypeName))
return xmlType.TypeName;
return type.Name;
}
}
}
您可能希望将自己的类型名称映射方案替换为您自己的;它只是一个原型。
然后使用它:
[XmlRoot("A", Namespace="ATestNameSpace")]
public class ClassA
{
[XmlText]
public string Value { get; set; }
}
public class MyObject
{
public string ObjectValue1 { get; set; }
public string ObjectValue2 { get; set; }
}
public class TestClass
{
public static void Test()
{
var root1 = RequestRootHelper.Create(new ClassA { Value = "Some data" });
var root2 = RequestRootHelper.Create(new List<MyObject> { new MyObject { ObjectValue1 = "Object Value 1-1", ObjectValue2 = "Object Value 2-1" }, new MyObject { ObjectValue1 = "Object Value 1-2", ObjectValue2 = "Object Value 2-2" } });
var serializer = new RequestRootXmlSerializer(new[] { typeof(ClassA), typeof(List<ClassA>), typeof(MyObject), typeof(List<MyObject>) });
TestRootSerialization(root1, serializer);
TestRootSerialization(root2, serializer);
}
private static void TestRootSerialization<T>(RequestRoot<T> root, RequestRootXmlSerializer serializer)
{
var xml1 = serializer.SerializeToString(root);
Debug.WriteLine(xml1);
var root11 = serializer.DeserializeFromString(xml1);
Debug.Assert(root.GetType() == root11.GetType()); // NO ASSERT
var xml11 = serializer.SerializeToString(root11);
Debug.WriteLine(xml11);
Debug.Assert(xml1 == xml11); // NO ASSERT
}
}
这为ClassA
生成以下XML输出:
<RequestRoot>
<Action Type="A">
<A xmlns="ATestNameSpace">Some data</A>
</Action>
</RequestRoot>
对于List<MyObject>
:
<RequestRoot>
<Action Type="ArrayOfMyObject">
<ArrayOfMyObject>
<MyObject>
<ObjectValue1>Object Value 1-1</ObjectValue1>
<ObjectValue2>Object Value 2-1</ObjectValue2>
</MyObject>
<MyObject>
<ObjectValue1>Object Value 1-2</ObjectValue1>
<ObjectValue2>Object Value 2-2</ObjectValue2>
</MyObject>
</ArrayOfMyObject>
</Action>
</RequestRoot>