How to serialize class type but not the namespace to a Json string using DataContractJsonSerializer

12,117

Solution 1

Adding the namespace parameter to the data contract does the trick. [DataContract(Namespace = "")]

Solution 2

This page describes the circumstances under which the __type property is emitted. In short, in WCF, if you use a derived type, and a KnownTypeAttribute, then you're going to get a __type property.

Example:

Assume

[DataContract]
[KnownType(typeof(Subscriber))]
public class Person { ... }

[DataContract]
public class Subscriber : Person { ... } 

This code generates a __type property:

    var o = new Subscriber("Fleming");
    var serializer = new DataContractJsonSerializer(typeof(Person));
    serializer.WriteObject(Console.OpenStandardOutput(), o);

But this code does not:

    var o = new Subscriber("Fleming");
    var serializer = new DataContractJsonSerializer(typeof(Subscriber));
    serializer.WriteObject(Console.OpenStandardOutput(), o);

Notice that the second snip uses a DCJS with the same type as the object being serialized.

To avoid the __type, don't use derived types, or to be precise, use a serializer typed to the type you are actually serializing. If the serialization is being performed implicitly by a WCF method, then the method must be typed appropriately. In my example, it means you must use a return type of "Subscriber", and not the parent type, "Person".

The __type is emitted into the JSON stream by the (private) WriteServerTypeAttribute method on the (internal) System.Runtime.Serialization.Json.XmlJsonWriter class. There is no public, documented, supported way to modify that, as far as I can tell.

To avoid this, you'd maybe need to return a string from the WCF method, perform the serialization yourself, and post-process the emitted JSON.


If you don't mind the __type thing, but just want to remove the qualifying namespace from the value, then put your types in the global namespace. In other words, put them outside of any namespace declaration in code.

Example: When the data types reside in a namespace, and when I used a derived type, the serialized JSON looks like this:

{
  "__type":"Subscriber:#My.Custom.Namespace",
  "Index":604455,
  "Name":"Fleming",
  "Id":580540
}

When the data types reside in the global namespace, it looks like this:

{
  "__type":"Subscriber:#",
  "Index":708759,
  "Name":"Fleming",
  "Id":675323
}

Solution 3

Cheeso's answer was excellent. I did discover a refinement to cleaning up the __type field though:

Rather than removing your subclass from its namespace you can add a property like the following:

[DataMember(Name = "__type")]
public string SubclassType
{
    get
    {
        return "Subscriber";
    }
    set { }
}

You still get stuck with the ugly name "__type" but I found that because I was returning a list of subtypes I wanted to specify the type name anyway. You could even return a value of "" to further reduce response size. You could also just declare the property as:

public string __type

but I found that to accentuate the hack so I stuck with an appropriate property name and then renamed it.

-Joey

Share:
12,117

Related videos on Youtube

rony l
Author by

rony l

I really love programming, so it's so cool I get paid for it!

Updated on May 28, 2022

Comments

  • rony l
    rony l almost 2 years

    I'm trying to serialize a class hierarchy to a Json string using DataContractJsonSerializer, in a WCF service. the default behaviour for serializing a derived class is to add the following key value pair to the object:

    "__type":"ClassName:#Namespace"

    My problem is that namespaces are long and they bloat the Json string. I would like to somehow intervene with the serialization and output this instead:

    "__type":"ClassName"

    and on deserialization intervene again to point to the correct namespace (which i know in runtime).

    Is there any way to do such a thing?

  • dlchambers
    dlchambers almost 13 years
    This suppressed the namespace part of the attribute, but not the colon that trails the classname. So, +1 for some savings, but still not a complete solution.
  • Nyerguds
    Nyerguds over 7 years
    What if the problem is subtypes used inside that object, though?
  • Nyerguds
    Nyerguds over 7 years
    Note that you need to call it recursively if you have more than one level of custom objects in the full file. In the end, I just stripped it with a regex: "\"__type\"\\s*:\\s*\"[^\"]+\"\\s*,?\\s*"
  • Thorkil Værge
    Thorkil Værge about 5 years
    I found that this solution requires the "__type" field to be the first element in the JSON serialization. This breaks with the JSON specification, but more importantly, I now have to find a way to order the fields in the serialization.