Laden...

XmlSerializer.Deserialize wirft InvalidOperationException mit NullReference-InnerException

Erstellt von inflames2k vor 9 Jahren Letzter Beitrag vor 9 Jahren 4.775 Views
inflames2k Themenstarter:in
2.298 Beiträge seit 2010
vor 9 Jahren
XmlSerializer.Deserialize wirft InvalidOperationException mit NullReference-InnerException

Hallo,

derzeit schreibe ich an einem Tool, das die Konfiguration einer externen Anwendung mittels Remoting ausliest und die erhaltenen Daten via XmlSerializer in eine Datei schreibt. Dabei wird für jede Konfiguration eine eigene Datei geschrieben. Diese Funktionalität dient der Sicherung der Konfiguration. Die Konfigurationen sind unterschiedlich ausgeprägt und liegen als Objektinstanzen vor (verschiedene Typen), die alle von einer Basisklasse "Config" erben.

Nun sollen die Konfigurationen auch wieder zurück gespielt werden. Grundsätzlich ist dies kein Problem. Allerdings gibt es darunter Konfigurationstypen, die beim Deserialisieren eine Exception werfen.

Fehlermeldung:
System.InvalidOperationException" ist in System.Xml.dll aufgetreten.
System.InvalidOperationException: Fehler im XML-Dokument (25,6). ---> System.NullReferenceException: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt.
bei Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderDatabaseConfig.Read7_DatabaseConfig(Boolean isNullable, Boolean checkType)
bei Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderDatabaseConfig.Read8_DatabaseConfig()

Im Internet werde ich nicht wirklich fündig, was die Ursache für das Verhalten ist. Die Serialisierung hat ja auch super funktioniert.

Kennt jemand das Problem und hat eventuell eine Lösung dafür?

EDIT:
Als Zusätzliche Info sei noch gesagt, dass die Konfigurationen die nicht deserialisiert werden können alle eine Property vom Typ List<T> enthalten (wobei T ein konkreter Typ ist , der sich unterscheided).

Ich habe nun herausgefunden, dass es an der List itself liegt und die Vorschläge die dazu zu finden sind, ist die Verwendung eines Arrays. Da ich jedoch keinen Zugriff auf die zu serialisierenden Klassen habe, könnte ich dies nur von außen tun. Gibt es da eine Möglichkeit?

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

P
157 Beiträge seit 2014
vor 9 Jahren

Holla,

mit dem Fehler kann man nicht viel Anfangen, benutzt du eigene Serialisierungmethoden ? dann würde ich da mal reinschauen...poste mehr von deinem Problem, sonst kann man dir nicht helfen 😉

Deserialisierung von generischen Liste sollte kein Problem darstellen, es kann sein, dass du Namings geändert hast oder die Datei wurde manuelll angepasst...null ref kann alles sein...

vg

Wenn's zum weinen nicht reicht, lach drüber!

inflames2k Themenstarter:in
2.298 Beiträge seit 2010
vor 9 Jahren

Hallo,

mit dem Fehler kann man nicht viel Anfangen, benutzt du eigene Serialisierungmethoden ?

Nein, ich nutze keine eigenen Serialisierungsmethoden. Macht denk ich auch keinen Sinn, denn bisher hats der XmlSerializer auch getan.

poste mehr von deinem Problem, sonst kann man dir nicht helfen 😉

Mehr Informationen als ich geliefert habe gibt es nicht (Gäbe es sie, hätte ich sie geliefert). Wie gesagt, die Serialisierung der Objekte funktionierte Problemlos. Das Problem kommt erst beim Deserialisieren. Nutzen tu ich dafür die üblichen XmlSerializer-Konstrukte.

In der generierten Xml-Datei habe ich auch keines Wegs Daten manuell angepasst. - Für 90% der Dateien funktioniert die Deserialisierung auch, einzige Ausnahme sind 4 Typen die jeweils Properties vom Typ List<T> besitzen.

