I have the following code:
[DataContract]
class TestContract {
private String _Name;
private Int32 _Age;
[DataMember( Name = "Name" )]
public String Name {
get { return _Name; }
set { _Name = value; }
}
[DataMember( Name = "Age" )]
public Int32 Age {
get { return _Age; }
set { _Age = value; }
}
}
[Serializable]
public class DNCJsonDictionary<K, V> : ISerializable {
Dictionary<K, V> dict = new Dictionary<K, V>();
public DNCJsonDictionary() { }
protected DNCJsonDictionary( SerializationInfo info, StreamingContext context ) {
}
public void GetObjectData( SerializationInfo info, StreamingContext context ) {
foreach( K key in dict.Keys ) {
info.AddValue( key.ToString(), dict[ key ] );
}
}
public void Add( K key, V value ) {
dict.Add( key, value );
}
public V this[ K index ] {
set { dict[ index ] = value; }
get { return dict[ index ]; }
}
}
public class MainClass {
public static String Serialize( Object data ) {
var serializer = new DataContractJsonSerializer( data.GetType() );
var ms = new MemoryStream();
serializer.WriteObject( ms, data );
return Encoding.UTF8.GetString( ms.ToArray() );
}
public static void Main() {
DNCJsonDictionary<String, Object> address = new DNCJsonDictionary<String, Object>();
address[ "Street" ] = "30 Rockefeller Plaza";
address[ "City" ] = "New York City";
address[ "State" ] = "NY";
TestContract test = new TestContract();
test.Name = "CsDJ";
test.Age = 28;
DNCJsonDictionary<String, Object> result = new DNCJsonDictionary<String, Object>();
result[ "foo" ] = "bar";
result[ "Name" ] = "John Doe";
result[ "Age" ] = 32;
result[ "Address" ] = address;
// ** --- THIS THROWS AN EXCEPTION!!! --- **
result[ "test" ] = test;
Console.WriteLine( Serialize( result ) );
Console.ReadLine();
}
}
When I run, I get this exception:
Type 'Json_Dictionary_Test.TestContract' with data contract name 'TestContract:http://schemas.datacontract.org/2004/07/Json_Dictionary_Test' 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.
But I don't understand that! As I know, KnownTypeAttribute is used just in deserialization, and if there is inheritance, isn't it? But here is just serialization. And without the datacontract member works fine.
Any idea?
I figured out something that works! There is a parent class with a KnownTypes list, which I fill with all child classes and that will be used in serialization:
[DataContract]
[KnownType( "GetKnownTypes" )] // for serialization
class ResultContract {
private static List<Type> KnownTypes { get; set; }
public static List<Type> GetKnownTypes() {
return KnownTypes;
}
static ResultContract() {
KnownTypes = new List<Type>();
try {
foreach( Type type in Assembly.GetExecutingAssembly().GetTypes() ) {
if( !type.IsAbstract && type.IsSubclassOf( typeof( ResultContract ) ) ) {
KnownTypes.Add( type );
}
}
} catch( Exception ex ) {
Console.WriteLine( "Fatal error!" );
}
}
}
[DataContract]
class TestContract : *ResultContract* {
...
}
...
Add this line:
[KnownType(typeof(TestContract))]
So that
[Serializable]
[KnownType(typeof(TestContract))]
public class DNCJsonDictionary<K, V> : ...
This is a known issue. That is why generics does not quite work with WCF.
But the reason is easy, WCF is supposed to create WSDL and be able to publish your contract. It is all well and good to use Generics to define your contract but WSDL needs to point to some concrete class hence you need KnownType
.