2009-08-18 1 views
28

Vorrei invocare XmlSerializer.Deserialize passandolo a XDocument. Può richiedere uno Stream, uno XmlReader o uno TextReader.Utilizzare XDocument come origine per XmlSerializer.Deserialize?

È possibile generare uno dei precedenti da XDocument senza effettivamente scaricare lo XDocument in un archivio intermedio, ad esempio MemoryStream?

Sembra che quello che sto cercando sia un'implementazione di XmlReader che funzioni con uno XDocument. Non riesco a trovarne uno però.

+0

Vedi anche http://stackoverflow.com/q/7901558/11912 –

risposta

43

È possibile utilizzare XDocument.CreateReader() per creare un XmlReader che legge il contenuto di XDocument.

Equivalentemente, anche il seguente funzionerà.

XmlReader GetReader(XDocument doc) 
{ 
    return doc.Root.CreateReader(); 
} 
0

solo pensato Vorrei aggiungere che dopo aver creato il XmlReader, vale a dire:

XmlSerializer serializer = new XmlSerializer(typeof(MyObject)); 
XmlReader reader = xmlDocumentToDeserialize.CreateReader(); 

allora si dovrebbe chiamare:

reader.MoveToContent(); 

perché altrimenti il ​​lettore non "punto" per il primo nodo, causando la comparsa di un lettore vuoto! Quindi puoi tranquillamente chiamare Deserialize:

MyObject myObject = (MyObject)serializer.Deserialize(reader); 
9

Ecco un'utilità per serializzare e deserializzare oggetti da/per XDocument.

XDocument doc = SerializationUtil.Serialize(foo); 
Foo foo = SerializationUtil.Deserialize<Foo>(doc); 

Ecco la classe:

public static class SerializationUtil 
{ 
    public static T Deserialize<T>(XDocument doc) 
    { 
     XmlSerializer xmlSerializer = new XmlSerializer(typeof(T)); 

     using (var reader = doc.Root.CreateReader()) 
     { 
      return (T)xmlSerializer.Deserialize(reader); 
     } 
    } 

    public static XDocument Serialize<T>(T value) 
    { 
     XmlSerializer xmlSerializer = new XmlSerializer(typeof(T)); 

     XDocument doc = new XDocument(); 
     using (var writer = doc.CreateWriter()) 
     { 
      xmlSerializer.Serialize(writer, value); 
     } 

     return doc; 
    } 
} 
3

(Allegato alla risposta di Steve Guidi)

Per quanto posso vedere non v'è alcuna implementazione di XmlReader si può facilmente utilizzare con XDocument senza spostare il Contenuto XML attraverso un negozio intermedio come una rappresentazione in stringa dell'XML e che supporta tutti i tipi che ad esempio supporta System.Xml.XmlNodeReader.

Il lettore restituito da XDocument.CreateReader (che è un System.Xml.Linq.XNodeReader, una classe interna) è un XmlReader e funziona per la maggior parte del documento XML, ma non con i documenti che hanno elementi di dati binari, perché la sua attuazione does not support Base64 or BinHex data:

Base64 e I dati BinHex non sono supportati. Se si tenta di recuperare questi tipi di dati (ad esempio, chiamando ReadElementContentAsBase64), il lettore genererà NotSupportedException.

Per questo lettore XDocument.CreateReader().CanReadBinaryContent è false in contrasto con la System.Xml.XmlNodeReader.

Per esempio, questo programma genera un'eccezione:

public class SomeTest 
{ 
    public byte[] BinaryTest { get; set; } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     XDocument document = new XDocument(
      new XElement("SomeTest", 
       new XElement("BinaryTest", "VGVzdA=="))); 

     using (var reader = document.CreateReader()) 
     { 
      var serializer = new XmlSerializer(typeof(SomeTest)); 
      var someTest = serializer.Deserialize(reader) as SomeTest; 
      // NotSupportedException here (as inner exception) 
     } 
    } 
} 

Tuttavia, l'estrazione del XML come string e passando come TextReader nel serializzatore funziona:

 using (var reader = new StringReader(document.ToString())) 

mi interesserebbe pure se c'è un altro modo per deserializzare un XDocument che include dati binari senza prima convertirlo in una stringa.

+0

speravo di trovare una soluzione per questo. Ho del contenuto xml con dati binari, che renderà tutto molto più lento. lo sgambio attorno a una corda sembra folle. Penso che potrei provare e negoziare abbandonando del tutto il contenuto di byte [], piuttosto che prendere il colpo della performance. – Jim

0

Mi piace la risposta di @Simon_Weaver al meglio. Sulla base di che questa è la mia sintesi:

using System; 
using System.Xml.Linq; 
using System.Xml.Serialization; 

namespace XDocSerialization 
{ 
    [TestClass] 
    public class Tests 
    { 
     [TestMethod] 
     public void Tests_SerializeToXDoc() 
     { 
      var sheep = new Animal 
      { 
       Name = "Sheep", Legs = 4, Nutrition = Nutrition.Herbivore 
      }; 
      var xdoc = sheep.SerializeToXDoc(); 
      var ser = "<Animal " + 
         "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " + 
         "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\r\n " + 
         "<Name>Sheep</Name>\r\n <Legs>4</Legs>\r\n " + 
         "<Nutrition>Herbivore</Nutrition>\r\n</Animal>"; 

      Assert.AreEqual(xdoc.ToString(), ser); 
      Assert.IsInstanceOfType(xdoc, typeof(XDocument)); 
     } 

     [TestMethod] 
     public void Tests_DeserializeFromXDoc() 
     { 
      var Sheep = new Animal 
      { 
       Name = "Sheep", Legs = 4, Nutrition = Nutrition.Herbivore 
      }; 
      var des = Sheep.SerializeToXDoc().DeserializeFromXDoc<Animal>(); 

      Assert.AreEqual(des.Name, Sheep.Name); 
      Assert.AreEqual(des.Nutrition, Sheep.Nutrition); 
      Assert.AreEqual(des.Legs, Sheep.Legs); 
      Assert.AreNotSame(des, Sheep); 
     } 
    } 

    public static class ExtensionMethods 
    { 
     public static T DeserializeFromXDoc<T>(this XDocument source) 
     { 
      if (source == null || source.Root == null) 
       return default(T); 

      using (var reader = source.Root.CreateReader()) 
       return (T)new XmlSerializer(typeof(T)).Deserialize(reader); 
     } 

     public static XDocument SerializeToXDoc<T>(this T source) 
     { 
      if (source == null) 
       return null; 

      var doc = new XDocument(); 
      using (var writer = doc.CreateWriter()) 
       new XmlSerializer(typeof(T)).Serialize(writer, source); 

      return doc; 
     } 
    } 

    [Serializable] 
    public class Animal 
    { 
     public string Name { get; set; } 
     public int Legs { get; set; } 
     public Nutrition Nutrition { get; set; } 
    } 

    public enum Nutrition 
    { 
     Herbivore, 
     Carnivore, 
     Omnivore 
    } 
}