XmlSerializer podający FileNotFoundException w konstruktorze

347

Aplikacja, z którą pracuję, zawiesza się, gdy próbuję serializować typy.

Oświadczenie jak

XmlSerializer lizer = new XmlSerializer(typeof(MyType));

produkuje:

System.IO.FileNotFoundException occurred
  Message="Could not load file or assembly '[Containing Assembly of MyType].XmlSerializers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified."
  Source="mscorlib"
  FileName="[Containing Assembly of MyType].XmlSerializers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
  FusionLog=""
  StackTrace:
       at System.Reflection.Assembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection)
       at System.Reflection.Assembly.nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection)

Nie definiuję żadnych specjalnych serializatorów dla mojej klasy.

Jak mogę rozwiązać ten problem?

Irwin
źródło
5
OK, więc to pytanie jest tylko moją wersją C # już zadanego pytania VB: stackoverflow.com/questions/294659/... Dzięki chłopaki.
Irwin
1
Sześć lat później odpowiedź @VladV jest najprostszym i najmniej niekorzystnym rozwiązaniem. Po prostu zmień Generate serialization assemblymenu rozwijane na „Wł.” Zamiast „Auto”.
Heliac,
@Heliac: Nie zgadzam się. Nie zawsze działa. Zobacz komentarz Benoit Blanchon do odpowiedzi Vlada. Najprostszą odpowiedzią jest dla mnie, aby nie używać String.Collection w plikach konfiguracyjnych. Zamiast tego używam: string [] items = Settings.Default.StringofNewlineDelimitedItems.Split (new [] {Environment.NewLine});
Andrew Dennison,

Odpowiedzi:

388

Wierzcie lub nie, to jest normalne zachowanie. Wyjątek jest zgłaszany, ale obsługiwany przez XmlSerializer, więc jeśli go zignorujesz, wszystko powinno być w porządku.

Uważam to za bardzo irytujące i było wiele skarg na to, jeśli trochę się rozglądasz, ale z tego, co przeczytałem, Microsoft nie planuje nic z tym zrobić.

Możesz uniknąć wyświetlania wyskakujących okien wyjątków przez cały czas podczas debugowania, jeśli wyłączysz wyjątki pierwszej szansy dla tego konkretnego wyjątku. W programie Visual Studio przejdź do opcji Debugowanie -> Wyjątki (lub naciśnij Ctrl+ Alt+ E), Wyjątki czasu wykonywania wspólnego języka -> System.IO -> System.IO.FileNotFoundException .

Informacje o innym rozwiązaniu można znaleźć w blogu C # XmlSerializer FileNotFound wyjątek (który omawia narzędzie Chrisa Sellsa XmlSerializerPreCompiler ).

Martin Sherburn
źródło
162
Jednym z możliwych sposobów pozbycia się tego problemu jest zaznaczenie opcji „Tylko mój kod” w menu Narzędzia -> Opcje -> Debugowanie -> Opcje ogólne.
Frederic
26
@Frederic: Ten komentarz jest niesamowity! Siedzę tutaj z „WTF !?” wyraz mojej twarzy, próbuję upolować ten fałszywy wyjątek, i znajduję to pytanie z odpowiedzią (to wina Microsoftu, co jeszcze jest nowego?), ale nie chciałem wyłączać obsługi wyjątków, ponieważ być może potrzebuję go do mój kod. A +!
Kumba
27
Myślę, że poniższa sugestia Hansa jest bardziej cenna - użyj innego wywołania metody, które w ogóle nie generuje tego wyjątku: XmlSerializer serializer = XmlSerializer.FromTypes (new [] {typeof (MyType)}) [0];
jasny
3
Problem polega na tym, że mój test się nie udaje, więc nie mogę po prostu „zignorować” wyjątku
Csaba Toth
16
Przepraszam, ale to okropna sugestia. FileNotFoundException jest jednym z bardziej powszechnych, z mojego doświadczenia, i wyłączenie raportowania wyjątków po prostu prosi o kłopoty pewnego dnia w przyszłości. Lepiej włączyć „Just My Code” lub umożliwić tworzenie zestawów serializacji opisanych poniżej.
Quarkly
104

