Xml 序列化 ICollection
Xml Serializing ICollection
我们正在开发一个具有适度复杂模型的项目,其核心 class 具有许多属性,这些属性是其他相关 class 的列表。我们在后端使用 EF 6(代码优先),并且按照有关如何为该环境构建 classes 的指导,我们大量利用了 ICollection<T>
:
public class Product
{
// a few normal properties
public string Name { get; set; }
public string Description { get; set; }
public string Code { get; set; }
// a large list of ICollection<T> properties
public virtual ICollection<Rule> Rules { get; set; }
public virtual ICollection<Update> Updates { get; set; }
public virtual ICollection<Reference> References { get; set; }
// more ICollections from here...
}
ICollection<T>
引用的 class 中有几个在它们自己内部有集合(例如 Rule
有 ICollection<Condition> Conditions
),创建了一个相当深和复杂的树。
现在我们的模型几乎已经完成,我们已经开始开发业务逻辑和 UI (ASP.NET MVC)。系统所需的功能之一是 serialize/deserialize XML 与另一个系统交互。但是我们发现 XmlSerialization.Serialize()
不起作用,抱怨它不能序列化使用接口的东西。
当我们开始整个项目时,知道序列化将成为一个因素,我们构建了一组 XSD 并使用 xsd.exe 生成 classes 给我们,我们已经对其进行了大量修改。但是,该实用程序放入了一堆本应有助于序列化的标记,我们将其全部保留:
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlRootAttribute("product", Namespace = "", IsNullable = false)]
public class Product
{
....
}
不幸的是,我们遇到了无法序列化的问题,即使我们(理论上)告诉编译器,是的,这个 class 确实是可序列化的。每个子 classes 都有一组相似的标签。因此,进行了更多研究...
提供的解决方案 here 似乎带有很多包袱。关于这个问题的其他 SO 答案似乎只是描述了 ICollection<T>
和 Collection<T>
之间的区别,而没有提供关于 为什么 你想要避免具体 class。我正在考虑将所有内容迁移到使用 Collection<T>
而不是 ICollection<T>
。如果我这样做,我会不会给自己带来麻烦?
您尝试过 IXmlSerializable 吗?从这个界面你可以控制你写和读的内容。我不确定这是否可以解决您的问题。
public CSortedList<string, CBasicStockData> Stocks { get; set; }
public CSortedList<string, CIndustrySectorExchangeInfo> Exchanges { get; set; }
public CSortedList<string, CIndustrySectorExchangeInfo> Industries { get; set; }
public CSortedList<string, CIndustrySectorExchangeInfo> Sectors { get; set; }
public void WriteXml(XmlWriter writer)
{
try
{
///////////////////////////////////////////////////////////
writer.WriteStartElement("Stocks");
writer.WriteAttributeString("num", Stocks.Count.ToString());
foreach (var kv in Stocks)
{
writer.WriteStartElement("item");
foreach (var p in kv.Value.WritableProperties)
{
var value = p.GetValue(kv.Value);
var str = (value == null ? string.Empty : value.ToString());
writer.WriteAttributeString(p.Name, str);
}
writer.WriteEndElement();
}
writer.WriteEndElement();
///////////////////////////////////////////////////////////
foreach (var propInfo in this.WritableProperties)
{
if (propInfo.Name == "Stocks") continue;
dynamic prop = propInfo.GetValue(this);
writer.WriteStartElement(propInfo.Name);
writer.WriteAttributeString("num", prop.Count.ToString());
foreach (var kv in prop)
{
writer.WriteStartElement("item");
foreach (var p in kv.Value.WritableProperties)
{
var value = p.GetValue(kv.Value);
var str = (value == null ? string.Empty : value.ToString());
writer.WriteAttributeString(p.Name, str);
}
writer.WriteEndElement();
}
writer.WriteEndElement();
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
throw ex;
}
}
public void ReadXml(XmlReader reader)
{
var propName = string.Empty;
while (reader.Read() &&
!(reader.NodeType == XmlNodeType.EndElement && reader.LocalName == this.GetType().Name))
{
if (reader.Name != "item")
{
propName = reader.Name;
continue;
}
switch (propName)
{
case "Stocks":
{
var obj = new CBasicStockData();
foreach (var propInfo in obj.WritableProperties)
{
var value = reader.GetAttribute(propInfo.Name);
if (value == null) //we may add new property to class after the file is created
continue;
propInfo.SetValue(obj, Convert.ChangeType(value, propInfo.PropertyType));
}
this.Stocks.Add(obj.Symbol, obj);
break;
}
case "Exchanges":
case "Industries":
case "Sectors":
{
var obj = new CIndustrySectorExchangeInfo();
foreach (var p in obj.WritableProperties)
{
var value = reader.GetAttribute(p.Name);
if (value == null)
continue;
p.SetValue(obj, Convert.ChangeType(value, p.PropertyType));
}
var propInfo = this.WritableProperties.Find(x => x.Name == propName);
dynamic prop = propInfo.GetValue(this);
prop.Add(obj.Name, obj);
break;
}
default:
break;
}
}
}
public static string XML_Serialize<T>(string filename, T myObject) where T : IXmlSerializable
{
XmlSerializer xmlSerializer = new XmlSerializer(myObject.GetType());
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
using (StringWriter stringWriter = new StringWriter())
using (XmlWriter writer = XmlWriter.Create(stringWriter, settings)) {
xmlSerializer.Serialize(writer, myObject);
var xml = stringWriter.ToString(); // Your xml
File.WriteAllText(filename, xml);
return xml;
}
}
public static void XML_DeSerialize<T>(string filename, out T myObject) where T : IXmlSerializable
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
using (StreamReader reader = new StreamReader(filename)) {
myObject = (T)xmlSerializer.Deserialize(reader);
}
}
我们正在开发一个具有适度复杂模型的项目,其核心 class 具有许多属性,这些属性是其他相关 class 的列表。我们在后端使用 EF 6(代码优先),并且按照有关如何为该环境构建 classes 的指导,我们大量利用了 ICollection<T>
:
public class Product
{
// a few normal properties
public string Name { get; set; }
public string Description { get; set; }
public string Code { get; set; }
// a large list of ICollection<T> properties
public virtual ICollection<Rule> Rules { get; set; }
public virtual ICollection<Update> Updates { get; set; }
public virtual ICollection<Reference> References { get; set; }
// more ICollections from here...
}
ICollection<T>
引用的 class 中有几个在它们自己内部有集合(例如 Rule
有 ICollection<Condition> Conditions
),创建了一个相当深和复杂的树。
现在我们的模型几乎已经完成,我们已经开始开发业务逻辑和 UI (ASP.NET MVC)。系统所需的功能之一是 serialize/deserialize XML 与另一个系统交互。但是我们发现 XmlSerialization.Serialize()
不起作用,抱怨它不能序列化使用接口的东西。
当我们开始整个项目时,知道序列化将成为一个因素,我们构建了一组 XSD 并使用 xsd.exe 生成 classes 给我们,我们已经对其进行了大量修改。但是,该实用程序放入了一堆本应有助于序列化的标记,我们将其全部保留:
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlRootAttribute("product", Namespace = "", IsNullable = false)]
public class Product
{
....
}
不幸的是,我们遇到了无法序列化的问题,即使我们(理论上)告诉编译器,是的,这个 class 确实是可序列化的。每个子 classes 都有一组相似的标签。因此,进行了更多研究...
提供的解决方案 here 似乎带有很多包袱。关于这个问题的其他 SO 答案似乎只是描述了 ICollection<T>
和 Collection<T>
之间的区别,而没有提供关于 为什么 你想要避免具体 class。我正在考虑将所有内容迁移到使用 Collection<T>
而不是 ICollection<T>
。如果我这样做,我会不会给自己带来麻烦?
您尝试过 IXmlSerializable 吗?从这个界面你可以控制你写和读的内容。我不确定这是否可以解决您的问题。
public CSortedList<string, CBasicStockData> Stocks { get; set; }
public CSortedList<string, CIndustrySectorExchangeInfo> Exchanges { get; set; }
public CSortedList<string, CIndustrySectorExchangeInfo> Industries { get; set; }
public CSortedList<string, CIndustrySectorExchangeInfo> Sectors { get; set; }
public void WriteXml(XmlWriter writer)
{
try
{
///////////////////////////////////////////////////////////
writer.WriteStartElement("Stocks");
writer.WriteAttributeString("num", Stocks.Count.ToString());
foreach (var kv in Stocks)
{
writer.WriteStartElement("item");
foreach (var p in kv.Value.WritableProperties)
{
var value = p.GetValue(kv.Value);
var str = (value == null ? string.Empty : value.ToString());
writer.WriteAttributeString(p.Name, str);
}
writer.WriteEndElement();
}
writer.WriteEndElement();
///////////////////////////////////////////////////////////
foreach (var propInfo in this.WritableProperties)
{
if (propInfo.Name == "Stocks") continue;
dynamic prop = propInfo.GetValue(this);
writer.WriteStartElement(propInfo.Name);
writer.WriteAttributeString("num", prop.Count.ToString());
foreach (var kv in prop)
{
writer.WriteStartElement("item");
foreach (var p in kv.Value.WritableProperties)
{
var value = p.GetValue(kv.Value);
var str = (value == null ? string.Empty : value.ToString());
writer.WriteAttributeString(p.Name, str);
}
writer.WriteEndElement();
}
writer.WriteEndElement();
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
throw ex;
}
}
public void ReadXml(XmlReader reader)
{
var propName = string.Empty;
while (reader.Read() &&
!(reader.NodeType == XmlNodeType.EndElement && reader.LocalName == this.GetType().Name))
{
if (reader.Name != "item")
{
propName = reader.Name;
continue;
}
switch (propName)
{
case "Stocks":
{
var obj = new CBasicStockData();
foreach (var propInfo in obj.WritableProperties)
{
var value = reader.GetAttribute(propInfo.Name);
if (value == null) //we may add new property to class after the file is created
continue;
propInfo.SetValue(obj, Convert.ChangeType(value, propInfo.PropertyType));
}
this.Stocks.Add(obj.Symbol, obj);
break;
}
case "Exchanges":
case "Industries":
case "Sectors":
{
var obj = new CIndustrySectorExchangeInfo();
foreach (var p in obj.WritableProperties)
{
var value = reader.GetAttribute(p.Name);
if (value == null)
continue;
p.SetValue(obj, Convert.ChangeType(value, p.PropertyType));
}
var propInfo = this.WritableProperties.Find(x => x.Name == propName);
dynamic prop = propInfo.GetValue(this);
prop.Add(obj.Name, obj);
break;
}
default:
break;
}
}
}
public static string XML_Serialize<T>(string filename, T myObject) where T : IXmlSerializable
{
XmlSerializer xmlSerializer = new XmlSerializer(myObject.GetType());
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
using (StringWriter stringWriter = new StringWriter())
using (XmlWriter writer = XmlWriter.Create(stringWriter, settings)) {
xmlSerializer.Serialize(writer, myObject);
var xml = stringWriter.ToString(); // Your xml
File.WriteAllText(filename, xml);
return xml;
}
}
public static void XML_DeSerialize<T>(string filename, out T myObject) where T : IXmlSerializable
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
using (StreamReader reader = new StreamReader(filename)) {
myObject = (T)xmlSerializer.Deserialize(reader);
}
}