DataContract
Moteur de sérialisation de WCF.
Utilise l'assembly System.Runtime.Serialization.
Liens
DataContractSerializer vs XmlSerializer
+ |
sérialise les propriétés et attributs marqués [DataMember] même s'ils ne sont pas public
|
+ |
permet de sérialiser les classes implémentant IDictionary, ce que ne permet pas XmlSerializer
|
+ |
sérialise uniquement les propriétés et attributs marqués [DataMember], là où XmlSerializer sérialise tout ce qui n'est pas exclu avec [XmlIgnore]
|
+ |
possibilité de spécifier l'ordre de sérialisation avec [DataMember(Order = 0)]
|
+ |
meilleurs performances que XmlSerializer
|
- |
une classe marquée [DataMember] ne peut hériter que de classes elles aussi marquées [DataMember]
|
Exemple
|
// On déclare les types (hors types de base) des membres qui sont à sérialiser
[KnownType(typeof(Classe2))]
// IsReference donne un id à l'objet, ainsi s'il est présent de multiple fois
// dans le xml seul son id est recopié et pas tous le xml qui lui est associé.
[DataContract(IsReference = true)]
public class Classe1
{
[DataMember] // Définit comme propriété à sérialiser
public Classe2 Classe2 { get; set; }
// Non-définit comme propriété à sérialiser
public Classe3 Classe3 { get; set; }
// Comme Classe3 n'est pas sérialisé et que lors de la désérialisation le
// constructeur de la classe n'est pas appelé, la propriété Classe3 sera
// à null sauf si on l'initialise lors de l'événement de désérialisation
[OnDeserialized]
void MethodOnDeserialized(StreamingContext context)
{
Classe3 = new Classe3();
}
// Cette méthode sera lancée juste avant la déserialisation.
// Alors que la méthode OnDeserialized sera lancée juste après.
[OnDeserializing]
void MethodOnDeserializing(StreamingContext context)
{ }
}
// Permet de redéfinir une collection !?
[CollectionDataContract]
public class MegaList : List<int>
{ }
|
Attention DataContract ne permet pas de sérialiser les propriétés contenues dans un type Collection.
|
// Ici les éléments de la liste seront bien sérialisés mais pas Propriété
[CollectionDataContract]
public class MegaList : List<int>
{
[DataMember]
public string Propriété { get; set; }
}
// Une solution est d'inclure la liste dans la classe plutôt que d'en hériter.
// Implémenter l'interface IList pour conserver le comportement de List.
[DataContract]
public class MegaList
{
[DataMember]
public string Propriété { get; set; }
[DataMember]
public List<int> List { get; set; }
}
|
Sérialisation
|
var serializer = new DataContractJsonSerializer(dataToSerialize.GetType()); // JSON
var serializer = new DataContractSerializer(dataToSerialize.GetType()); // XML
using (var stream = new FileStream(destinationFilePath, FileMode.Create)) // dans un fichier. FileMode.Create : create or override
using (var stream = new MemoryStream()) // dans un string
{
using (var writer = XmlDictionaryWriter.CreateTextWriter(stream)) // XML
using (var writer = XmlWriter.Create(stream, new XmlWriterSettings() { Indent = true })) // XML avec indentation
using (var writer = XmlDictionaryWriter.CreateMtomWriter(stream, Encoding.UTF8, int.MaxValue, "")) // MTOM
using (var writer = XmlDictionaryWriter.CreateBinaryWriter(stream)) // Binary
{
serializer.WriteObject(writer, dataToSerialize);
// écrire le MemoryStream dans le string
writer.Flush();
dataString = Encoding.UTF8.GetString(stream.ToArray(), 0, (int)stream.Length);
}
}
|
Désérialisation
|
using (var stream = new FileStream(dataFilePath, FileMode.Open)) // depuis un fichier
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(dataString))) // depuis un string
{
using (var reader = XmlDictionaryReader.CreateTextReader(stream, XmlDictionaryReaderQuotas.Max))
{
var dataContractSerializer = new DataContractSerializer(typeof(MyData));
var myData = dataContractSerializer.ReadObject(reader) as MyData;
}
}
|
Attributs DataContract, DataMember
Par défaut
Si la classe à sérialiser n'est pas décorée avec l'attribut DataContract, toutes les propriétés public sont sérialisées.
|
public class MaClasse
{
// sérialiser
public int P1 { get; set; }
[IgnoreDataMember] // ne pas sérialiser
public string P2 { get; set; }
|
|
DataMember peut être utilisé même si DataContract ne décore pas la classe. |
Attribut DataContract
Si l'attribut DataContract décore la classe, toutes les propriétés et champs décorées avec DataMember seront sérialisées, même s'ils sont private.
|
[DataContract]
public class MaClasse
{
[DataMember] // sérialiser
public int P1 { get; set; }
[DataMember] // sérialiser
private int _p2 { get; set; }
// ne pas sérialiser
public string P3 { get; set; }
[DataMember(Name = "property3")] // changer le nom à sérialiser
public int P4 { get; set; }
|
CollectionDataContract
|
[CollectionDataContract(ItemName = "SItem")]
public class SuperList : List<int> {}
[CollectionDataContract(KeyName = "SKey", ValueName = "SValue")]
public class SuperDico : Dictionary<int, string>
public class MaClasse
{
public SuperList SList { get; private set; }
public SuperDico SDico { get; private set; }
|
|
<!-- Sans CollectionDataContract -->
<SList xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<d2p1:int>1</d2p1:int>
</SList>
<SDico xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<d2p1:KeyValueOfintstring>
<d2p1:Key>1</d2p1:Key>
<d2p1:Value>un</d2p1:Value>
</d2p1:KeyValueOfintstring>
<!-- Avec CollectionDataContract -->
<SList>
<SItem>1</SItem>
<int>1</int> <!-- si ItemName n'a pas été définit, le type est utilisé -->
</SList>
<SDico>
<KeyValueOfintstring>
<SKey>1</SKey>
<SValue>un</SValue>
</KeyValueOfintstring>
|
Polymorphisme
|
[KnownType(typeof(InhertedClass))] // lister tous les types pouvant hériter de ParentClass
[KnownType("GetDerivedTypes")] // appeler une méthode qui lister tous les types pouvant hériter de ParentClass
public class ParentClass
{
public int P1 { get; set; }
public static IEnumerable<Type> GetDerivedTypes()
{
return typeof(ParentClass).Assembly.GetTypes().
Where(type => typeof(ParentClass).IsAssignableFrom(type));
}
}
public class InhertedClass : ParentClass
{
public int P2 { get; set; }
}
// utiliser le type parent pour le serializer
var dataContractSerializer = new DataContractSerializer(typeof(ParentClass));
|
Namespace
|
<?xml version="1.0" encoding="utf-8"?>
<MaClasse xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.datacontract.org/2004/07/MonNamespace">
|
|
// définit le xmlns par défaut pour les assembly de MonNamespace
[assembly:ContractNamespace("http://www.domaine.fr/service/v1.0.0", ClrNamespace = "MonNamespace")]
// force le xmlns pour MaClasse
[DataContract(Namespace = "http://www.domaine.fr/service/v2.0.0")]
public class MaClasse {}
|