Perché è Deserialize () di XmlSerializer che sputa un object figlio che è un XmlNode ?

Sto usando XmlSerializer per serializzare e poi deserializzare un object semplice. Quando ho deserializzato l’object con mia sorpresa, ho trovato che un object figlio non era correttamente deserializzato, ma invece è diventato XmlNode[] .

Qui c’è quasi la struttura che ho:

 // This line I put in here as a way of sneaking into the XML the // root node's C# namespace, since it's not the same as the // deserializing code and the deserializing code seemed unable to // deserialize properly without knowing the Type (see my code below). // So I basically just use this fake construct to get the namespace // and make a Type of it to feed the XmlSerializer() instantiation. [XmlRoot(Namespace = "http://foo.com/CSharpNamespace/Foo.Bar")] // This is because QueuedFile can be given to the Argument array. [XmlInclude(typeof(QueuedFile))] // This class is Foo.Bar.CommandAndArguments public class CommandAndArguments { public String Command; public object[] Arguments; } // I don't think this matters to XmlSerialize, but just in case... [Serializable()] // I added this line just thinking maybe it would help, but it doesn't // do anything. I tried it without the XmlType first, and that // didn't work. [XmlType("Foo.Baz.Bat.QueuedFile")] // This class is Foo.Baz.Bat.QueuedFile (in a different c# // namespace than CommandAndArguments and the deserializing code) public QueuedFile { public String FileName; public String DirectoryName; } 

E il codice che deserializza sembra:

 public static object DeserializeXml(String objectToDeserialize) { String rootNodeName = ""; String rootNodeNamespace = ""; using (XmlReader xmlReader = XmlReader.Create(new StringReader(objectToDeserialize))) { if (xmlReader.MoveToContent() == XmlNodeType.Element) { rootNodeName = xmlReader.Name; rootNodeNamespace = xmlReader.NamespaceURI; if (rootNodeNamespace.StartsWith("http://foo.com/CSharpNamespace/")) { rootNodeName = rootNodeNamespace.Substring("http://foo.com/CSharpNamespace/".Length) + "." + rootNodeName; } } } //MessageBox.Show(rootNodeName); try { Type t = DetermineTypeFromName(rootNodeName); if (t == null) { throw new Exception("Could not determine type of serialized string. Type listed as: "+rootNodeName); } var s = new XmlSerializer(t); return s.Deserialize(new StringReader(objectToDeserialize)); // object o = new object(); // MethodInfo castMethod = o.GetType().GetMethod("Cast").MakeGenericMethod(t); // return castMethod.Invoke(null, new object[] { s.Deserialize(new StringReader(objectToDeserialize)) }); } catch (InvalidOperationException) { return null; } } 

Ed ecco l’XML quando il CommandAndArguments è serializzato:

   I am a command   HelloWorld.txt C:\foo\bar    

Ma quando deserializzo mi viene dato un object CommandAndArguments dove Arguments è XmlNode[] con il primo elemento che è l’attributo che dà il QueuedFile come tipo e gli altri indici che sono elementi delle proprietà. Ma perché l’object QueuedFile non è stato ricreato?

Sospetto che questo potrebbe in qualche modo avere a che fare con gli spazi dei nomi C # e il motore che sta facendo la deserializzazione non essendo in grado di trovare o lavorare con QueuedFile … Ma non vedo perché da quando ho dimenticato XmlInclude() mi ha fatto in modo di dirmelo non mi aspettavo QueuedFile e ora che ho aggiunto XmlInclude() non ottengo alcun errore, solo una deserializzazione incompleta.

Aiuto? Ho letto tutto quello che riesco a leggere e ho cercato su Google tutto ciò che so su Google e sono bloccato. Ho sicuramente molto da imparare sulla serializzazione XML, ma non sono sicuro di come sto fallendo in qualcosa che dovrebbe essere piuttosto semplice (in realtà ho fatto qualcosa di simile esattamente prima senza alcun problema, l’unica differenza era che tutto era nello stesso spazio dei nomi C #).

Sei in grado di modificare il formato XML o è stato risolto? Non so quale sia il problema, ma utilizzo le classi DataContractSerializer senza problemi.

http://msdn.microsoft.com/en-us/library/system.runtime.serialization.datacontractserializer.aspx

 public static void WriteObject(string fileName) { Console.WriteLine( "Creating a Person object and serializing it."); Person p1 = new Person("Zighetti", "Barbara", 101); FileStream writer = new FileStream(fileName, FileMode.Create); DataContractSerializer ser = new DataContractSerializer(typeof(Person)); ser.WriteObject(writer, p1); writer.Close(); } public static void ReadObject(string fileName) { Console.WriteLine("Deserializing an instance of the object."); FileStream fs = new FileStream(fileName, FileMode.Open); XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(fs, new XmlDictionaryReaderQuotas()); DataContractSerializer ser = new DataContractSerializer(typeof(Person)); // Deserialize the data and read it from the instance. Person deserializedPerson = (Person)ser.ReadObject(reader, true); reader.Close(); fs.Close(); Console.WriteLine(String.Format("{0} {1}, ID: {2}", deserializedPerson.FirstName, deserializedPerson.LastName, deserializedPerson.ID)); } 

A chiunque si avvicini con un problema simile, a seconda della situazione, probabilmente stai meglio con NetDataContractSerializer. È un’alternativa a DataContractSerializer che registra i tipi .Net nell’XML rendendo la deserializzazione un gioco da ragazzi, poiché sa esattamente quali tipi sono coinvolti e quindi non è necessario dirgli quale tipo l’object radice è con il comando deserialize. E può produrre output in formato XML o binario (preferisco XML per facilitare il debugging).

Ecco alcuni esempi di codice per serializzare e deserializzare facilmente un object da e verso una stringa:

 private static object Deserialize(string xml) { object toReturn = null; using (Stream stream = new MemoryStream()) { byte[] data = System.Text.Encoding.UTF8.GetBytes(xml); stream.Write(data, 0, data.Length); stream.Position = 0; var netDataContractSerializer = new NetDataContractSerializer(); toReturn = netDataContractSerializer.Deserialize(stream); } return toReturn; } private static string Serialize(object obj) { using (var memoryStream = new MemoryStream()) using (var reader = new StreamReader(memoryStream)) { var netDataContractSerializer = new NetDataContractSerializer(); netDataContractSerializer.Serialize(memoryStream, obj); memoryStream.Position = 0; return reader.ReadToEnd(); } } 

Facile come torta!