Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Atteint de JavaScriptite Aiguë [Cyril Durand]

Expert ASP.net Ajax et WCF, Cyril Durand parle dans son blog de point techniques sur ASP.net, ASP.net Ajax, JavaScript, WCF et .net en général. Cyril est également consultant indépendant, n'hésitez pas à le contacter pour de l'assistance sur vos projets

Actualités

  • Blog de Cyril DURAND, passionné de JavaScript, Ajax, ASP.net et tout ce qui touche au developpement Web Client-Side.

    N'hésitez pas à me contacter pour vos projets .net : architecture, accompagnement, formation, ...

    View Cyril Durand's profile on LinkedIn
    hit counters


    Expertise Commerce server et BizTalk

DataContractSerializer et DataContractResolver sérialisation d’un type abstrait sans KnownType

Lorsque l’on utilise le DataContractSerializer, il arrive que l’on souhaite sérialiser un type abstrait sans connaitre l’implémentation concrète.

Exemple :

[DataContract]
abstract class Foo
{ }
 
[DataContract]
class Bar : Foo
{ }
 
class Program
{
    static void Main(string[] args)
    {
 
        DataContractSerializer serializer = new DataContractSerializer(typeof(Foo));
        using (MemoryStream ms = new MemoryStream())
        {
            serializer.WriteObject(ms, new Bar());
        }
    }
}

Le code ci-dessus va déclencher une exception de type SerializationException avec comme message :

Type 'ConsoleApplication8.Bar' with data contract name 'Bar:http://schemas.datacontract.org/2004/07/ConsoleApplication8' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer

Le problème est que le DataContractSerializer n’est pas capable de sérialiser un type s’il ne connait pas l’ensemble des types à sérialiser.

Une des solutions pour résoudre ce problème est d’utiliser les KnownType. Je vous avais d’ailleurs parlé des KnownType avec WCF il y a quelques temps (WCF : KnownType - Comment retourner des types enfants du type définit dans le ServiceContract)

Il existe plusieurs façons de faire:

  • soit en modifiant l’instanciation du DataContractSerializer :
DataContractSerializer serializer = new DataContractSerializer(typeof(Foo), new Type[] { typeof(Bar) });
  • soit en rajoutant des attributs
[DataContract]
[KnownType(typeof(Bar))]
abstract class Foo
{ }
  • soit en fournissant une méthode retournant les KnownTypes
[DataContract]
[KnownType("GetKnownTypes")]
abstract class Foo
{
    public static IEnumerable<Type> GetKnownTypes()
    {
        yield return typeof(Bar);
    }
}
<configuration>
  <system.runtime.serialization>
    <dataContractSerializer>
      <declaredTypes>
         <add type="ConsoleApplication8.Foo, ConsoleApplication8">
            <knownType type="ConsoleApplication8.Bar, ConsoleApplication8"/>
         </add>
      </declaredTypes>
    </dataContractSerializer>
  </system.runtime.serialization>
</configuration>

Ces solutions sont intéressantes mais elles nécessitent de connaitre le type concret, or, il peut arriver que l’on ne le connaisse pas.

Mais pourquoi le DataContractSerializer a besoin de connaitre l’ensemble des types qu’il va sérialiser ?

Le DataContractSerializer a besoin de connaitre l’ensemble des types pour plusieurs raisons. D’une part lors de la sérialisation, comment transmettre le type Bar à une autre application alors que celle-ci attends le type Foo ? D’autre part, lors de la désérialisation, que faire si l’on m’envoie un type Bar alors que j’attends un Foo? ? ignorer les membres qui ne sont pas de Foo ? lever une exception ? Ce sont quelques une des raisons pour lesquelle le DataContractSerializer nécessite de connaitre l’ensemble des types qu’il va sérialiser / déserializer.

Cependant, lorsque l’on regarde les différentes signatures du constructeur du DataContractSerializer, on voit une signature qui prend un DataContractResolver. Un DataContractResolver permet d’expliquer au DataContractSerializer comment sérialiser/déserialiser un type.

Le type DataContractResolver est une classe abstraite possédant 2 méthodes :

  • ResolveName : permet de retrouver un type à partir d’un String
  • TryResolveType : permet de retourner un String à partir d’un type

