All About KnownTypes



<This turned out to be longer than what I had intended. Sorry about it>


 


One of the common errors in Deserialization is “Element ‘‘http://mycompany.com/:shape’‘ contains data of the ‘http://mycompany.com/:Circle‘ data contract. The deserializer has no knowledge of any type that maps to this contract. Add the type corresponding to ‘Circle’ to the list of known types – for example, by using the KnownTypeAttribute attribute or by adding it to the list of known types passed to DataContractSerializer.” The following code will throw such an exception.


 


[DataContract(Namespace = “http://mycompany.com/”)]


public class Shape{…}


 


[DataContract(Namespace = “http://mycompany.com/”)]


public class Circle : Shape {…}


  


[ServiceContract]


public interface IMyServer


{


    [OperationContract]


    bool AddShape(Shape shape);


}


 


IMyServer client = new ChannelFactory<IMyServer>(binding, endPoint).CreateChannel();


client.AddShape(new Circle());


 


The contract specifies Shape but the client passes Circle a subtype of Shape. Since Circle is not a part of the contract the DataContractSerializer fails to deserialize.


 


Shared Contract vs Shared Type Model:


Remoting users would have never faced this problem. This is due to the fact that, in remoting, the CLR type name is serialized to the wire. The serialized type name is of the following format.


 


MyCompany.Library.Circle, MyAssembly, Version=2.0.0.0, Culture=neutral, PublicKeyToken=XXXXXX, processorArchitecture=MSIL


 


The reader uses the Assembly.Load API to load the assembly referred in the wire. In this approach the writer controls the type loaded by the reader. While this removes the hassle of the reader having to know the type being deserialized it has a couple of drawbacks.


1.      It disallows the writer and reader using different types which is common in an interoperable webservice scenario.


2.      Letting the writer load an arbitrary type into the reader’s appdomain is not a sound idea from security standpoint.


 


NetDataContractSerializer, shipped in WCF, supports sharing types as described above.  DataContractSerializer (which is used by default in WCF services) follows a different approach. Like the XmlSerializer (which is used by the ASMX webservices), the DataContractSerializer shares contracts instead of types. This allows the reader to control the deserializing type. From the webservice contract the reader specifies the type to deserialize. In the derived type scenario the contract name (i.e the XSD schema type name) is written on the wire. The contract name is a XML qualified name like ‘http://mycompany.com/:Circle‘.


 


While contract name is inferable from the CLR type the reverse is not possible since there can be multiple CLR types with the same contract name. To enable this reverse lookup the user needs to pass a set of types called the KnownTypes. The deserializer resolves the type qname in the wire using these types. It must be noted that unlike the remoting scenario the deserializer never loads the type based on the information from the wire.


 


Declaring Known types:


ASMX uses XmlInclude and SoapInclude attribute to denote known types. WCF allows more than one way to specify the known types.


 


1.      The base type can specify the derived as a known type using the KnownTypeAttribute as shown below.


[DataContract(Namespace = “http://mycompany.com/”)]



[KnownType(typeof(Circle))]


public class Shape{…}


 


2.      Known types can be specified in the config as shown below.


<configuration>


  <system.runtime.serialization>


    <dataContractSerializer>


      <declaredTypes>


         <add type=”MyCompany.Library.Shape,


              MyAssembly, Version=2.0.0.0, Culture=neutral,


              PublicKeyToken=XXXXXX, processorArchitecture=MSIL“>


            <knownType type=”MyCompany.Library.Circle,


                       MyAssembly, Version=2.0.0.0, Culture=neutral,


                       PublicKeyToken=XXXXXX, processorArchitecture=MSIL“/>


         </add>


      </declaredTypes>


    </dataContractSerializer>


  </system.runtime.serialization>


</configuration>


The config defines Circle as a known type for Shape is used. This is equivalent to defining KnownType in the base type as described in #1.


 


3.      Known types can also be passed to the constructor of the DataContractSerializer


 


4.      In service model known types can be added via the ServiceKnownType attribute along with either ServiceContract or OperationContract.


[ServiceContract]


public interface IMyServer


{


    [OperationContract]



[ServiceKnownType(typeof(Circle))]


    bool AddShape(Shape shape);


}


Declaring ServiceKnownType on an operation causes the known type to be applied for all the parameters for the operation. Declaring on the service contract causes known type to be applied for all the parameters of all the operations.


 


5.      The known types specified via ServiceKnownType attribute end up in the OperationDescription. Known types can also be added to OperationDescription.KnownTypes imperatively. Known types in the description are passed to the constructor of DataContractSerializer. These known types are also passed as extra types to XmlSerializer in the XmlSerializerFormat mode.


 


Known Type Scope:


Deserialization is a process of constructing the object tree. Known types passed to the constructor of DataContractSerializer apply to all nodes of the tree. A KnownType declared on a class not only applies to the class itself but for the entire subtree.


 


[DataContract(Namespace = “http://mycompany.com/”)]



[KnownType(typeof(Square))]


[KnownType(typeof(Circle))]


public class Shapes


{


  [DataMember]


  Shape[] shapeList;


}


 


In the above example the types Square and Circle are ‘in scope’ KnownType not only when Shapes is deserialized but also when the Shape[] is deserialized.


 


It is an error to have more than one KnownType with the same contract name at a given scope (i.e in the list of KnownTypes on a class or the types passed to the constructor of the DataContractSerializer). However there could be such duplication across scopes. In such cases the type in the inner most scope is used. To understand the scoping rules consider the class Shape being defined as follows.


 


[DataContract(Namespace = “http://mycompany.com/”)]



[KnownType(typeof(AnotherSquare))]


[KnownType(typeof(AnotherCircle))]


public class Shape


{


}


 


If AnotherSquare has the same contract name as Square then AnotherSquare will be used when the contract name of Square is seen on the wire.


 


To summarize the order in which known types are applied is shown below.



  1. Data contract primitive type

  2. Known types provided via KnownTypeAttribute from the inner most scope to the outer most.

  3. Known types passed to the constructor of the DataContractSerializer

  4. The declared member type.

  5. The root type passed to the constructor of DataContractSerializer

 


Known Types for Generic Types:


Consider Circle and Shape in the previous example to be generic type. Ideally one would want to define the known type as follows.


[DataContract(Namespace = “http://mycompany.com/”)]



[KnownType(typeof(Circle<>))] //NOT ALLOWED


public class Shape<T>{…}


Unfortunately the CLR does not allow generic types in attributes. Thus the above declaration will not work. Fortunately the DataContractSerializer supports another way to provide generic known type. Instead of specifying the known type directly in the KnownTypeAttrribute the user can also point a method from KnownTypeAttribute. The method can return an array of KnownTypes. This will allow generic known types as shown below.


 



    [KnownType(“GetKnownTypes”)]


    public class Shape<T>


    {



        static Type[] GetKnownTypes()


        {


            return new Type[] { typeof(Circle<T>) };


        }


    }


 


The method approach also allows the type authors to be able to control the known types dynamically at runtime. The method pointed by KnownType attribute must be static, take no parameters and return an IEnumerable<Type>. It is an error to mix a static known type with the method based dynamic known type for a given class.


 


ServiceKnownType supports a similar model to specify dynamic known types as shown below.


 


[ServiceContract]



[ServiceKnownType(Method = “GetKnownTypes”, Type = typeof(KnownTypeProvider))]


public interface IMyServer<T>


{


    [OperationContract]


    bool AddShape(Shape shape);


}


 


static class KnownTypesProvider


{


  static Type[] GetKnownTypes(ICustomAttributeProvider knownTypeAttributeTarget)


  {


    Type contractType = (Type)knownTypeAttributeTarget;


    return new Type[]{contractType.GetGenericArguments()[0]};


 


  }


}


 


Note that the attribute refers to a method name and a type name. The type name is needed since the ServiceKnownType attribute can go on interface or interface methods and the Known type method cannot be implemented in the interface.  The other difference between ServiceKnownType and KnownType is in the method definition. In ServiceKnownType the method takes an additional ICustomAttributeProvider as parameter. DataContractSerializer passes the ServiceContract class or MethodInfo of the OperationContract method when invoking this method.


 


Generic Types in Config


Generic known types can also be defined in config as shown below.


 


<configuration>


  <system.runtime.serialization>


    <dataContractSerializer>


      <declaredTypes>


         <add type=”MyCompany.Library.Shape`1,


              MyAssembly, Version=2.0.0.0, Culture=neutral,


              PublicKeyToken=XXXXXX, processorArchitecture=MSIL“>


            <knownType type=”MyCompany.Library.Circle`1,


                       MyAssembly, Version=2.0.0.0, Culture=neutral,


                       PublicKeyToken=XXXXXX, processorArchitecture=MSIL“>



                    <parameter index=”0″/>


            </knownType>


         </add>


      </declaredTypes>


    </dataContractSerializer>


  </system.runtime.serialization>


</configuration>


 


The above config specifies that the generic parameter for Circle is the same as the generic parameter for the declared type Shape. The config allows the definition of known type of arbitrary complexity. For example if it is needed to define Circle<Dictionary<string, T>> as the known type of Shape<T> (of course this is purely academic) it can be done as follows.


 


<configuration>


  <system.runtime.serialization>


    <dataContractSerializer>


      <declaredTypes>


         <add type=”MyCompany.Library.Shape`1,


              MyAssembly, Version=2.0.0.0, Culture=neutral,


              PublicKeyToken=XXXXXX, processorArchitecture=MSIL“>


            <knownType type=”MyCompany.Library.Circle`1,


                       MyAssembly, Version=2.0.0.0, Culture=neutral,


                       PublicKeyToken=XXXXXX, processorArchitecture=MSIL“>



                   <parameter type=”System.Collections.Generic.Dictionary`2″>


                      <parameter type=”System.String”/>


                      <parameter index=”0″/>


                   </parameter>                


            </knownType>


         </add>


      </declaredTypes>


    </dataContractSerializer>


  </system.runtime.serialization>


</configuration>


 


Note the use config element “parameter” with the attributes ‘type’ and ‘index’.


 


Common KnownType Scenarios:


In addition to the inherited scenario described earlier, there are some typical scenarios that require known types.



  1. When passing a DataContract class instance as an item to non generic collection class such as ArrayList. This due to the fact that ArrayList is projected as an array of objects.

  2. When passing a DataContract class instance as an item in ISerializable class such as System.Exception. This is due to the fact that ISerializable is an untyped (System.Object based) bag.

 


While these can also be solved by declaring known type, it is recommended to consider other approaches. Scenario #1 can be worked around by using a generic collection. Scenario #2 will never arise if the user follows the fault contract guidance and explicitly declare fault types as FaultContract and not use exception for faults. In general it is recommended to avoid ISerializable in a contract oriented application.


Comments (18)

  1. Hello Sowmy,

    1. Used XsdObjectGen to create my typed classes from an XSD file.

    2. Created a WCF Service with one OperationContract which returns one of the typed methods generated earlier.

    [OperationContract]

    SupplierCatalogue GetSupplierCatalogue(string ccountNumber)

    3. Hosted within IIS

    4. Generated client code within VS by "Add Service Reference"

    5. When I try to call the web method. I’m getting the following error (tracing is enabled)

    <Message>There was an error while trying to serialize parameter http://tempuri.org/:GetSupplierCatalogueResult. The InnerException message was ‘Type ‘Fujitsu.Catalogue.Feed.ProductlineItem’ with data contract name ‘ProductlineItem:http://schemas.datacontract.org/2004/07/Fujitsu.Catalogue.Feed‘ is not expected. 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.’.  Please see InnerException for more details.</Message>

    Inside my typed class, definition of ProductlineItem looks like

       [XmlType(TypeName = "ProductlineItem", Namespace = Declarations.SchemaVersion), Serializable]

       public class ProductlineItem

       {

    Schema roughly looks like

    SupplierCatalogue

    |

    |—CatalogueHeader

    |

    |——(Some header specif elements)

    |

    |—CatalogueDetail

    |

    |——ProductlineItem (0 to *)

    |

    |———-(Some product line item specific elements)

    Any quick thought, how I can fix it. Generating typed classes using xsd.exe didn’t help (even though it worked) because when we serialize the class the element names are different on the client side.

    I can send you the schema and generated file…..my email addr saravana(dot)kumar(at)digitaldeposit(dot)co(dot)uk

    Regards,

    Saravana

  2. Hi Sowmy

    Thanks for sharing this with us. It is working while retrieve a collection of objects, which has cyclic references.

    I implemented your solution and knocked down fetching issues I had before.

    Now I am getting into trouble, while sending back the modified collection. If I modify only the parent and set the child to null, it is great. If modify the child and parent or if I modify the child then I am getting into IndexOutOfrange issue. Apparently I increased maxarraylength and maxItemsinObjectGraph, and started getting a different error message, Object graph for type BusinessEntity contains cycles and cannot be serialized if reference tracking is disabled. And the stack trace is showing me some inner classes of System.Runtime.Serialization.XmlObjectSerializerWriteContext.OnHandleReference(XmlWriterDelegator xmlWriter, Object obj, Boolean canContainCyclicReference) and so on. Could you please help me out in this?

  3. One of the challenges encountered when using DataContract serialization (the default for WCF web services)

  4. One of the challenges encountered when using DataContract serialization (the default for WCF web services

  5. Lorsque vous créez un service WCF, vous allez créer un contrat. Dans la plupart des cas, ce contrat est

  6. Lorsque vous créez un service WCF, vous allez créer un contrat. Dans la plupart des cas, ce contrat est

  7. leolite1 says:

    Hi Sowmy,

    The article was quite informational but I did not understand the knownTypes definition under system.runtime.serialization. I tried including a declaration under system.runtime.serialization for a simple WCF service in a website for but it doesn’t seem to be respected.

    Thanks

  8. jy.boudreau says:

    Hi Sowny,

    Great article, thanks for sharing. One comment:

    You mention this as a common case for using KnownTypes:

    "2- When passing a DataContract class instance as an item in ISerializable class such as System.Exception. This is due to the fact that ISerializable is an untyped (System.Object based) bag."

    Which I totally agree is a common case, but I have to add that having ISerializable class being marshaled that is not an Exception is a very valid case. Later when you mention:

    "Scenario #2 will never arise if the user follows the fault contract guidance and explicitly declare fault types as FaultContract and not use exception for faults. In general it is recommended to avoid ISerializable in a contract oriented application."

    Here is where I disagree. What if the you don’t control the type you want to serialize? What if this type is part of some 3rd-party library. Then using the KnownTypes with generic parameters is crucial if that 3rd-party class embeds even a generic collection.

    In my case, the 3rd-party class was a class from the Microsoft Synchronization framework, which sadly uses ISerializable instead of DataContract.

  9. sunit_82 says:

    Hi sowmy,

    Excellent article.

    There is one question have though.

    What if the service method(Operation contract) takes a argument of type ‘object’ and from client we pass it any other ‘user defined’ type.

    In this case how can we manage using either KnownType attribute or config file?

    Thanx

    s.a.w

  10. Tim says:

    Hi,

    I tried all that and I am basically able to achieve the result without the following: I am consuming the service from Silverlight and I cannot have the types working dynamically, so the client is only aware of the types at the time when the proxy classes were generated from Add Service Reference from Visual Studio.

    Any idea how to solve that?

    Thanks in advance,

    Tim

  11. John Washburn says:

    I hope the code snippet below helps someone else as much as this article helped me:

    Method 2 is the best way.  Manual annotation of the base classes is a maintenance nightmare long term. But, you do not always have access to the call to construct the XmlSerializer (or DataContractSerializer) in order to provide a list of known types.

    Our application was a REST API with three parts Service, Common, Client where the bodies of the HTTP in the requests into and responses from to the service portion of the RESTful API are Xml serialized objects.  In this scenario the call to the constructor in the WCF auto-generated code (TypeLoader.cs) is not statically accessible to the developer of the REST API.

    Our solution was to use the ServiceKnownTypes annotation on the service contract.  The annotation resembles:

       [ServiceContract(Name = Contract.Identifier)]

       [ServiceKnownType("GetKnownTypes", typeof(KnownTypesProvider))]

       [XmlSerializerFormat]

       public interface IContract

       {

        // Service contract and WCF routing information here

       }

    And to create the method, GetKnownTypes, to enumerated the known types programmaticly via reflection.

       using System;

       using System.Collections.Generic;

       using System.Linq;

       using System.Reflection;

       using System.Text;

       using System.Xml.Linq;

       namespace Rest.API.Common

       {

         /// <summary>

         ///

         /// </summary>

         /// <note1>

         /// Somehow the XML serialization using XmlSerilaizer needs to be aware of all the classes to be serialized.

         ///

         /// As this article points out

         /// http://www.johnsoer.com/blog

         /// there are two ways to make the XmlSerializer aware of the type that it might be serailizing and deserializing.

         ///

         ///  1) Decorate appropriate classes with the information of derived classes that might be serailized. (Bad Idea and  maintanence nightmare long term)

         ///  2) Add a list of known types to the construction of the instances of XMLSerializer

         ///

         /// This implements Method 2; inform the XML serializer of all known types at the time the serializer is contructed.  

         /// This is dificult because the call to the XmlSerialize constructor is done within the code auto-generated by the WCF.

         /// But this auto generated code by the WCF (TypeLoader.cs)  provides a way for the code constructed by the WCF

         /// to ask the calling applicationfor a list of known types.

         ///

  12. John Washburn says:

    /// This line:

    ///

    ///     [ServiceKnownType("GetKnownTypes", typeof(KnownTypesProvider))]

    ///

    /// instructs WCF that when it creates an instance of XmlSerializer to call (via reflection) the

    /// method named: "foo", which is a method from type: "bar".  In the case of the REST API

    /// the line above results in a call to the method:

    ///

    ///     Type[] RestAPI.Common.KnownTypesProvider.GetKnownTypes(ICustomAttributeProvider)

    /// </note1>

    public class KnownTypesProvider

    {

    static Type[] GetKnownTypes(ICustomAttributeProvider knownTypeAttributeTarget)

    {

    List<Type> knownTypesFound = new List<Type>();

    // If this particular provider is a generic type (closed),

    // then return the types that are the arguments to this generic class

    var contractType = knownTypeAttributeTarget as Type;

    if (null != contractType)

    {

    var genericsFound = contractType.GetGenericArguments().ToList();

    if (genericsFound.Any())

    {

    knownTypesFound.AddRange(genericsFound);

    }

    }

    // Any type located within the this assembly (Rest.API.Common.DLL) is a candidate for serialization

    // by this Service Contract

    // But open generic classes located within this assembly are not serializable.  

    // See below for more on the enumerating the generics to be serialized.

    var commonAssembly = Assembly.GetAssembly(typeof(KnownTypesProvider));

    var allSupportedConcreteTypes = commonAssembly.GetTypes().Where(currType => !currType.IsGenericType).ToList();

    knownTypesFound.AddRange(allSupportedConcreteTypes);

    // What are concrete (aka closed) types used (or could be used) as parameters to the generic types

    // serialized under this Service Contract?

    var allSupportedGenericParameters = allSupportedConcreteTypes.ToList(); // Linq-ish way to clone list of types found in the assembl(y|ies)

    allSupportedGenericParameters.Add(typeof(XElement));

    // Add the closed forms for each of the generics that the REST API may serialize or deserialize

    // the parameter used to convert the open generic into a closed (aka concrete) type is the list

    // of supported generic parameters created above

    foreach (var currGenericParam in allSupportedGenericParameters)

    {

    knownTypesFound.Add(typeof(RestApiResponse<>).MakeGenericType(currGenericParam));

    knownTypesFound.Add(typeof(RestApiRequest<>).MakeGenericType(currGenericParam));

    }

    // The types used as generic parameters *WILL* be seraialized and deserialized.  

    // Add them to the list of known types. Duplicates if any are handled later

    knownTypesFound.AddRange(allSupportedGenericParameters);

    return knownTypesFound.Distinct().ToArray();

    }

    }

    }

  13. Burak says:

    Thanks for useful info dude, I finally understood the issue :)

  14. yilmaz says:

    Hi, could you attach source code ?

    Thanks for the article,