Jak powiedział Martin Sherburn, jest to normalne zachowanie. Konstruktor XmlSerializer najpierw próbuje znaleźć zestaw o nazwie [YourAssembly] .XmlSerializers.dll, który powinien zawierać wygenerowaną klasę do serializacji danego typu. Ponieważ taka biblioteka DLL nie została jeszcze wygenerowana (nie są domyślnie), zgłaszany jest wyjątek FileNotFoundException. Kiedy tak się dzieje, konstruktor XmlSerializer przechwytuje ten wyjątek, a biblioteka DLL jest generowana automatycznie w czasie wykonywania przez konstruktor XmlSerializer (odbywa się to poprzez wygenerowanie plików źródłowych C # w katalogu% temp% na komputerze, a następnie kompilację przy użyciu kompilatora C #). Dodatkowe konstrukcje XmlSerializer dla tego samego typu będą po prostu korzystać z już wygenerowanej biblioteki DLL.

AKTUALIZACJA: Począwszy od .NET 4.5, XmlSerializernie wykonuje już generowania kodu ani nie wykonuje kompilacji z kompilatorem C # w celu utworzenia zestawu serializatora w czasie wykonywania, chyba że jest to wyraźnie wymagane przez ustawienie pliku konfiguracyjnego ( useLegacySerializerGeneration ). Ta zmiana usuwa zależność csc.exei poprawia wydajność uruchamiania. Źródło: Readme .NET Framework 4.5 , sekcja 1.3.8.1.

Wyjątek jest obsługiwany przez konstruktor XmlSerializer. Nie musisz nic robić sam, wystarczy kliknąć „Kontynuuj” (F5), aby kontynuować wykonywanie programu i wszystko będzie dobrze. Jeśli przeszkadzają Ci wyjątki, które zatrzymują wykonywanie programu i wyświetlają pomocnika wyjątków, albo masz „Just My Code” wyłączony, albo masz wyjątek FileNotFoundException, który przerywa wykonywanie po rzuceniu, zamiast gdy „User- nieobsługiwany ”.

Aby włączyć „Just My Code”, przejdź do Narzędzia >> Opcje >> Debugowanie >> Ogólne >> Włącz Just My Code. Aby wyłączyć przerwanie wykonywania po wyrzuceniu FileNotFound, przejdź do Debugowanie >> Wyjątki >> Znajdź >> wpisz „FileNotFoundException” >> odznacz pole wyboru „Zgłoszony” w System.IO.FileNotFoundException.

Allon Guralnek
źródło
+1 za aktualizację: wyjaśnia to inne zachowanie podczas debugowania przypadków testowych
mbx
3
Twoja aktualizacja sugeruje, że ten wyjątek nie powinien wystąpić w .NET 4.5, ale nadal go widzę.
Timbo
@Timbo: Nie rozumiem, dlaczego nie dostałeś tego wyjątku w .NET 4.5. Nadal szuka pliku, a jeśli go brakuje, FileNotFoundExceptionzostanie wyrzucony. Różnica nie polega na tym, jak sprawdzane jest istnienie złożenia, ale na tym, jak go wygenerować, gdy zostanie ustalone, że go brakuje. Wcześniej używano tekstowego generowania kodu C # z wywołaniem kompilatora C # w celu utworzenia IL. Począwszy od .NET 4.5, emituje IL bezpośrednio, bez użycia kompilatora.
Allon Guralnek
1
Chciałbym tylko, żeby MS wdrożył to tak, jakby (File.Exists (...)) {Load} else {Fallback} zamiast spróbować {Load} catch {Fallback}. Kontrola przepływu oparta na wyjątkach pachnie źle i sprawia, że ​​moje debugowanie jest trudniejsze i bardziej kruche niż to konieczne.
Timbo
1
@ Timbo: prosty File.Exists()może być niewystarczający. Lokalizowanie zestawu nie jest prostą sprawą, środowisko wykonawcze sprawdza w kilku lokalizacjach i wierzę, że zachowanie zmienia się w zależności od środowiska (aplikacja konsolowa vs. hostowana w IIS itp.). Myślę, że to, co powinno zostać zaimplementowane, było TryLoadAssembly()lub coś podobnego.
Allon Guralnek
63

We właściwościach projektu Visual Studio (strona „Kompiluj”, jeśli dobrze to pamiętam) istnieje opcja „generuj zespół serializacji”. Spróbuj włączyć go dla projektu, który generuje [Zawiera zestaw MyType] .

VladV
źródło
4
Zobacz także stackoverflow.com/a/8798289/1164966, jeśli zespół serializacji nadal nie jest generowany przez Visual Studio.
Benoit Blanchon
Najlepsza, najczystsza, zwięzła odpowiedź! Chciałbym też ponownie zagłosować!
John Zabroski,
59

Istnieje obejście tego problemu. Jeśli użyjesz

XmlSerializer lizer = XmlSerializer.FromTypes(new[] { typeof(MyType) })[0];

powinien unikać tego wyjątku. To zadziałało dla mnie.

OSTRZEŻENIE: Nie należy używać wielokrotnie, w przeciwnym razie nastąpi wyciek pamięci

Wyciekasz pamięć jak szaloną, jeśli użyjesz tej metody do tworzenia instancji XmlSerializer dla tego samego typu więcej niż jeden raz!

Wynika to z tego, że ta metoda omija wbudowane buforowanie pod warunkiem, że XmlSerializer(type)iXmlSerializer(type, defaultNameSpace) konstruktorów (wszystkie inne konstruktory również omijają pamięć podręczną).

Jeśli użyjesz dowolnej metody, aby utworzyć XmlSerializer, który nie odbywa się za pośrednictwem tych dwóch konstruktorów, musisz zaimplementować własne buforowanie lub pamięć krwotoku.

poczwórność
źródło
44
OSTRZEŻENIE: wyciek pamięci będzie szalony, jeśli użyjesz tej metody do tworzenia wystąpień XmlSerializerdla tego samego typu więcej niż jeden raz! Wynika to z tego, że ta metoda omija wbudowane buforowanie pod warunkiem konstruktorów XmlSerializer(type)i XmlSerializer(type, defaultNameSpace)(wszystkie inne konstruktory również omijają pamięć podręczną). Jeśli użyjesz dowolnej metody, aby utworzyć narzędzie, XmlSerializerktóre nie odbywa się za pośrednictwem tych dwóch konstruktorów, musisz zaimplementować własne buforowanie, w przeciwnym razie pamięć krwotoków zostanie zachowana.
Allon Guralnek
4
@AllonGuralnek Cóż, niech mnie szlag ... masz absolutną rację; dalsze kopanie za pomocą Reflector pokazuje, że chociaż sprawdza pamięć podręczną, robi to po wygenerowaniu zestawu serializacji! Wtf?!?
JerKimball 25.01.2013
4
Okazuje się, że jest to znany błąd: weblogs.asp.net/cschittko/archive/2005/01/14/353435.aspx
JerKimball
3
@JerKimball: Ta strona tak naprawdę nie kłamie. Jak odkryłeś, FromTypeswydaje się zapełniać pamięć podręczną. Powinien to być prawidłowy sposób na rozgrzanie pustej XmlSerializerpamięci podręcznej w jednej instrukcji (jak sugeruje artykuł), ale naprawdę zły sposób na odzyskanie z niej czegokolwiek (powinno się to odbywać tylko za pomocą najprostszych konstruktorów). W każdym razie nie wiedziałem, że to błąd, zawsze myślałem, że wszystko, co wyciek ma wyciec (podobnie jak bardziej zaawansowane XmlSerializerkonstruktory). Nawet nie pomyślałbym o użyciu, FromTypes()skoro możesz to zrobić types.Select(t => new XmlSerializer(t)).
Allon Guralnek
2
@AllonGuralnek Niesprawiedliwy aspekt używania FromTypesma swój urok - nawet jeśli wszystkie zgłoszone wyjątki zostaną złapane, jest to bardzo cenna operacja; podejście „buforuj po swojemu” wydaje się być jedynym obejściem, ponieważ jedyna oficjalnie obsługiwana poprawka wygląda na niejasny zestaw internetowy. (edycja: szczerze mówiąc, jestem za przeniesieniem wszystkiego do kontraktów danych :))
JerKimball 25.01.2013
22

