프로그래머가 구현하기로 결정하면이를 구현하기위한 IXmlSerializable
규칙과 모범 사례는 무엇입니까? 저는 확인했어요 GetSchema()
반환해야 null
하고 ReadXml
반환하기 전에 다음 요소로 이동해야한다는걸. 그런데 이것이 사실입니까? 그리고 어떻게 WriteXml
객체의 루트 요소를 작성해야합니까, 아니면 루트가 이미 작성되었다고 가정합니까? 자식 개체는 어떻게 취급하고 작성해야합니까?
여기 내가 가진 것의 샘플이 있습니다. 좋은 답변을 얻으면 업데이트하겠습니다.
public class MyCalendar : IXmlSerializable
{
private string _name;
private bool _enabled;
private Color _color;
private List<MyEvent> _events = new List<MyEvent>();
public XmlSchema GetSchema() { return null; }
public void ReadXml(XmlReader reader)
{
if (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "MyCalendar")
{
_name = reader["Name"];
_enabled = Boolean.Parse(reader["Enabled"]);
_color = Color.FromArgb(Int32.Parse(reader["Color"]));
if (reader.ReadToDescendant("MyEvent"))
{
while (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "MyEvent")
{
MyEvent evt = new MyEvent();
evt.ReadXml(reader);
_events.Add(evt);
}
}
reader.Read();
}
}
public void WriteXml(XmlWriter writer)
{
writer.WriteAttributeString("Name", _name);
writer.WriteAttributeString("Enabled", _enabled.ToString());
writer.WriteAttributeString("Color", _color.ToArgb().ToString());
foreach (MyEvent evt in _events)
{
writer.WriteStartElement("MyEvent");
evt.WriteXml(writer);
writer.WriteEndElement();
}
}
}
public class MyEvent : IXmlSerializable
{
private string _title;
private DateTime _start;
private DateTime _stop;
public XmlSchema GetSchema() { return null; }
public void ReadXml(XmlReader reader)
{
if (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "MyEvent")
{
_title = reader["Title"];
_start = DateTime.FromBinary(Int64.Parse(reader["Start"]));
_stop = DateTime.FromBinary(Int64.Parse(reader["Stop"]));
reader.Read();
}
}
public void WriteXml(XmlWriter writer)
{
writer.WriteAttributeString("Title", _title);
writer.WriteAttributeString("Start", _start.ToBinary().ToString());
writer.WriteAttributeString("Stop", _stop.ToBinary().ToString());
}
}
해당 샘플 XML
<MyCalendar Name="Master Plan" Enabled="True" Color="-14069085">
<MyEvent Title="Write Code" Start="-8589241828854775808" Stop="-8589241756854775808" />
<MyEvent Title="???" Start="-8589241828854775808" Stop="-8589241756854775808" />
<MyEvent Title="Profit!" Start="-8589247048854775808" Stop="-8589246976854775808" />
</MyCalendar>
답변
예, GetSchema () 는 null을 반환해야합니다 .
IXmlSerializable.GetSchema 메서드이 메서드는 예약되어 있으므로 사용해서는 안됩니다. IXmlSerializable 인터페이스를 구현할 때이 메소드에서 널 참조 (Visual Basic의 경우 Nothing)를 리턴해야하며 대신 사용자 정의 스키마를 지정해야하는 경우 XmlSchemaProviderAttribute를 클래스에 적용하십시오.
읽기 및 쓰기 모두에 대해 오브젝트 요소가 이미 작성되었으므로 외부 요소를 쓰기에 추가 할 필요가 없습니다. 예를 들어 두 속성에서 읽기 / 쓰기 속성을 시작할 수 있습니다.
쓰기를 위해 :
제공하는 WriteXml 구현은 객체의 XML 표현을 작성해야합니다. 프레임 워크는 랩퍼 요소를 작성하고 시작 후 XML 작성기를 배치합니다. 구현시 자식 요소를 포함하여 해당 내용을 작성할 수 있습니다. 그런 다음 프레임 워크가 랩퍼 요소를 닫습니다.
그리고 읽기를 위해 :
ReadXml 메서드는 WriteXml 메서드로 작성된 정보를 사용하여 개체를 재구성해야합니다.
이 메소드가 호출되면 리더는 사용자 유형의 정보를 랩핑하는 요소의 시작 부분에 위치합니다. 즉, 직렬화 된 객체의 시작을 나타내는 시작 태그 바로 앞에 있습니다. 이 메소드가 리턴되면 모든 내용을 포함하여 처음부터 끝까지 전체 요소를 읽었어야합니다. WriteXml 메서드와 달리 프레임 워크는 래퍼 요소를 자동으로 처리하지 않습니다. 구현해야합니다. 이러한 위치 지정 규칙을 준수하지 않으면 코드에서 예기치 않은 런타임 예외가 발생하거나 데이터가 손상 될 수 있습니다.
나는 그것이 다소 불분명하다는 것에 동의 할 것이지만, 그것은 ” Read()
래퍼의 끝 요소 태그에 대한 당신의 임무”로 귀결됩니다 .
답변
MSDN 문서가 이제 명확하지 않고 웹에서 찾을 수있는 예제가 대부분 잘못 구현되어 샘플로 주제에 대한 기사를 썼습니다.
함정은 Marc Gravell이 이미 언급 한 것 외에도 로케일과 빈 요소를 처리합니다.
http://www.codeproject.com/KB/XML/ImplementIXmlSerializable.aspx
답변
예, 모든 것이 약간의 지뢰밭입니다. Marc Gravell 의 답변은 거의 대부분을 다루고 있지만, 내가 작업 한 프로젝트에서 외부 XML 요소를 수동으로 작성 해야하는 것이 상당히 어색하다는 것을 추가하고 싶습니다. 또한 동일한 유형의 오브젝트에 대해 XML 요소 이름이 일치하지 않습니다.
우리의 솔루션은 IXmlSerializable
시스템에서 파생 된 자체 인터페이스 를 정의하고 라는 메소드를 추가하는 것이 었습니다 WriteOuterXml()
. 짐작할 수 있듯이이 메소드는 단순히 외부 요소 WriteXml()
를 작성한 다음을 호출 한 다음 요소의 끝을 작성합니다. 물론 시스템 XML serializer는이 메서드를 호출하지 않으므로 자체 직렬화를 수행 할 때만 유용하므로 귀하의 경우에는 도움이 될 수도 있고 그렇지 않을 수도 있습니다. 마찬가지로 ReadContentXml()
외부 요소를 읽지 않고 내용 만 읽는 메소드를 추가했습니다 .
답변
클래스의 XmlDocument 표현이 이미 있거나 XML 구조를 사용하는 XmlDocument 방식을 선호하는 경우 IXmlSerializable을 구현하는 빠르고 더러운 방법은이 xmldoc을 다양한 함수에 전달하는 것입니다.
경고 : XmlDocument (및 / 또는 XDocument)는 xmlreader / writer보다 훨씬 느리므로 성능이 절대적인 요구 사항이라면이 솔루션은 적합하지 않습니다!
class ExampleBaseClass : IXmlSerializable {
public XmlDocument xmlDocument { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
xmlDocument.Load(reader);
}
public void WriteXml(XmlWriter writer)
{
xmlDocument.WriteTo(writer);
}
}
답변
인터페이스 구현은 다른 답변으로 덮여 있지만 루트 요소에 대해 2 센트를 던지기를 원했습니다.
과거에는 루트 요소를 메타 데이터로 사용하는 것을 선호하는 법을 배웠습니다. 몇 가지 장점이 있습니다.
- null 개체가 있으면 여전히 직렬화 할 수 있습니다
- 코드 가독성 관점에서 말이됩니다.
아래는 사전 루트 요소가 그런 식으로 정의 된 직렬화 가능한 사전의 예입니다.
using System.Collections.Generic;
[System.Xml.Serialization.XmlRoot("dictionary")]
public partial class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, System.Xml.Serialization.IXmlSerializable
{
public virtual System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public virtual void ReadXml(System.Xml.XmlReader reader)
{
var keySerializer = new System.Xml.Serialization.XmlSerializer(typeof(TKey));
var valueSerializer = new System.Xml.Serialization.XmlSerializer(typeof(TValue));
bool wasEmpty = reader.IsEmptyElement;
reader.Read();
if (wasEmpty)
return;
while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
{
reader.ReadStartElement("item");
reader.ReadStartElement("key");
TKey key = (TKey)keySerializer.Deserialize(reader);
reader.ReadEndElement();
reader.ReadStartElement("value");
TValue value = (TValue)valueSerializer.Deserialize(reader);
reader.ReadEndElement();
Add(key, value);
reader.ReadEndElement();
reader.MoveToContent();
}
reader.ReadEndElement();
}
public virtual void WriteXml(System.Xml.XmlWriter writer)
{
var keySerializer = new System.Xml.Serialization.XmlSerializer(typeof(TKey));
var valueSerializer = new System.Xml.Serialization.XmlSerializer(typeof(TValue));
foreach (TKey key in Keys)
{
writer.WriteStartElement("item");
writer.WriteStartElement("key");
keySerializer.Serialize(writer, key);
writer.WriteEndElement();
writer.WriteStartElement("value");
var value = this[key];
valueSerializer.Serialize(writer, value);
writer.WriteEndElement();
writer.WriteEndElement();
}
}
public SerializableDictionary() : base()
{
}
public SerializableDictionary(IDictionary<TKey, TValue> dictionary) : base(dictionary)
{
}
public SerializableDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer) : base(dictionary, comparer)
{
}
public SerializableDictionary(IEqualityComparer<TKey> comparer) : base(comparer)
{
}
public SerializableDictionary(int capacity) : base(capacity)
{
}
public SerializableDictionary(int capacity, IEqualityComparer<TKey> comparer) : base(capacity, comparer)
{
}
}