Ich vermute allerdings derzeit, dass es daran liegt, dass die Listen innerhalb der Klasse nicht initialisiert werden (ist extern, habe ich keinen Zugriff drauf).

Übrigens, du solltest den Aufbau deiner Beiträge mal überarbeiten. Wechsel zwischen Groß- und Kleinschreibung sowie mehrere Punkte zwischen einzelnen Textabschnitten erschweren das Lesen enorm.

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

P
660 Beiträge seit 2008
vor 9 Jahren

Morgen,

würde sich an so einer Stelle nicht eine Wrapper-Klasse anbieten?

Die Wrapper-Klasse würde dann ein Array bereitstellen, und nach dem einlesen, die Liste der eigentlichen Klasse befüllen und das Objekt zurückliefern.

MfG
ProGamer*Der Sinn Des Lebens Ist Es, Den Sinn Des Lebens Zu Finden! *"Wenn Unrecht zu Recht wird dann wird Widerstand zur Pflicht." *"Ignorance simplifies ANY problem." *"Stoppt die Piraterie der Musikindustrie"

49.485 Beiträge seit 2005
vor 9 Jahren

Hallo inflames2k,

wenn es eigentlich funktionieren sollte, dann ist das vielleicht ein Fall für [Tutorial] Vertrackte Fehler durch Vergleich von echtem Projekt mit minimalem Testprojekt finden. Ein minimales Beispiel zum Vergleich könnte eine Klasse sein, die nichts anderes als eine Property vom Typ List<T> enthält. Lässt sich diese de-serialisieren? Macht es einen unterschied, ob die Property null ist oder ob sie auf eine Liste der Länge Null verweist oder ob bei einer längeren Liste die Elemente der Liste null sind oder nicht?

wobei T ein konkreter Typ ist , der sich unterscheided

Nur zur Sicherheit: Du meinst, sich in den verschiedenen Klassen/Properties unterscheidet? Aber beim Serialisieren und Deserialieren gleich ist, oder? Sonst wäre die Ursache auch offensichtlich. 😃

herbivore

inflames2k Themenstarter:in
2.298 Beiträge seit 2010
vor 9 Jahren

würde sich an so einer Stelle nicht eine Wrapper-Klasse anbieten?

Hallo ProGamer,

das funktioniert nur bedingt. - Wie gesagt werden die Objekte per .NET Remoting von einem anderen Programm ausgelesen (dieses stellt auch eine Schnittstelle zum Abruf aller verwendeten Plugins bereit). Da ich zuvor die Plugins auslese und in einem lokalen Ordner ablege sowie die Plugins im Programm lade, kann ich die Objekte auch empfangen. - Anschließend speichere ich die Konfigurationen per Xml-Serialisierung. - Ein Wrapper setzt vorraus, dass ich die konkrete Implementation der Konfigurationsklasse kenne. Das ist aber nicht der Fall.

Hallo herbivore,

Ein minimales Beispiel zum Vergleich könnte eine Klasse sein, die nichts anderes als eine Property vom Typ List<T> enthält. Lässt sich diese de-serialisieren? Macht es einen unterschied, ob die Property null ist oder ob sie auf eine Liste der Länge Null verweist oder ob bei einer längeren Liste die Elemente der Liste null sind oder nicht?

Das werde ich mal probieren, bisher war mein Testprojekt um dem zu folgen so aufgebaut, dass ich das Konkrete Plugin geladen hatte. Aber auf diese Weise komme ich dem eventuell eher auf die Schliche.

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

U
1.688 Beiträge seit 2007
vor 9 Jahren

Hallo,

die Exception tritt in Deiner Anwendung oder in der Remote-Anwendung auf? Hilft es, von VS die Exception direkt beim Auftreten anzeigen zu lassen und dann den disassemblierten Code anzuschauen (ggf. per Reflector)?

Ein minimales Beispiel zum Vergleich könnte eine Klasse sein, die nichts anderes als eine Property vom Typ List<T> enthält. Lässt sich diese de-serialisieren?