Natrafiłem na ten konkretny problem i nie mogłem go obejść za pomocą żadnego z wymienionych rozwiązań.

W końcu znalazłem rozwiązanie. Wygląda na to, że serializator potrzebuje nie tylko typu, ale także typów zagnieżdżonych. Zmieniając to:

XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));

Do tego:

XmlSerializer xmlSerializer = new XmlSerializer(typeof(T).GetNestedTypes());

Naprawiono problem dla mnie. Nigdy więcej wyjątków lub czegokolwiek.

Mroźny
źródło
8
To zadziałało dla mnie. Przy użyciu .Net4.0 format ma var xmlSerializer = new XmlSerializer(typeof(T), typeof(T).GetNestedTypes());
postać
1
To również działało dla mnie. Ale wydaje się to konieczne tylko podczas serializacji, a nie podczas deserializacji. Może to ma sens, a może nie.
SteveCinq,
2
Powoduje to także wyciek pamięci, jeśli jest uruchamiany wiele razy.
Volodymyr Kotylo
9

Moim rozwiązaniem jest przejście bezpośrednio do refleksji, aby utworzyć serializator. Pomija to dziwne ładowanie plików, które powoduje wyjątek. Spakowałem to w funkcji pomocniczej, która również zajmuje się buforowaniem serializatora.

