Piszę kod do serializacji XML. Z poniższą funkcją.
public static string SerializeToXml(object obj)
{
XmlSerializer serializer = new XmlSerializer(obj.GetType());
using (StringWriter writer = new StringWriter())
{
serializer.Serialize(writer, obj);
return writer.ToString();
}
}
Jeśli argument jest wystąpieniem klasy bez konstruktora bez parametrów, zgłosi wyjątek.
Nieobsługiwany wyjątek: System.InvalidOperationException: nie można serializować CSharpConsole.Foo, ponieważ nie ma konstruktora bez parametrów. at System.Xml.Serialization.TypeDesc.CheckSupported () at System.Xml.Serialization.TypeScope.GetTypeDesc (Type type, MemberInfo sourc e, Boolean directReference, Boolean throwOnError) at System.Xml.Serialization.ModelScope.GetType (TypeModelScope.GetType Boolean direct Reference) w System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping (Type type, XmlRootAttribute root, String defaultNamespace) at System.Xml.Serialization.XmlSerializer..ctor (Type type, String defaultName space) at System.Xml.Serialization. XmlSerializer..ctor (typ typu)
Dlaczego musi istnieć konstruktor bez parametrów, aby umożliwić pomyślną serializację XML?
EDYCJA: dzięki za odpowiedź cfeduke. Konstruktor bez parametrów może być prywatny lub wewnętrzny.
źródło
XmlSerializer
wymaga domyślnego konstruktora bez parametrów do deserializacji.Odpowiedzi:
Podczas deserializacji obiektu klasa odpowiedzialna za desserializację obiektu tworzy wystąpienie serializowanej klasy, a następnie przechodzi do wypełniania serializowanych pól i właściwości dopiero po uzyskaniu wystąpienia do wypełnienia.
Możesz utworzyć konstruktora
private
lubinternal
jeśli chcesz, o ile jest on bez parametrów.źródło
private
lubinternal
wszystkie właściwości, których wartości zostały serializowane, muszą miećpublic
metody ustawiające.To jest ograniczenie
XmlSerializer
. Zauważ, żeBinaryFormatter
iDataContractSerializer
nie wymagają tego - mogą stworzyć niezainicjowany obiekt z eteru i zainicjować go podczas deserializacji.Ponieważ używasz XML, możesz rozważyć użycie
DataContractSerializer
i oznaczenie swojej klasy za pomocą[DataContract]
/[DataMember
], ale zwróć uwagę, że zmienia to schemat (na przykład nie ma odpowiednika[XmlAttribute]
- wszystko staje się elementami).Aktualizacja: jeśli naprawdę chcesz wiedzieć,
BinaryFormatter
i inni używajFormatterServices.GetUninitializedObject()
do tworzenia obiektu bez wywoływania konstruktora. Prawdopodobnie niebezpieczne; Nie polecam używania go zbyt często ;-p Zobacz też uwagi na MSDN:Mam własny silnik serializacji, ale nie zamierzam go używać
FormatterServices
; Lubię wiedzieć, że konstruktor ( dowolny konstruktor) faktycznie wykonał.źródło
FormatterServices
używanie przez wiekiIXmlSerializable
, ale: co dzieje się po konstruktora i b: to jest bardzo brzydkie i trudne do uzyskania właściwej (zwłaszcza deserializacji) - zdecydowanie odradzam próby wykonania, ale: to nie będzie można korzystać z konstruktorówOdpowiedź brzmi: bez żadnego powodu.
Wbrew swojej nazwie
XmlSerializer
klasa służy nie tylko do serializacji, ale także do deserializacji. Wykonuje pewne kontrole Twojej klasy, aby upewnić się, że zadziała, a niektóre z tych testów dotyczą tylko deserializacji, ale i tak je wszystkie, ponieważ nie wie, co zamierzasz zrobić później.Sprawdzenie, którego klasa nie przejdzie, jest jednym z testów, które dotyczą tylko deserializacji. Oto co się dzieje:
Podczas deserializacji
XmlSerializer
klasa będzie musiała utworzyć wystąpienia Twojego typu.Aby utworzyć instancję typu, należy wywołać konstruktor tego typu.
Jeśli nie zadeklarowałeś konstruktora, kompilator dostarczył już domyślny konstruktor bez parametrów, ale jeśli zadeklarowałeś konstruktora, jest to jedyny dostępny konstruktor.
Tak więc, jeśli zadeklarowany konstruktor akceptuje parametry, jedynym sposobem na utworzenie instancji klasy jest wywołanie konstruktora, który akceptuje parametry.
Jednak
XmlSerializer
nie jest w stanie wywołać żadnego konstruktora z wyjątkiem konstruktora bez parametrów, ponieważ nie wie, jakie parametry przekazać do konstruktorów akceptujących parametry. Tak więc sprawdza, czy klasa ma konstruktora bez parametrów, a ponieważ go nie ma, kończy się niepowodzeniem.Tak więc, gdyby
XmlSerializer
klasa została napisana w taki sposób, że wykonuje tylko kontrole związane z serializacją, to Twoja klasa przeszłaby, ponieważ nie ma absolutnie nic w serializacji, co wymagałoby posiadania konstruktora bez parametrów.Jak inni już zauważyli, szybkim rozwiązaniem twojego problemu jest po prostu dodanie konstruktora bez parametrów. Niestety jest to również brudne rozwiązanie, ponieważ oznacza to, że nie można
readonly
inicjować żadnych członków z parametrów konstruktora.Oprócz tego
XmlSerializer
klasę można było napisać w taki sposób, aby umożliwić nawet deserializację klas bez konstruktorów bez parametrów. Wystarczyłoby skorzystać z „Wzorca projektowania metod fabrycznych” (Wikipedia) . Z wyglądu Microsoft uznał, że ten wzorzec projektowy jest zbyt zaawansowany dla programistów DotNet, których najwyraźniej nie należy niepotrzebnie mylić z takimi rzeczami. Dlatego programiści DotNet powinni według Microsoftu lepiej trzymać się konstruktorów bez parametrów.źródło
For no good reason whatsoever,
a następnie mówisz,XmlSerializer is not capable of invoking any constructor except a parameterless constructor, because it does not know what parameters to pass to constructors that accept parameters.
jeśli nie wie, jakie parametry przekazać konstruktorowi, to skąd miałby wiedzieć, jakie parametry przekazać do fabryki? Albo jakiej fabryki użyć? Nie mogę sobie wyobrazić, że to narzędzie będzie prostsze w użyciu - chcesz, aby klasa została zdeserializowana, a następnie pozwól deserializatorowi utworzyć domyślną instancję, a następnie zapełnij każde oznaczone pole. Łatwo.Przede wszystkim to, co jest napisane w dokumentacji . Myślę, że jest to jedno z twoich pól klasy, a nie główne - i jak chcesz, aby deserialiser skonstruował go z powrotem bez konstrukcji bez parametrów?
Myślę, że istnieje obejście, aby uczynić konstruktora prywatnym.
źródło