Inwieweit ist es abhängig von <T>? Welche Gemeinsamkeiten haben die betroffenen <T>? Benutzt Du zum Serialisieren/Deserialisieren eine Basisklasse (oder den konkreten Typ)?

Ein bisschen Code wäre nicht schlecht. Evtl. kannst Du Dir auch eine betroffene Klasse aus der Remote-Anwendung per Reflector zum Testen kopieren.

inflames2k Themenstarter:in
2.298 Beiträge seit 2010
vor 9 Jahren

Hallo ujr,

die Exception tritt in Deiner Anwendung oder in der Remote-Anwendung auf?

Die Exception tritt in meiner Anwendung beim deserialisieren auf. Deserialisiere ich eine Xml-Datei, in der keine Einträge für die Liste vorhanden sind, wird die Deserialisierung durchgeführt und es kommt zu keinem Fehler.

Inwieweit ist es abhängig von <T>? Welche Gemeinsamkeiten haben die betroffenen <T>? Benutzt Du zum Serialisieren/Deserialisieren eine Basisklasse?

Hallo, von dem Typparameter ist es nicht abhängig. Viel eher davon, dass überhaupt Listen verwendet werden. Wie gesagt, lasse ich die Listeneinträge beim auslesen weg (manuell aus der Datei löschen), wird die Deserialisierung durchgeführt und im Debugger sehe ich auch, dass das Objekt den korrekten Typ besitzt.

Ein bisschen Code wäre nicht schlecht.

Da gibt es nicht viel Code. Aber dennoch, kann ich die Methode zum Deserialisieren ja mal posten.


private Config DeserializeConfigFromXml(XmlDocument xml, Type configType)
        {
            Config config = null;

            // initialize memory stream for writing the xml document to
            using (Stream stream = new MemoryStream())
            {
                // write the xml document to the memory stream
                xml.Save(stream);
                // deserialize the config from the stream
                XmlSerializer serializer = new XmlSerializer(configType);
                stream.Flush();
                stream.Position = 0;

                config = (Config)serializer.Deserialize(stream);
            }

            return config;
        }

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

4.939 Beiträge seit 2008
vor 9 Jahren

Hallo,

viel interessanter sind wohl deine Config-Klassen.
Hattest du schon Getting InvalidOperationException when Deserializing in C# gefunden?
Und verwendest du das XmlArrayItemAttribute bei deinen List-Properties?

U
1.688 Beiträge seit 2007
vor 9 Jahren

Hallo,

Da gibt es nicht viel Code. Aber dennoch, kann ich die Methode zum Deserialisieren ja mal posten.

Eher der Code einer zu serialiserenden Klasse - aber den hast Du wohl original nicht. Darum noch die Idee mit dem Reflector.

inflames2k Themenstarter:in
2.298 Beiträge seit 2010
vor 9 Jahren

Hallo,

viel interessanter sind wohl deine Config-Klassen.

den Code der Config-Klassen besitze ich ja nicht. 🤔 Dachte das wäre aus den vorherigen Beiträgen heraus gekommen. Ich hole mir nur die Assemblies mit den Klassen per Remoting und lade die Typen mittels Reflection.

Hattest du schon
>
gefunden?

Und verwendest du das XmlArrayItemAttribute bei deinen List-Properties?

Habe ich noch nicht gefunden. Xml-Attribute kann ich ja leider nicht setzen. Die Klassen sind ja nicht von mir.

@herbivore
Das Testprojekt habe ich erstellt und es läuft Problemlos. Ich werde es jetzt mal so umbauen, dass der wirkliche Typ (also der Config-Typ) einmal geladen wird. Wenn es dann funktioniert, muss der Fehler doch wo anders liegen.

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

4.939 Beiträge seit 2008
vor 9 Jahren

Sorry, das hatte ich überlesen.
Aber die Config-Basisklasse muß dir ja bekannt sein (sonst würde deine Methode ja nicht kompilieren) - poste diese mal.
Es ist entscheidend, wie die List<T>-Eigenschaft deklariert ist - oder existieren diese nur jeweils in den abgeleiteten Config-Klassen.