private static readonly Dictionary<Type,XmlSerializer> _xmlSerializerCache = new Dictionary<Type, XmlSerializer>();

public static XmlSerializer CreateDefaultXmlSerializer(Type type) 
{
    XmlSerializer serializer;
    if (_xmlSerializerCache.TryGetValue(type, out serializer))
    {
        return serializer;
    }
    else
    {
        var importer = new XmlReflectionImporter();
        var mapping = importer.ImportTypeMapping(type, null, null);
        serializer = new XmlSerializer(mapping);
        return _xmlSerializerCache[type] = serializer;
    }
}
d - b
źródło
2 problemy tutaj - po pierwsze twój kod nie jest bezpieczny dla wątków, a po drugie (co ważniejsze) próbujesz replikować to, co już działa środowisko uruchomieniowe .net (na podstawie używanego ctora). tzn. nie ma potrzeby używania tego kodu
Dave Black
@DaveBlack: Tak, odpowiedź quadfinity z buforowaniem do ConcurrentDictionary byłaby lepsza
d - b
@db Moja druga uwaga była taka, że ​​buforowanie nie jest nawet potrzebne - pod warunkiem, że używasz jednego z 2 cache'ów, które buforuje framework (OP używa pierwszego). Z MSDN: Aby zwiększyć wydajność, infrastruktura serializacji XML dynamicznie generuje zestawy w celu serializacji i deserializacji określonych typów. Struktura wyszukuje i ponownie wykorzystuje te zespoły. To zachowanie występuje tylko w przypadku używania następujących wskaźników: XmlSerializer.XmlSerializer (typ) XmlSerializer.XmlSerializer (typ, ciąg) Odniesienie: msdn.microsoft.com/en-us/library/...
Dave Black
@DaveBlack: Tak, ale te konstruktory zgłaszają i przechwytują wyjątek wewnętrznie, nawet jeśli użycie jest całkowicie poprawne. To źle, i właśnie dlatego PO zadał to pytanie.
d - b
@db Prawda, ale to, co chciałem powiedzieć (ale nie było jasne - przepraszam) było to, że jedyne niezbędne wiersze twojego soln to pierwsze 3 wiersze w innym stanie.
Dave Black
8

Aby uniknąć wyjątku, musisz zrobić dwie rzeczy:

  1. Dodaj atrybut do serializowanej klasy (mam nadzieję, że masz dostęp)
  2. Wygeneruj plik serializacji za pomocą sgen.exe

Dodaj atrybut System.Xml.Serialization.XmlSerializerAssembly do swojej klasy. Zamień „MyAssembly” na nazwę zestawu, w którym znajduje się MyClass.

