DataContract

De Banane Atomic
Aller à la navigationAller à la recherche

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

Csharp.svg
// 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.

Csharp.svg
// 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

Csharp.svg
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

Csharp.svg
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.

Csharp.svg
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.

Csharp.svg
[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

Csharp.svg
[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; }
Xml.png
<!-- 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

Csharp.svg
[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.png
<?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">
Csharp.svg
// 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 {}