문제 해결됨!
좋아, 그래서 마침내 거기에 도착했습니다 (분명히 여기 에서 많은 도움을 받았습니다 !).
요약하자면 :
목표 :
- 유지 관리 문제로 인해 XmlInclude 경로를 따르고 싶지 않았습니다 .
- 솔루션을 찾으면 다른 응용 프로그램에서 빠르게 구현할 수 있기를 원했습니다.
- 개별 추상 속성뿐만 아니라 추상 유형의 컬렉션을 사용할 수 있습니다.
- 나는 구체적인 수업에서 "특별한"일을해야하는 것을 정말로 귀찮게하고 싶지 않았습니다.
확인 된 문제 / 참고 사항 :
- XmlSerializer 는 꽤 멋진 리플렉션을 수행하지만 추상 유형에 관해서 는 매우 제한적입니다 (즉, 하위 클래스가 아닌 추상 유형 자체의 인스턴스에서만 작동합니다).
- Xml 속성 데코레이터는 XmlSerializer가 찾은 속성을 처리하는 방법을 정의합니다. 물리적 유형도 지정할 수 있지만 이로 인해 클래스와 직렬 변환기간에 긴밀한 결합 이 생성됩니다 (좋지 않음).
- IXmlSerializable 을 구현하는 클래스를 만들어 자체 XmlSerializer를 구현할 수 있습니다.
해결책
작업 할 추상 유형으로 제네릭 유형을 지정하는 제네릭 클래스를 만들었습니다. 이렇게하면 캐스팅을 하드 코딩 할 수 있으므로 (즉, 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가 기본값이 아닌 직렬 변환기와 함께 작동하도록 어떻게 지시합니까? 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>();
}
}
여기에서 컬렉션과 단일 속성이 노출되는 것을 볼 수 있으며, 우리가해야 할 일은 매개 변수라는 형식 을 Xml 선언에 쉽게 추가하는 것뿐입니다! :디
참고 :이 코드를 사용하는 경우 정말 감사합니다. 또한 더 많은 사람들을 커뮤니티로 유도하는 데 도움이 될 것입니다. :)
이제는 모두 장단점을 가지고 있기 때문에 여기에서 답변으로 무엇을 해야할지 확신 할 수 없습니다. 나는 내가 유용하다고 생각하는 것을 upmod하고 (그렇지 않은 사람들에게는 공격하지 않음) 담당자가 있으면 이것을 닫습니다 :)
재미있는 문제와 해결하기 좋은 재미! :)