[Serializable]
[XmlSerializerAssembly("MyAssembly.XmlSerializers")]
public class MyClass
{

}

Wygeneruj plik serializacji za pomocą narzędzia sgen.exe i wdróż go przy użyciu zestawu klasy.

„sgen.exe MyAssembly.dll” wygeneruje plik MyAssembly.XmlSerializers.dll

Te dwie zmiany spowodują, że .net bezpośrednio znajdzie zestaw. Sprawdziłem to i działa na .NET Framework 3.5 z Visual Studio 2008

Ami Bar
źródło
Ok, a czy to się nie powiedzie bez tych zmian, a jeśli tak, to dlaczego?
John Saunders
1
Nie mogę znaleźć powodu, dla którego mój projekt, 4.0 w VS2012, nagle zaczął zawodzić. „Ignorowanie” nie było opcją, ponieważ występował za każdym razem, gdy próbowałem uzyskać dostęp do Active Directory; ignorowanie oznaczałoby zatem brak uwierzytelnienia. Nadal jestem bardzo sfrustrowany, że VS2012 nie wygeneruje automatycznie biblioteki DLL serializacji. Jednak te kroki stanowiły idealne rozwiązanie.
sfuqua
6

Ten wyjątek może również zostać uwięziony przez zarządzanego asystenta debugowania (MDA) o nazwie BindingFailure.

Ta MDA jest przydatna, jeśli aplikacja jest zaprojektowana do dostarczania z kompilacjami serializacji przed kompilacją. Robimy to, aby zwiększyć wydajność naszej aplikacji. Pozwala nam to upewnić się, że wstępnie skompilowane zestawy serializacji są poprawnie budowane przez nasz proces kompilacji i ładowane przez aplikację bez konieczności ponownej kompilacji w locie.

Jest to naprawdę nieprzydatne, z wyjątkiem tego scenariusza, ponieważ jak powiedzieli inni plakaty, gdy konstruktor Serializer uwięził błąd wiązania, zespół serializacji jest ponownie budowany w czasie wykonywania. Więc zazwyczaj możesz to wyłączyć.

HiredMind
źródło
6

Funkcja XmlSerializer.FromTypes nie zgłasza wyjątku, ale przecieka pamięć. Dlatego musisz buforować taki serializator dla każdego typu, aby uniknąć wycieku pamięci dla każdej utworzonej instancji.

Utwórz własną fabrykę XmlSerializer i użyj jej po prostu:

XmlSerializer serializer = XmlSerializerFactoryNoThrow.Create(typeof(MyType));

Fabryka wygląda jak:

public static class XmlSerializerFactoryNoThrow
{
    public static Dictionary<Type, XmlSerializer> _cache = new Dictionary<Type, XmlSerializer>();

    private static object SyncRootCache = new object();        

    /// <summary>
    /// //the constructor XmlSerializer.FromTypes does not throw exception, but it is said that it causes memory leaks
    /// http://stackoverflow.com/questions/1127431/xmlserializer-giving-filenotfoundexception-at-constructor
    /// That is why I use dictionary to cache the serializers my self.
    /// </summary>
    public static XmlSerializer Create(Type type)
    {
        XmlSerializer serializer;

        lock (SyncRootCache)
        {
            if (_cache.TryGetValue(type, out serializer))
                return serializer;
        }

        lock (type) //multiple variable of type of one type is same instance
        {
            //constructor XmlSerializer.FromTypes does not throw the first chance exception           
            serializer = XmlSerializer.FromTypes(new[] { type })[0];
            //serializer = XmlSerializerFactoryNoThrow.Create(type);
        }

        lock (SyncRootCache)
        {
            _cache[type] = serializer;
        }
        return serializer;
    }       
}

