[英]Deserialize class properties to derived class instead of base abstract class
The structure here is a bit convoluted so please bear with me. 这里的结构有些复杂,请耐心等待。 I hope that there's a way to do what a want to do, but if it's not possible then feel free to tell me! 我希望有一种方法可以做想做的事情,但是如果不可能的话,请随时告诉我!
Unfortunately I've not been involved in the design process of the XML file spec from the beginning, the goalposts have been moved a dozen times and the spec can't be amended for the sake of making it easier for me to do as any spec amendments are extortionately priced (yes, even renaming XML elements!) 不幸的是,我从一开始就没有参与XML文件规范的设计过程,目标杆已经移动了十多次,并且为了使我更容易按照任何规范进行操作,无法修改规范。修正案定价过高(是的,甚至重命名XML元素!)
Anyway I digress. 反正我离题。
There are two different types of purchase order that have a slightly different file structure and slightly different processing logic and I am trying to handle them both with the same code instead of having two different projects for what is nearly identical. 有两种不同类型的采购订单,它们的文件结构和处理逻辑略有不同,我正在尝试使用相同的代码来处理它们,而不是针对几乎相同的项目使用两个不同的项目。
Both types of purchase order derive from a set of abstract classes that determine basic business logic that both PO types share (quantity, cost, PO number, etc.). 两种类型的采购订单都来自一组抽象类,这些抽象类确定两种PO类型共享的基本业务逻辑(数量,成本,PO编号等)。
Base classes 基类
abstract class PO {
[XmlIgnore]
abstract POType PurchaseOrderType {get;}
[XmlIgnore]
abstract PO_Head Header {get;set;}
[XmlIgnore]
abstract List<PO_Line> LineItems {get;set;}
}
abstract class PO_Head {
[XmlIgnore]
abstract string PONumber {get;set;}
}
abstract class PO_Line {
[XmlIgnore]
abstract string ItemCode {get;set;}
[XmlIgnore]
abstract decimal UnitCost {get;set;}
[XmlIgnore]
abstract int OrderQty {get;set;}
}
Derived classes 派生类
class POR : PO {
// POR implementations
}
class POR_Head : PO_Line {
// POR implementations
}
class POR_Line : PO_Line {
// POR implementations
}
class CPO : PO {
// CPO implementations
}
class CPO_Head : PO_Line {
// CPO implementations
}
class CPO_Line : PO_Line {
// CPO implementations
}
The base abstract class is then used in the code to process each purchase order and import them into our accounting system. 然后,在代码中使用基本抽象类来处理每个采购订单并将其导入我们的会计系统。
for (int i = pending.Transactions.Count -1; i > -1; i--) {
PO pendingOrder = (PO)pending.Transactions[i];
// Import PO type
}
The problem is that when I attempt to deserialize into each derived class as appropriate, it attempts to deserialize the Header
and LineItems
into the derived types PO_Head
and PO_Line
. 问题是,当我尝试对每个派生类进行反序列化时,它会尝试将Header
和LineItems
反序列化为派生类型PO_Head
和PO_Line
。 Is there any way I can explicitly tell the XmlSerializer
to treat Header
and LineItems
as the derived class versions - CPO_Head and CPO_Line or POR_Head and POR_Line - depending on the class that is being serialized - CPO or POR respectively. 我有什么办法可以明确地告诉XmlSerializer
将Header
和LineItems
视为派生类版本-CPO_Head和CPO_Line或POR_Head和POR_Line-分别取决于要序列化的类-CPO或POR。 Something akin to the below. 类似于以下内容。
class CPO : PO {
[XmlIgnore]
override POType PurchaseOrderType => POType.CPO;
[XmlElement("CPO_Head")]
[XmlDeserializerType(typeof(CPO_Head))]
override PO_Head Header {get;set;}
[XmlArray("CPO_Lines")]
[XmlArrayItem("CPO_Line")]
[XmlDeserializerType(typeof(CPO_Line))]
override List<PO_Line> LineItems {get;set;}
}
Kind of dug myself a hole with this one (first time using deserialization) so I'm hoping there's an easy way out that would save me having to rewrite a ton of the work I've done! 有点为自己挖了一个洞(第一次使用反序列化),所以我希望有一个简单的解决方法,省去了我重写大量工作的麻烦!
TIA TIA
EDIT - code used to deserialize/serialize as requested 编辑-用于根据要求反序列化/序列化的代码
public static void SerializeToXmlFile(object obj, FileInfo dstFile)
{
XmlSerializer xmlSerializer = new XmlSerializer(obj.GetType());
using (FileStream fs = dstFile.Open(FileMode.Create, FileAccess.Write))
{
xmlSerializer.Serialize(fs, obj);
}
}
public static object DeserializeFromXmlFile(FileInfo srcFile, Type type)
{
XmlSerializer xmlSerializer = new XmlSerializer(type);
using (FileStream fs = srcFile.Open(FileMode.Open, FileAccess.Read))
{
return xmlSerializer.Deserialize(fs);
}
}
public static void Main(string[] args)
{
// Deserialize from XML file
FileInfo srcFile = new FileInfo("path\to\file");
Type t;
if (srcFile.Name.Substring(0,3) == "CPO")
t = typeof(CPO);
else if (srcFile.Name.Substring(0,3) == "POR")
t = typeof(POR);
PO po = DeserializeFromXmlFile(file, t);
// Process the file
// ...
// Serialize back to file
FileInfo dstFile = new FileInfo("new\path\to\file");
SerializeToXmlFile(po, dstFile);
}
EDIT - fixed 编辑-固定
As per the marked correct answer I was able to resolve this by specifying the Type
in the XmlElement
and XmlArrayItem
attributes. 根据标记的正确答案,我能够通过在XmlElement
和XmlArrayItem
属性中指定Type
来解决此问题。
class POR {
[XmlElement(typeof(POR_Head), ElementName="PO_Head")]
override PO_Head Header {get;set;} = new POR_Head();
[XmlArray("PO_Lines")]
[XmlArrayItem(typeof(POR_Line), ElementName="PO_Line")]
override List<PO_Line> LineItems {get;set;} = new List<LineItems>();
}
class CPO {
[XmlElement(typeof(CPO_Head), ElementName="PO_Head")]
override PO_Head Header {get;set;} = new CPO_Head();
[XmlArray("PO_Lines")]
[XmlArrayItem(typeof(CPO_Line), ElementName="PO_Line")]
override List<PO_Line> LineItems {get;set;} = new List<LineItems>();
}
I think you are looking for the XmlArrayItemAttribute . 我认为您正在寻找XmlArrayItemAttribute 。 According to the docs: 根据文档:
You can apply the XmlArrayItemAttribute to any public read/write member that returns an array, or provides access to one. 您可以将XmlArrayItemAttribute应用于返回数组或提供对数组的访问的任何公共读/写成员。 For example, a field that returns an array of objects, a collection, an ArrayList, or any class that implements the IEnumerable interface. 例如,返回一个对象数组,一个集合,一个ArrayList或实现IEnumerable接口的任何类的字段。
The XmlArrayItemAttribute supports polymorphism--in other words, it allows the XmlSerializer to add derived objects to an array. XmlArrayItemAttribute支持多态性-换句话说,它允许XmlSerializer将派生对象添加到数组。
If I understand the example correctly, the attribute should be written with a list of the possible derived types that are allowed in the serialized enumerable, eg 如果我正确理解该示例,则该属性应写有序列化可枚举中允许的可能派生类型的列表,例如
[XmlArrayItem (typeof(PO_Line), ElementName = "PO_Line"),
XmlArrayItem (typeof(CPO_Line),ElementName = "CPO_Line")]
Since you are only passing a string, the attribute interprets that as the ElementName, not the type. 由于仅传递字符串,因此属性会将其解释为ElementName而不是类型。 The type must be passed using typeof(ClassName)
. 必须使用typeof(ClassName)
传递类型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.