Gdy programista zdecyduje się na wdrożenie IXmlSerializable
, jakie są zasady i najlepsze praktyki dotyczące jego wdrożenia? Słyszałem, że GetSchema()
powinien powrócić null
i ReadXml
przejść do następnego elementu przed powrotem. Czy to prawda? A co z WriteXml
- czy powinien napisać element główny dla obiektu, czy zakłada się, że root jest już zapisany? Jak należy traktować i pisać przedmioty dziecięce?
Oto próbka tego, co mam teraz. Zaktualizuję go, gdy uzyskam dobre odpowiedzi.
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());
}
}
Odpowiedni przykładowy 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>
c#
xml
xml-serialization
Greg
źródło
źródło
Odpowiedzi:
Tak, GetSchema () powinna zwrócić wartość null .
Zarówno dla odczytu, jak i zapisu element object został już zapisany, więc nie ma potrzeby dodawania elementu zewnętrznego podczas zapisu. Na przykład możesz po prostu zacząć czytać / zapisywać atrybuty w obu.
Do napisania :
I do przeczytania :
Zgadzam się, że jest to trochę niejasne, ale sprowadza się to do stwierdzenia: „Twoja praca polega na
Read()
tagu elementu końcowego opakowania”.źródło
Napisałem jeden artykuł na ten temat z przykładami, ponieważ dokumentacja MSDN jest obecnie dość niejasna, a przykłady, które można znaleźć w Internecie, są w większości przypadków nieprawidłowo zaimplementowane.
Pułapki dotyczą obsługi lokalizacji i pustych elementów poza tym, o czym wspomniał już Marc Gravell.
http://www.codeproject.com/KB/XML/ImplementIXmlSerializable.aspx
źródło
Tak, to całe pole minowe, prawda? Odpowiedź Marca Gravella prawie to pokrywa, ale chciałbym dodać, że w projekcie, nad którym pracowałem, okazało się, że ręczne pisanie zewnętrznego elementu XML jest dość niewygodne. Spowodowało to również niespójne nazwy elementów XML dla obiektów tego samego typu.
Naszym rozwiązaniem było zdefiniowanie własnego
IXmlSerializable
interfejsu, wywodzącego się z systemowego, do którego dodano metodę o nazwieWriteOuterXml()
. Jak można się domyślić, metoda ta po prostu zapisuje element zewnętrzny, następnie wywołujeWriteXml()
, a następnie zapisuje koniec elementu. Oczywiście systemowy serializator XML nie wywoła tej metody, więc był on przydatny tylko wtedy, gdy wykonaliśmy własną serializację, więc może to być pomocne lub nie w twoim przypadku. Podobnie dodaliśmyReadContentXml()
metodę, która nie czytała zewnętrznego elementu, tylko jego zawartość.źródło
Jeśli masz już reprezentację klasy XmlDocument lub wolisz sposób pracy ze strukturami XML w XmlDocument, szybkim i brudnym sposobem implementacji IXmlSerializable jest po prostu przekazanie tego xmldoc do różnych funkcji.
OSTRZEŻENIE: XmlDocument (i / lub XDocument) jest o rząd wielkości wolniejszy niż xmlreader / writer, więc jeśli wydajność jest absolutnym wymogiem, to rozwiązanie nie jest dla Ciebie!
źródło
Implementacja interfejsu jest opisana w innych odpowiedziach, ale chciałem dorzucić moje 2 centy za element główny.
W przeszłości nauczyłem się, że wolę umieszczać element główny jako metadane. Ma to kilka zalet:
Poniżej znajduje się przykład słownika z możliwością serializacji, w którym element główny słownika jest zdefiniowany w następujący sposób:
źródło