Bardziej skomplikowana wersja bez możliwości wycieku pamięci (proszę przejrzeć kod):

    public static XmlSerializer Create(Type type)
    {
        XmlSerializer serializer;

        lock (SyncRootCache)
        {
            if (_cache.TryGetValue(type, out serializer))
                return serializer;
        }

        lock (type) //multiple variable of type of one type is same instance
        {
            lock (SyncRootCache)
            {
                if (_cache.TryGetValue(type, out serializer))
                    return serializer;
            }
            serializer = XmlSerializer.FromTypes(new[] { type })[0];
            lock (SyncRootCache)
            {
                _cache[type] = serializer;
            }
        }          
        return serializer;
    }       
}
Tomas Kubes
źródło
Zamiast tego powinieneś użyć ConcurrentDictionary. Ten kod może zostać zakleszczony.
Behrooz
Jak można to zrobić, gdy całe zarządzanie ze słownikiem znajduje się w sekcji blokady?
Tomas Kubes
Przepraszam, mylę te słowa. Miałem na myśli to, że może wstawić element więcej niż jeden raz. ponieważ istnieje luka między momentem sprawdzenia istnienia a momentem wstawienia. słownik współbieżny używa pewnego rodzaju blokady dwufazowej (torba [0], a następnie torba [hash]]) i zachowuje odniesienie do torby, która musi wstawić / zawierać przedmiot, który pracujesz. Jest szybszy, bezpieczniejszy i czystszy.
Behrooz
Tak i nie. Masz rację, że może się zdarzyć, że w tym samym czasie jeden serializator tego samego typu zostanie utworzony w dwóch wątkach równolegle, a następnie dwukrotnie dodany do słownika. W takim przypadku druga wkładka po prostu zastąpi pierwszą, ale sekcja blokująca gwarantuje bezpieczeństwo gwintu, a ogólną wadą jest niewielki wyciek pamięci. Jest to optymalizacja wydajności, ponieważ nie chcesz, aby wątek pierwszy z Serializatorem typu A był blokowany przez wątek drugi z serializatorem typu B w prawdziwym scenariuszu.
Tomas Kubes
Mogę sobie wyobrazić, że rozwiązanie może być jeszcze lepsze (bez teoretycznego wycieku pamięci), ale bardziej skomplikowane.
Tomas Kubes
3

Z drugiej strony rozwiązywanie problemów z kompilacją jest bardzo skomplikowane. Te problemy przejawiają się w wyjątku FileNotFoundException z komunikatem:

File or assembly name abcdef.dll, or one of its dependencies, was not found. File name: "abcdef.dll"
   at System.Reflection.Assembly.nLoad( ... )
   at System.Reflection.Assembly.InternalLoad( ... )
   at System.Reflection.Assembly.Load(...)
   at System.CodeDom.Compiler.CompilerResults.get_CompiledAssembly() 

Możesz się zastanawiać, co wyjątek związany z nie znalezieniem pliku ma związek z tworzeniem wystąpienia obiektu serializującego, ale pamiętaj: konstruktor zapisuje pliki C # i próbuje je skompilować. Stos wywołań tego wyjątku dostarcza pewnych dobrych informacji na poparcie tego podejrzenia. Wystąpił wyjątek, gdy XmlSerializer próbował załadować zestaw wygenerowany przez CodeDOM wywołujący metodę System.Reflection.Assembly.Load. Wyjątek nie wyjaśnia, dlaczego zestaw, który miał utworzyć XmlSerializer, nie był obecny. Ogólnie rzecz biorąc, zestaw nie jest obecny, ponieważ kompilacja nie powiodła się, co może się zdarzyć, ponieważ w rzadkich okolicznościach atrybuty serializacji generują kod, którego kompilator C # nie kompiluje.

Uwaga Ten błąd występuje również, gdy XmlSerializer działa na koncie lub w środowisku bezpieczeństwa, które nie ma dostępu do katalogu tymczasowego.

Źródło : http://msdn.microsoft.com/en-us/library/aa302290.aspx

