programing

IXml Serializable을 구현하는 적절한 방법은 무엇입니까?

bestprogram 2023. 10. 19. 22:38

IXml Serializable을 구현하는 적절한 방법은 무엇입니까?

프로그래머가 구현하기로 결정하면,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을 반환해야 합니다.

IXml 직렬화 가능.GetSchema 메서드 이 메서드는 예약되어 있으므로 사용하면 안 됩니다.IXmlSerializable 인터페이스를 구현할 때 이 메서드에서 null 참조(Visual Basic의 Nothing)를 반환하고 대신 사용자 지정 스키마를 지정해야 하는 경우 XmlSchemaProviderAttribute를 클래스에 적용합니다.

읽기 및 쓰기 모두 개체 요소가 이미 작성되었으므로 쓰기에서 외부 요소를 추가할 필요가 없습니다.예를 들어, 두 개의 속성을 읽기/쓰기만 시작하면 됩니다.

쓰기의 경우:

제공하는 WriteXml 구현은 개체의 XML 표현을 기록해야 합니다.프레임워크는 래퍼 요소를 작성하고 XML 작성기를 시작 후에 배치합니다.구현에서 하위 요소를 포함한 내용을 작성할 수 있습니다.그러면 프레임워크가 래퍼 요소를 닫습니다.

참고:

ReadXml 메서드는 WriteXml 메서드에 의해 작성된 정보를 사용하여 개체를 재구성해야 합니다.

이 메서드를 호출하면 유형에 대한 정보를 래핑하는 요소의 시작 부분에 판독기가 배치됩니다.즉, 직렬화된 개체의 시작을 나타내는 시작 태그 바로 앞에 있습니다.이 메서드가 돌아오면 모든 내용을 포함하여 전체 요소를 처음부터 끝까지 읽었을 것입니다.WriteXml 메서드와 달리 프레임워크는 래퍼 요소를 자동으로 처리하지 않습니다.구현을 수행해야 합니다.이러한 위치 지정 규칙을 준수하지 않으면 코드에서 예기치 않은 런타임 예외가 발생하거나 데이터가 손상될 수 있습니다.

그것이 조금 불분명하다는 것에 동의하겠지만, 결국 "당신이 해야 할 일입니다.Read()포장지의 끝 element 태그".

MSDN 문서화가 상당히 불분명하고 웹에서 찾을 수 있는 예제들이 대부분 잘못 구현되어 있기 때문에 샘플로 주제에 대한 글을 하나 썼습니다.

함정은 마크 그레이벨이 이미 언급한 것 외에 장소와 공허한 요소들을 다루는 것입니다.

http://www.codeproject.com/KB/XML/ImplementIXmlSerializable.aspx

네, 전체가 약간 지뢰밭이죠?Marc Gravell의 답변에서는 이를 거의 다루고 있지만, 제가 작업한 프로젝트에서 외부 XML 요소를 수동으로 작성해야 하는 것이 상당히 어색하다는 것을 추가하고 싶습니다.또한 동일한 유형의 개체에 대해 일치하지 않는 XML 요소 이름이 생성되었습니다.

우리의 해결책은 우리의 것을 정의하는 것이었습니다.IXmlSerializableinterface, 시스템 1에서 파생된, 라고 불리는 메소드를 추가했습니다.WriteOuterXml(). 추측할 수 있듯이, 이 방법은 단순히 외부 요소를 쓴 다음,WriteXml(), 그런 다음 요소의 끝을 씁니다.물론 시스템 XML 직렬화기에서는 이 방법을 사용하지 않으므로 자체 직렬화를 수행할 때만 유용하므로 사용자의 경우 도움이 될 수도 있고 그렇지 않을 수도 있습니다.마찬가지로, 우리는 A를 추가했습니다.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 개체가 있는 경우에도 serialize할 수 있습니다.
  • 코드 가독성의 관점에서 보면 말이 됩니다.

다음은 사전 루트 요소가 그런 방식으로 정의되는 직렬화 가능한 사전의 예입니다.

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)
    {
    }

}

언급URL : https://stackoverflow.com/questions/279534/proper-way-to-implement-ixmlserializable