How to not serialize the __type property on JSON objects
Asked Answered
A

18

69

Every object I return from a WebMethod of a ScriptService is wrapped into a JSON object with the data in a property named d. That's ok. But I don't want the additional __type property to be served to the client, since I do manual processing with jQuery.

Is it possible?

Argot answered 9/3, 2009 at 17:59 Comment(2)
ASP.NET 3.5 SP1, C# server side jQuery client side, doing $.ajax callsArgot
None of these solutions worked for me I still have __type in my response. Does anyone had any more solution?Stilbestrol
B
40

I found that if I make the default constructor of my class that my webmethod returns anything other than public it will not serialize the __type:ClassName portion.

You may want to declare your default constructor protected internal ClassName() { }

Bogosian answered 22/4, 2009 at 16:9 Comment(8)
I'm not getting this result, though I'm returning an array of objects. Not sure if this is the issue.Sol
Worked like a charm. The simple addition of "protected internal ClassName(){} " more than halved the amount of data in the response json object.Ingenious
Any solutions for if you can't make it non-public, and your object comes from a different assembly so it can't be internal? I didn't have __type for the longest time, and now it's cropping up occasionally, even though I've defined my own JavaScriptConverter :/Zoezoeller
@Catch22: How does it halved the amount of data in the response json object?? how many objects and how long is you __type??Slapup
@Naor: My JS object contained an array with about 1000 simple objects with two properties (ID, Name). Then if__type=namespace.name is added (1000 times), that about doubles the amount of data being transferred.Ingenious
This isn't working for me. Can someone post some sample code?Viki
For me it also doesn't work... still have this __type property.Origen
could not reproduce that behaviour eitherTrygve
A
25

John's solution didn't work for me as the type I'm returning is in a seperate DLL. I have full control over that DLL but I can't construct my return type if the constructor is internal.

I wondered if the return type being a public type in a library might even be the cause - I've been doing a lot of Ajax and not seen this one before.

Quick tests:

  • Temporarily moved the return type declaration into App_Code. Still get __type serialised.

  • Ditto and applied the protected internal constructor per JM. This worked (so he gets a vote).

Strangely I don't get __type with a generic return type:

[WebMethod]
public static WebMethodReturn<IEnumerable<FleetObserverLiteAddOns.VehicleAddOnAccountStatus>> GetAccountCredits()

The solution for me, however, was to leave my return type in the DLL but change the WebMethod return type to object, i.e.

[WebMethod]
public static object ApplyCredits(int addonid, int[] vehicleIds) 

instead of

[WebMethod]
public static WebMethodReturn ApplyCredits(int addonid, int[] vehicleIds)
Amphimixis answered 1/10, 2010 at 13:33 Comment(0)
G
16

I've been trying some of these suggestions with a .NET 4 WCF service, and they don't seem to work - the JSON response still includes __type.

The easiest way I've discovered to remove the type-hinting is to change the endpoint behaviour from enableWebScript to webHttp.

    <behavior name="MapData.MapDataServiceAspNetAjaxBehavior">
      <webHttp />
    </behavior>

The default enableWebScript behaviour is required if you're using an ASP.NET AJAX client, but if you're manipulating the JSON with JavaScript or jQuery then the webHttp behaviour is probably a better choice.

Guggle answered 19/12, 2010 at 11:18 Comment(5)
That is the only thing that works for me in a .Net 4 web service. Very useful, thanks.Languish
How wonderful of this way, the 'd' keyword has been removed at the same time. Thanks a lot.Stratovision
I also had to set message format explicitly: [WebGet(ResponseFormat = WebMessageFormat.Json)]Isola
^ I also had to specify defaultOutgoingResponseFormat="Json" or automaticFormatSelectionEnabled="true" after switching from enableWebScript to webHttp.Advert
This worked for me but, /jsdebug is lost now. If you are using asp:ScriptManager to generate js stubs this is no good.Bozeman
T
12