Zyphrax
źródło
nie sprecyzował, że tak się dzieje w czasie wykonywania. Inną rzeczą, o której mogę myśleć, jest to, że możesz mieć konflikt przestrzeni nazw / klasy. Jaka jest pełna nazwa twojego MyType?
Zyphrax
Tak, sprawdziłem twój link, informacje o konstruktorach, choć pomocne, nie były tym, czego potrzebowałem.
Irwin
5
@SpaceghostAl Możesz kompilować w czasie wykonywania. I to właśnie robi XmlSerializer. W czasie wykonywania dynamicznie konstruuje zestaw, który (de) serializuje XML dla określonego typu. Z jakiegokolwiek powodu proces ten kończy się niepowodzeniem dla PO. Prawdopodobnie z powodu problemów z uprawnieniami, np. W katalogu tymczasowym. (Może być tak głupie, jak z braku miejsca na dysku.)
nos
Jesteś tego pewien? Byłem całkiem pewien, że serializacja zostanie skompilowana do zestawu o nazwie YourAssemblyName.XmlSerializers.dll podczas kompilacji , a nie kompilacji w czasie wykonywania. Może to zakończyć się niepowodzeniem z różnych powodów, a przynajmniej ze wszystkich uprawnień NTFS w folderze wdrażania.
tomfanning
1
Chciałbym móc to głosować wiele razy. Twoja notatka o tym, że konto nie może uzyskać dostępu do folderu tymczasowego, wywołała u mnie odpowiedź. Po dodaniu mojego konta usługi do grupy administracyjnej na serwerze, po prostu działało. Dziękuję Ci!
Bob Horn
2

We właściwościach projektu Visual Studio istnieje opcja „generuj zespół serializacji”. Spróbuj włączyć go dla projektu, który generuje [Zawiera zestaw MyType].

Pascal
źródło
1

Klasa niestandardowa do serializacji:

[Serializable]
public class TestClass
{
    int x = 2;
    int y = 4;
    public TestClass(){}
    public TestClass(int x, int y)
    {
        this.x = x;
        this.y = y;
    }

    public int TestFunction()
    {
        return x + y;
    }
}

Załączam fragment kodu. Może to może ci pomóc.

static void Main(string[] args)
{
    XmlSerializer xmlSerializer = new XmlSerializer(typeof(TestClass));

    MemoryStream memoryStream = new MemoryStream();
    XmlTextWriter xmlWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);

    TestClass domain = new TestClass(10, 3);
    xmlSerializer.Serialize(xmlWriter, domain);
    memoryStream = (MemoryStream)xmlWriter.BaseStream;
    string xmlSerializedString = ConvertByteArray2Str(memoryStream.ToArray());

    TestClass xmlDomain = (TestClass)DeserializeObject(xmlSerializedString);

    Console.WriteLine(xmlDomain.TestFunction().ToString());
    Console.ReadLine();
}
shahjapan
źródło
2
-1 za brak użycia bloków w celu zapobiegania wyciekom zasobów oraz za użycie XmlTextWriter.
John Saunders
ok zgadzam się, ale nadal używałem XmlSerializer xmlSerializer = nowy XmlSerializer (typeof (TestClass)); ale nie dostaję wspomnianego wyjątku.
shahjapan
1

Miałem podobny problem i zignorowanie wyjątku nie zadziałało. Mój kod wywoływał konfigurację NServiceBusConfigure.With(...).XmlSerializer()...

Naprawdę zmieniłem platformę mojego projektu.

  1. Przejdź do Build \ Configuration Manager ...
  2. Znajdź swój projekt i zmień platformę (w moim przypadku z x86 na dowolny procesor)
kkelley
źródło
1

Tylko jako odniesienie. Biorąc od DB odpowiedzi i komentarze, przyszedłem z tym rozwiązaniem, które jest zbliżone do rozwiązania DB. Działa dobrze we wszystkich moich przypadkach i jest bezpieczny dla wątków. Nie sądzę, że użycie ConcurrentDictionary byłoby w porządku.

using System;
using System.Collections.Generic;
using System.Xml.Serialization;

namespace HQ.Util.General
{
    public class XmlSerializerHelper
    {
        private static readonly Dictionary<Type, XmlSerializer> _dictTypeToSerializer = new Dictionary<Type, XmlSerializer>();

