แก้ไขปัญหา!
ตกลงดังนั้นในที่สุดฉันก็มี (เป็นที่ยอมรับมีจำนวนมากของความช่วยเหลือจากที่นี่ !)
สรุป:
เป้าหมาย:
- ฉันไม่ต้องการลงเส้นทางXmlIncludeเนื่องจากปวดหัวในการบำรุงรักษา
- เมื่อพบวิธีแก้ปัญหาแล้วฉันต้องการให้นำไปใช้ในแอปพลิเคชันอื่นได้อย่างรวดเร็ว
- อาจใช้คอลเล็กชันประเภทบทคัดย่อรวมทั้งคุณสมบัตินามธรรมแต่ละรายการ
- ฉันไม่อยากจะกังวลกับการต้องทำสิ่ง "พิเศษ" ในชั้นเรียนที่เป็นรูปธรรม
ระบุประเด็น / ประเด็นที่ควรทราบ:
- XmlSerializerให้การสะท้อนที่ค่อนข้างเท่ แต่มีข้อ จำกัดมากเมื่อพูดถึงประเภทนามธรรม (กล่าวคือจะใช้ได้เฉพาะกับอินสแตนซ์ของประเภทนามธรรมเท่านั้นไม่ใช่คลาสย่อย)
- ตัวตกแต่งแอตทริบิวต์ Xml กำหนดวิธีที่ XmlSerializer ปฏิบัติต่อคุณสมบัติที่พบ นอกจากนี้ยังสามารถระบุประเภททางกายภาพได้ แต่จะสร้างการมีเพศสัมพันธ์ที่แน่นหนาระหว่างคลาสและซีเรียลไลเซอร์ (ไม่ดี)
- เราสามารถใช้ XmlSerializer ของเราเองโดยการสร้างคลาสที่ดำเนินIXmlSerializable
การแก้ไขปัญหา
ฉันสร้างคลาสทั่วไปซึ่งคุณระบุประเภททั่วไปเป็นประเภทนามธรรมที่คุณจะใช้งาน สิ่งนี้ทำให้คลาสสามารถ "แปล" ระหว่างชนิดนามธรรมและประเภทคอนกรีตได้เนื่องจากเราสามารถฮาร์ดโค้ดการหล่อ (กล่าวคือเราสามารถรับข้อมูลได้มากกว่าที่ XmlSerializer สามารถทำได้)
จากนั้นฉันก็ใช้อินเทอร์เฟซIXmlSerializableซึ่งค่อนข้างตรงไปตรงมา แต่เมื่อทำให้เป็นอนุกรมเราจำเป็นต้องตรวจสอบให้แน่ใจว่าเราเขียนประเภทของคลาสคอนกรีตลงใน XML เพื่อให้เราสามารถส่งกลับได้เมื่อทำการยกเลิกการทำให้เป็นอนุกรม สิ่งสำคัญคือต้องทราบว่าต้องมีคุณสมบัติครบถ้วนเนื่องจากส่วนประกอบที่ทั้งสองคลาสอยู่ในนั้นมีแนวโน้มที่จะแตกต่างกัน แน่นอนว่ามีการตรวจสอบประเภทเล็ก ๆ น้อย ๆ และสิ่งที่ต้องเกิดขึ้นที่นี่
เนื่องจาก XmlSerializer ไม่สามารถแคสต์ได้เราจึงจำเป็นต้องระบุรหัสเพื่อทำเช่นนั้นดังนั้นตัวดำเนินการโดยนัยจึงทำงานมากเกินไป (ฉันไม่เคยรู้มาก่อนเลยว่าคุณสามารถทำได้!)
รหัสสำหรับ AbstractXmlSerializer คือ:
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
namespace Utility.Xml
{
public class AbstractXmlSerializer<AbstractType> : IXmlSerializable
{
public static implicit operator AbstractType(AbstractXmlSerializer<AbstractType> o)
{
return o.Data;
}
public static implicit operator AbstractXmlSerializer<AbstractType>(AbstractType o)
{
return o == null ? null : new AbstractXmlSerializer<AbstractType>(o);
}
private AbstractType _data;
public AbstractType Data
{
get { return _data; }
set { _data = value; }
}
public AbstractXmlSerializer()
{
}
public AbstractXmlSerializer(AbstractType data)
{
_data = data;
}
#region IXmlSerializable Members
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
string typeAttrib = reader.GetAttribute("type");
if (typeAttrib == null)
throw new ArgumentNullException("Unable to Read Xml Data for Abstract Type '" + typeof(AbstractType).Name +
"' because no 'type' attribute was specified in the XML.");
Type type = Type.GetType(typeAttrib);
if (type == null)
throw new InvalidCastException("Unable to Read Xml Data for Abstract Type '" + typeof(AbstractType).Name +
"' because the type specified in the XML was not found.");
if (!type.IsSubclassOf(typeof(AbstractType)))
throw new InvalidCastException("Unable to Read Xml Data for Abstract Type '" + typeof(AbstractType).Name +
"' because the Type specified in the XML differs ('" + type.Name + "').");
reader.ReadStartElement();
this.Data = (AbstractType)new
XmlSerializer(type).Deserialize(reader);
reader.ReadEndElement();
}
public void WriteXml(System.Xml.XmlWriter writer)
{
Type type = _data.GetType();
writer.WriteAttributeString("type", type.AssemblyQualifiedName);
new XmlSerializer(type).Serialize(writer, _data);
}
#endregion
}
}
ดังนั้นจากตรงนั้นเราจะบอก XmlSerializer ให้ทำงานกับ serializer ของเราได้อย่างไรแทนที่จะเป็นค่าเริ่มต้น เราต้องส่งผ่านประเภทของเราภายในคุณสมบัติประเภทแอตทริบิวต์ Xml ตัวอย่างเช่น:
[XmlRoot("ClassWithAbstractCollection")]
public class ClassWithAbstractCollection
{
private List<AbstractType> _list;
[XmlArray("ListItems")]
[XmlArrayItem("ListItem", Type = typeof(AbstractXmlSerializer<AbstractType>))]
public List<AbstractType> List
{
get { return _list; }
set { _list = value; }
}
private AbstractType _prop;
[XmlElement("MyProperty", Type=typeof(AbstractXmlSerializer<AbstractType>))]
public AbstractType MyProperty
{
get { return _prop; }
set { _prop = value; }
}
public ClassWithAbstractCollection()
{
_list = new List<AbstractType>();
}
}
ที่นี่คุณจะเห็นว่าเรามีคอลเลกชันและคุณสมบัติเดียวที่ถูกเปิดเผยและสิ่งที่เราต้องทำคือเพิ่มพารามิเตอร์type named ในการประกาศ Xml ง่าย ๆ ! : ง
หมายเหตุ: หากคุณใช้รหัสนี้ฉันขอขอบคุณอย่างยิ่งกับการตะโกนออกมา นอกจากนี้ยังช่วยผลักดันให้ผู้คนเข้าสู่ชุมชนมากขึ้น :)
ตอนนี้ แต่ไม่แน่ใจว่าจะทำอย่างไรกับคำตอบที่นี่เนื่องจากพวกเขาทั้งหมดมีโปรและข้อเสีย ฉันจะปรับปรุงสิ่งที่ฉันรู้สึกว่ามีประโยชน์ (ไม่มีความผิดต่อผู้ที่ไม่ได้เป็น) และปิดสิ่งนี้เมื่อฉันมีตัวแทน :)
ปัญหาที่น่าสนใจและสนุกกับการแก้ปัญหา! :)