If you're using ServiceStack.Text JSON Serializer you just need to:

JsConfig.ExcludeTypeInfo = true;

This functionality was automatically added back in v2.28, but the code above keeps that out of the serialization. You can also change this behavior by Type with:

JsConfig<Type>.ExcludeTypeInfo = true;
Tracee answered 12/10, 2012 at 16:51 Comment(0)
B
5

I think I have narrowed down the root cause of the mysterious appearing "__type" !

Here is an example where you can recreate the issue.

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
[System.Web.Script.Services.ScriptService]
public class Test : System.Web.Services.WebService
{
    public class Cat
    {
        public String HairType { get; set; }
        public int MeowVolume { get; set; }
        public String Name { get; set; }
    }

    [WebMethod]
    public String MyMethodA(Cat cat)
    {
        return "return value does not matter";
    }

    [WebMethod]
    public Cat MyMethodB(String someParam)
    {
        return new Cat() { HairType = "Short", MeowVolume = 13, Name = "Felix the Cat" };
    }
}

Here is the key part!

Simply because MyMethodA() exists in this same .asmx file and takes the class Cat as a parameter.... the __type will be added to the JSON returned from calling the other method: MyMethodB().

Even though they are different methods!!

My theory is as follows:

  1. When writing web services like this, Microsoft's code automatically hooks up the JSON serializing/deserializing behavior for you since you used the correct attributes, like [WebMethod] and [ScriptService].
  2. When this auto-magic Microsoft code executes, it finds a method that takes in Cat class as a parameter.
  3. It figures... oh... ok.... well since I will be receiving a Cat object from JSON.... therefore... if I ever return a Cat object as JSON from any method in the current web service class... I will give it a __type property so it will be easy to identify later when deserializing back to C#.
  4. Nyah-hahahaha...

Important Take-Away Note

You can avoid having the __type property appear in your generated JSON by avoiding taking in the class in question (Cat in my case) as a parameter to any of your WebMethods in your web service. So, in the above code, simply try modifying MyMethodA() to remove the Cat parameter. This causes the __type property to not be generated.

Balakirev answered 17/10, 2012 at 22:31 Comment(1)
This was my issue also. I had a function that took the class as a param and another that returned it. Once I removed the function that took the class on input, the __type was gone. So for me, I'm going to make 2 versions of any classes that need to go both directions, one for requests and one for responses. This way I don't get the extra __type overhead.Edra
Y
4

My 2 cents, however late in the day: as others have mentioned, there seem to be two ways to prevent the "__type" property:

a) Protect the parameterless constructor

b) Avoid passing the class in as a parameter to a web method

If you never need to pass the class as a parameter then you can make the constructor "protected internal". If you need to create an empty object then add in a factory method or some other constructor with a dummy parameter.

However, if you need to pass the class as a parameter to a web method then you will find that this will not work if the parameterless constructor is protected (the ajax call fails, presumably as the passed in json data cannot be deserialized into your class).

This was my problem, so I had to use a combination of (a) and (b): protect the parameterless constructor and create a dummy derived class to be used exclusively for parameters to web methods. E.g:

public class MyClass
{
    protected internal MyClass() { }
    public MyClass(Object someParameter) { }
    ...
}

// Use this class when we need to pass a JSON object into a web method
public class MyClassForParams : MyClass
{
    public MyClassForParams() : base() { }
}

Any web method that need to take in MyClass then uses MyClassForParams instead:

[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public MyClass DoSomething(MyClassForParams someObject)
{
    // Do something with someObject
    ...
    // Maybe return a MyClass object
    ...
}
Yuki answered 8/3, 2015 at 10:21 Comment(0)
S
3

Pass in null for the JavaScriptTypeResolver and the __type will not be serialized

JavaScriptSerializer serializer = new JavaScriptSerializer(null);
string json = serializer.Serialize(foo);
Storer answered 24/11, 2011 at 3:2 Comment(1)
Tried all of the above solutions. Still didn't work for me.Ensoul
B
2

I not sure this a good solution , but if you use the Json.net library, you can ignore some properties by adding [JsonIgnore] attribute.

Blandishments answered 3/3, 2010 at 19:54 Comment(1)
Yes Mironline, it's a Good Solution. I personally use it and I thought best option to ignore the attribute is [jsonIngore],Thanks for itKuykendall
M
2

In addition to John Morrison's advice on internal or protected internal constructor in your DataContract class, which works amazingly well for web services and majority of WCF, you might need to make an additional change in your web.config file. Instead of <enableWebScript/> element use <webHttp/> for your endpointBehaviors, e.g.:

<endpointBehaviors>
  <behavior name="MyServiceEndpoint">
    <webHttp/>
  </behavior>
</endpointBehaviors>
Markson answered 3/10, 2012 at 19:18 Comment(0)
A
1

Do not use the [Serializable] attribute.

The following should just do it

JavaScriptSerializer ser = new JavaScriptSerializer(); string json = ser.Serialize(objectClass);

Ataliah answered 3/6, 2009 at 8:32 Comment(1)
it outputs a JSON that has escaped quotas ( \" ) on all keys - which must be dealt with on client side. To avoid this (double JSON formatting) see stackoverflow.com/a/3079326 . You also must use the System.Web.Script.Serialization namespace found in System.Web.Extensions.dllTrygve
P
1

A bit late to the thread but here goes.

We had the same issue when the property being added to the json string was a List<T>. What we did was add another property which was an array of T, something like.

Before.

[DataMember]
public List<Person> People { get; set; }

After.

public List<Person> People { get; set; }

[DataMember(Name = "People")]
public Person[] Persons {
    get {
        return People.ToArray();
    }
    private set { }
}

While not an ideal solution, it does the trick.

Pickerelweed answered 22/5, 2012 at 5:9 Comment(0)
P
1

Here is a way around that

    [WebMethod]
    [ScriptMethod(UseHttpGet = true, ResponseFormat = ResponseFormat.Json)]
    public void Status()
    {
        MyObject myObject = new MyObject(); // Your class here
        var json = Newtonsoft.Json.JsonConvert.SerializeObject(myObject);

        HttpContext.Current.Response.Write(json);
    }
Pedometer answered 5/11, 2018 at 20:52 Comment(0)
S
0

This should solve it.

In the private SerializeValue method of JavaScriptSerializer in System.WebExtensions.dll, the __type is added to an internal dictionary if it can be resolved.

From Reflector:

private void SerializeValue(object o, StringBuilder sb, int depth, Hashtable objectsInUse)
{
    if (++depth > this._recursionLimit)
    {
        throw new ArgumentException(AtlasWeb.JSON_DepthLimitExceeded);
    }
    JavaScriptConverter converter = null;
    if ((o != null) && this.ConverterExistsForType(o.GetType(), out converter))
    {
        IDictionary<string, object> dictionary = converter.Serialize(o, this);
        if (this.TypeResolver != null)
        {
            string str = this.TypeResolver.ResolveTypeId(o.GetType());
            if (str != null)
            {
                dictionary["__type"] = str;
            }
        }
        sb.Append(this.Serialize(dictionary));
    }
    else
    {
        this.SerializeValueInternal(o, sb, depth, objectsInUse);
    }
}

If the type can't be determined, serialization will still proceed, but the type will be ignored. The good news is that since anonymous types inherit getType() and the names returned are dynamically generated by the compiler, the TypeResolver returns null for ResolveTypeId and the "__type" attribute is subsequently ignored.

I also took John Morrison's advice with the internal constructor just in case, though using just this method, I was still getting __type properties in my JSON response.

//Given the following class
[XmlType("T")]
public class Foo
{
    internal Foo()
    {

    }

    [XmlAttribute("p")]
    public uint Bar
    {
        get;
        set;
    }
}

[WebService(Namespace = "http://me.com/10/8")]
[System.ComponentModel.ToolboxItem(false)]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
public class MyService : System.Web.Services.WebService
{

    //Return Anonymous Type to omit the __type property from JSON serialization
    [WebMethod(EnableSession = true)]
    [System.Web.Script.Services.ScriptMethod(UseHttpGet = false, ResponseFormat = ResponseFormat.Json, XmlSerializeString = false)]
    public object GetFoo(int pageId)
    {
        //Kludge, returning an anonymois type using link, prevents returning the _type attribute.
        List<Foo> foos = new List<Foo>();
        rtnFoos.Add( new Foo(){
            Bar=99
        }};

        var rtn = from g in foos.AsEnumerable()
                   select g;

        return rtn;
    }
}

Note: I'm using an inherited JSON type converter that reads the XML Serialization attributes from serialized types to further compress the JSON. With thanks to CodeJournal. Works like a charm.

Sol answered 9/8, 2010 at 21:50 Comment(1)
Why in the world would they think it was a good idea to modify your custom serialization after you finished making changes? I'm in ASP.NET 2.0, no var keyword here. Know if there's another way similar to this?Zoezoeller
T
0

In addition to @sean 's answer of using JavaScriptSerializer .

When using JavaScriptSerializer and marking the method's ResponseFormat = WebMessageFormat.Json, the resulting response has double JSON encoding plus that if the resulting response is string, it will be plced bweteen double quotes.

To avoid this use the solution from this excellent answer to define the content type as JSON (overwrite) and stream the binary result of the JavaScriptSerializer.

The code sample from the mentioned answer:

public Stream GetCurrentCart()
{
    //Code ommited
    var j = new { Content = response.Content, Display=response.Display,
                  SubTotal=response.SubTotal};
    var s = new JavaScriptSerializer();
    string jsonClient = s.Serialize(j);
    WebOperationContext.Current.OutgoingResponse.ContentType =
        "application/json; charset=utf-8";
    return new MemoryStream(Encoding.UTF8.GetBytes(jsonClient));
}

JavaScriptSerializer is in the System.Web.Script.Serialization namespace found in System.Web.Extensions.dll which is not referenced by default.

Trygve answered 27/3, 2019 at 15:42 Comment(0)
S
0
var settings = new DataContractJsonSerializerSettings();
settings.EmitTypeInformation = EmitTypeInformation.Never;
DataContractJsonSerializer serializerInput = new DataContractJsonSerializer(typeof(Person), settings);
var ms = new MemoryStream();
serializerInput.WriteObject(ms, personObj);
string newRequest = Encoding.UTF8.GetString(ms.ToArray());
Selfexistent answered 5/8, 2019 at 0:45 Comment(0)
C
0

You can use create your own return type for sending response. and also while sending response use object as return type .so _type property will be get ignored.

Classieclassification answered 15/9, 2020 at 7:13 Comment(0)
C
0

The following does not remove __type attribute but replace content instead.

public class MyClass
{
    //... your code

    //... set __type to your own
    public string __type => "blaah"; // results in: '__type: "blaah"'
}
Christianna answered 12/1 at 13:8 Comment(0)
S
-7

This is a bit of a hack, but this worked for me (using C#):

s = (JSON string with "__type":"clsname", attributes)
string match = "\"__type\":\"([^\\\"]|\\.)*\",";
RegEx regex = new Regex(match, RegexOptions.Singleline);
string cleaned = regex.Replace(s, "");

Works with both [DataContract] and [DataContract(Namespace="")]

Sturgeon answered 14/6, 2011 at 20:38 Comment(2)
I -1 because this is so terrible to advice someone to do that, that it is better to say nothing.Sylvan
what language is this: s = (JSON string with "__type":"clsname", attributes) ??Novokuznetsk

© 2022 - 2024 — McMap. All rights reserved.