Ce qui nous donne le code suivant :

/// <remarks>May not correctly if an assembly is signed and 2 version of the same type live in the AppDomain</remarks>
public class LazyContractResolver : DataContractResolver
{
  private static Regex URI_REGEXP = new Regex("^uri:(.*),(.*)$", RegexOptions.Compiled);
 
  public override Type ResolveName(String typeName, String typeNamespace, Type declaredType, 
                   DataContractResolver knownTypeResolver)
  {
    Type t = knownTypeResolver.ResolveName(typeNamespace, typeNamespace, declaredType, knownTypeResolver);
    if (t == null && typeNamespace.StartsWith("uri:"))
    {
      Match match = LazyContractResolver.URI_REGEXP.Match(typeNamespace);
      if (!match.Success)
      {
        throw new ArgumentException(String.Format("uri is not valid. uri : '{0}'", typeNamespace));
      }
      String namespaceName = match.Groups[1].Value;
      String assemblyName = match.Groups[2].Value;
 
      t = Type.GetType(String.Format("{0}.{1},{2}", namespaceName, typeName, assemblyName));
    }
    return t;
  }
 
  public override Boolean TryResolveType(Type type, Type declaredType, DataContractResolver knownTypeResolver, 
                      out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace)
  {
    Boolean isTypeResolved = knownTypeResolver.TryResolveType(type, declaredType, null, out typeName, out typeNamespace);
    if (!isTypeResolved)
    {
      XmlDictionary dictionary = new XmlDictionary();
      typeName = dictionary.Add(type.Name);
      typeNamespace = dictionary.Add(String.Format("uri:{0},{1}", type.Namespace, type.Assembly.GetName().Name));
      isTypeResolved = true;
    }
    return isTypeResolved;
  }
}

Voici un exemple permettant d’utiliser le LazyContractResolver :

[DataContract]
abstract class Foo
{ }
 
[DataContract]
class Bar : Foo
{ }
 
class Program
{
  static void Main(string[] args)
  {
    DataContractSerializer serializer = 
      new DataContractSerializer(typeof(Foo), null, int.MaxValue, true, true, 
                                 null, new LazyContractResolver());
    using (MemoryStream ms = new MemoryStream())
    {
      serializer.WriteObject(ms, new Bar());
    }
  }
}

Et vous, avez-vous déjà été confronté à des problèmes de KnownTypes ? Avez-vous déjà utilisé un DataContractResolver ?

Posted: mercredi 29 février 2012 02:32 par cyril
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :

Commentaires

Pas de commentaires

Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- [ #Yammer ] From Mailbox to Yammer and back / De votre messagerie vers Yammer et retour ! par Le blog de Patrick [MVP SharePoint] le 09-15-2014, 11:31

- [ #Office 365 ] New service settings panel / Nouveau panneau de paramétrage des services par Le blog de Patrick [MVP SharePoint] le 09-11-2014, 08:50

- Problème de déploiement pour une démo SharePoint/TFS? par Blog de Jérémy Jeanson le 09-10-2014, 21:52

- [ #Office365 ] Delve first impressions / Premières impressions sur Delve par Le blog de Patrick [MVP SharePoint] le 09-09-2014, 16:57

- [ #Office365 ] How to change Administration console language ? / Comment changer la langue de la console d’administration ? par Le blog de Patrick [MVP SharePoint] le 09-09-2014, 08:25

- [ #SharePoint 2013 ] Suppression de bases de données en état “Pas de Réponse” par Le blog de Patrick [MVP SharePoint] le 09-04-2014, 14:10

- Changer l’adresse d’une ferme Office Web Apps associée à SharePoint par Blog de Jérémy Jeanson le 09-01-2014, 22:21

- Une ferme #SharePoint 2013 dans @Azure en quelques clics (1ère partie) ! par Le blog de Patrick [MVP SharePoint] le 08-28-2014, 18:52

- SharePoint 2013: Préparation de la migration - Création des site Templates dans 2010 et 2013 par Blog Technique de Romelard Fabrice le 08-20-2014, 16:31

- [ #Yammer ] How to change interface language ? Comment changer la langue de l’interface ? par Le blog de Patrick [MVP SharePoint] le 08-20-2014, 14:21