        public static XmlSerializer GetSerializer(Type type)
        {
            lock (_dictTypeToSerializer)
            {
                XmlSerializer serializer;
                if (! _dictTypeToSerializer.TryGetValue(type, out serializer))
                {
                    var importer = new XmlReflectionImporter();
                    var mapping = importer.ImportTypeMapping(type, null, null);
                    serializer = new XmlSerializer(mapping);
                    return _dictTypeToSerializer[type] = serializer;
                }

                return serializer;
            }
        }
    }
}

Stosowanie:

        if (File.Exists(Path))
        {
            using (XmlTextReader reader = new XmlTextReader(Path))
            {
                // XmlSerializer x  = new XmlSerializer(typeof(T));
                var x = XmlSerializerHelper.GetSerializer(typeof(T));

                try
                {
                    options = (OptionsBase<T>)x.Deserialize(reader);
                }
                catch (Exception ex)
                {
                    Log.Instance.AddEntry(LogType.LogException, "Unable to open Options file: " + Path, ex);
                }
            }
        }
Eric Ouellet
źródło
0

Twój typ może odwoływać się do innych zespołów, których nie można znaleźć ani w GAC, ani w lokalnym folderze bin ==> ...

„lub jedną z jego zależności. System nie może znaleźć określonego pliku”

Czy możesz podać przykład typu, który chcesz serializować?

Uwaga: Upewnij się, że Twój typ implementuje Serializable.

Henrik
źródło
0

Otrzymywałem ten sam błąd i wynikało to z typu, którego próbowałem deserializować, nie mając domyślnego konstruktora bez parametrów . Dodałem konstruktor i zaczął działać.

kay.one
źródło
0

Miałem ten sam problem, dopóki nie użyłem narzędzia innej firmy do wygenerowania klasy z XSD i zadziałało! Odkryłem, że narzędzie dodawało dodatkowy kod na początku mojej klasy. Kiedy dodałem ten sam kod na początku mojej oryginalnej klasy, zadziałało. Oto co dodałem ...

#pragma warning disable
namespace MyNamespace
{
  using System;
  using System.Diagnostics;
  using System.Xml.Serialization;
  using System.Collections;
  using System.Xml.Schema;
  using System.ComponentModel;
  using System.Xml;
  using System.Collections.Generic;

  [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.6.1064.2")]
  [System.SerializableAttribute()]
  [System.Diagnostics.DebuggerStepThroughAttribute()]
  [System.ComponentModel.DesignerCategoryAttribute("code")]
  [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
  [System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
  public partial class MyClassName
  {
  ...
TheJonz
źródło
0

Widziałem wiele rekomendacji do użycia ConcurrentDictionary, ale nie ma solidnych przykładów, więc wrzucę swój kapelusz w ten wyścig rozwiązań. Nie jestem programistą bezpiecznym dla wątków, więc jeśli ten kod nie jest solidny, proszę zabrać głos ze względu na tych, którzy go śledzą.

public static class XmlSerializerHelper
{
    private static readonly ConcurrentDictionary<Type, XmlSerializer> TypeSerializers = new ConcurrentDictionary<Type, XmlSerializer>();

    public static XmlSerializer GetSerializer(Type type)
    {
        return TypeSerializers.GetOrAdd(type,
        t =>
        {
            var importer = new XmlReflectionImporter();
            var mapping = importer.ImportTypeMapping(t, null, null);
            return new XmlSerializer(mapping);
        });
    }
}

Widziałem inne posty obejmujące ConcurrentDictionaryi Lazyładujące wartość. Nie jestem pewien, czy jest to istotne tutaj, czy nie, ale oto kod do tego:

private static readonly ConcurrentDictionary<Type, Lazy<XmlSerializer>> TypeSerializers = new ConcurrentDictionary<Type, Lazy<XmlSerializer>>();

public static XmlSerializer GetSerializer(Type type)
{
    return TypeSerializers.GetOrAdd(type,
    t =>
    {
        var importer = new XmlReflectionImporter();
        var mapping = importer.ImportTypeMapping(t, null, null);
        var lazyResult = new Lazy<XmlSerializer>(() => new XmlSerializer(mapping), LazyThreadSafetyMode.ExecutionAndPublication);
        return lazyResult;
    }).Value;
}
Airn5475
źródło