views:

229

answers:

1

I am using the WCF JSON serializer to produce JSON for use as return data for the ASP.NET MVC framework. I am doing this because the built-in JsonAction does not provide any way to control the naming of public properties in the serialized JSON.

public override void ExecuteResult(ControllerContext context)
{
    ...

    if (Data != null)
    {
        var serializer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(Data.GetType());
        System.IO.MemoryStream ms = new System.IO.MemoryStream();

        serializer.WriteObject(ms, this.Data);

        response.Write(Encoding.Default.GetString(ms.ToArray()));
    }
}

In this example, I am using this with OpenFlashChart, so I set this.Data to a PieChart instance. It worked fine. Then, I set this.Data to an instance of Chart, and I got the following exception:

Type 'OpenFlashChart.Pie' with data contract name 'Pie:http://schemas.datacontract.org/2004/07/OpenFlashChart' 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.

Obviously, when I gave the serializer a PieChart element, it was able to infer that I needed the Pie class as well. Why when I provide it Chart<PieChart> is it no longer looking at the classes used by PieChart? Is there a way to get around this issue without having to annotate everything with KnownTypeAttributes?

+1  A: 

I've edited my previous answer because I've just realised the problem here.

I think problem is because the type being serialized is generic, you have to add any generic parameter types to the DataContractJsonSerializer's known types.

If I'm right, then you can still do this without annotating.

Firstly, call the constructor on DataContractJsonSerializer which accepts a Type and an IEnumerable<Type>.

Then change how you construct the serializer to something like this:

DataContractJsonSerializer serializer = null;
Type dataType = Data.GetType();
if(dataType.IsGenericType)
    serializer = new DataContractJsonSerializer(dataType, dataType.GetGenericArguments());
else
    serializer = new DataContractJsonSerializer(dataType);

That would be my next step.

Andras Zoltan
While I find it ridiculous that the serializer doesn't do this on its own, you are correct.
David Pfeffer
Sorry, your comment is probably based on my original answer, which I think was slightly wider of the mark (although it would have worked in this case).I've since changed my answer to provide what I hope will be a more resilient solution which will also not require you annotating everything in advance.KnownTypes are an issue with WCF serialization, but they're there because the whole type information is not persisted (making the XML/Json more readable and compliant) unlike with standard Serialization.
Andras Zoltan
Oh, you edited your comment as well!I tried to edit mine again, but missed the time limit.Bottom line - you're right; whilst it makes sense in many cases that KnownTypes are required, in this particular case a minor alteration to the constructor code to deal with generics by the WCF team could solve all these problems.Glad it worked, anyway :)
Andras Zoltan