inflames2k Themenstarter:in
2.298 Beiträge seit 2010
vor 9 Jahren

Es ist entscheidend, wie die List<T>-Eigenschaft deklariert ist - oder existieren diese nur jeweils in den abgeleiteten Config-Klassen.

Genau so ist es. Die Basisklasse kenne ich auch nur in sofern, dass ein Verweis auf die entsprechende DLL existiert.

Von einem Modul weiß ich jedoch, dass die Liste innerhalb der Konfiguration etwa wie folgt deklariert ist:


public class DatabaseConfig : Config
{
        private List<DatabaseParameter> dbParameters;
        public List<DatabaseParameter> DBParameters
        {
           get { return _dbParameters; }
           set { _dbparameters = value; }
        }
}

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

4.939 Beiträge seit 2008
vor 9 Jahren

Sofern DatabaseParameter wiederum eine Basisklasse ist, dann müssen explizit alle davon abgeleiteten Klassen als XmlArrayItemAttribute bei der Deklaration der List<T>-Eigenschaft angegebn werden (auch wenn diese dem Open-Closed-Prinzip der OOP widerspricht).

Wie sieht denn exemplarisch die dazugehörige Xml-Datei aus?

inflames2k Themenstarter:in
2.298 Beiträge seit 2010
vor 9 Jahren

Hallo,

ich denke ich habe die Ursache gefunden. Die Plugins liegen in verschiedenen Versionen beim externen System vor. - Einige Komponenten im externen System greifen dabei auch auf unterschiedliche Versionen der Plugins zu.

Ich lade derzeit immer das jeweils benötigte Plugin zur Deserialisierung. Das hat aber zur Folge, dass ich ein Plugin z.T. auch mehrfach (in verschiedenen Versionen) lade.

Bei dem im vorherigen Beitrag exemplarisch dargestellten Database-Plugin ist es so, dass zwischen den letzten 2 Versionen eine Änderung des Typs der Collection durchgeführt wurde.

In der letzten Version war eines eine List<DatabaseParameter> und in der aktuellen Version ist es ein Typ der von List<DatabaseParameter> erbt. Nach mehrerem hin- und herprobieren sagt mir der Debugger nun nämlich, dass er nicht entscheiden kann welchen Typen er nehmen soll (Obwohl ich diesen explizit angegeben habe, er wird ja aus dem entsprechenden Plugin geladen).

Ich werde mal schauen, ob ich das Problem irgendwie gelöst bekomme.

// EDIT:
@Talla:
DatabaseParameter hat keine Basisklasse. Allerdings bleibt wie gesagt das Problem, dass ich am Code der Plugins nichts ändern kann (da es sich um eine exteren Anwendugn handelt).

Das generierte Xml sieht in etwa wie folgt aus (speziell für die DatabaseConfig):


<DatabaseConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <ConnectionString>Initial Catalog=datenbank; Data Source=hostname; User id=user; password=password</ConnectionString>
  <CommandTimeout>120</CommandTimeout>
  <ProviderName>System.Data.SqlClient</ProviderName>  
  <StoredProcedure>auszuführende Prozedur</StoredProcedure>
  <Description />
  <DatabaseParameters>
    <DatabaseParameter>
      <PropertyName>Externe BindingProperty</PropertyName>
      <ParameterName>Parametername</ParameterName>
      <Type>String</Type>
      <Size>0</Size>
      <Direction>Input</Direction>
    </ParameterMapping>
   <!-- ... -->
  </ParameterMappings>
</DatabaseConfig>

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

F
10.010 Beiträge seit 2004
vor 9 Jahren

Dann ist aber der beschränkte XmlSerializer die falsche Wahl.
Der kann vieles nur durch manuellen Eingriff.

Benutze lieber etwas wie den ( zufällig gestern erst hier erwähnten ) UniversalSerializer