如何不序列化 JSON 对象上的 __type 属性

发布于 2024-07-15 07:55:43 字数 187 浏览 5 评论 0原文

我从 ScriptServiceWebMethod 返回的每个对象都被包装到一个 JSON 对象中,其中的数据位于名为 d 的属性中。 没关系。 但我不希望向客户端提供额外的 __type 属性,因为我使用 jQuery 进行手动处理。

是否可以?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(18

南风几经秋 2024-07-22 07:55:43

我发现,如果我创建类的默认构造函数,我的 webmethod 返回除 public 之外的任何内容,它不会序列化 __type:ClassName 部分。

您可能需要声明默认构造函数 protected inside ClassName() { }

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() { }

旧城烟雨 2024-07-22 07:55:43

约翰的解决方案对我不起作用,因为我返回的类型位于单独的 DLL 中。 我可以完全控制该 DLL,但如果构造函数是内部的,则无法构造返回类型。

我想知道返回类型是库中的公共类型是否可能是原因 - 我已经做了很多 Ajax 并且以前没有见过这个。

快速测试:

  • 暂时将返回类型声明移至 App_Code 中。 仍将 __type 序列化。

  • 同上,并根据 JM 应用了受保护的内部构造函数。 这有效(所以他获得了投票)。

奇怪的是,我没有得到具有通用返回类型的 __type

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

然而,对我来说,解决方案是将我的返回类型保留在 DLL 中,但更改 WebMethod将类型返回给对象,即

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

代替

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

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)
心在旅行 2024-07-22 07:55:43

我一直在使用 .NET 4 WCF 服务尝试其中一些建议,但它们似乎不起作用 - JSON 响应仍然包含 __type。

我发现删除类型提示的最简单方法是将端点行为从enableWebScript更改为webHttp。

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

如果您使用 ASP.NET AJAX 客户端,则需要默认的 enableWebScript 行为,但如果您使用 JavaScript 或 jQuery 操作 JSON,则 webHttp 行为可能是更好的选择。

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.

冷默言语 2024-07-22 07:55:43

如果您使用 ServiceStack.Text JSON Serializer 您只需要:

JsConfig.ExcludeTypeInfo = true;

此功能是自动添加的回到 v2.28 中,但上面的代码将其排除在序列化之外。 您还可以通过 Type 更改此行为:

JsConfig<Type>.ExcludeTypeInfo = true;

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;
夏日浅笑〃 2024-07-22 07:55:43

我想我已经缩小了神秘出现“__type”的根本原因!

这是一个可以重现问题的示例。

[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" };
    }
}

这里是关键部分!

仅仅因为 MyMethodA() 存在于同一个 .asmx 文件中并且采用 Cat 类作为参数.... __type 将被添加到调用另一个方法返回的 JSON:MyMethodB()。

即使它们是不同的方法!!

我的理论如下:

  1. 当编写这样的Web服务时,微软的代码会自动为您连接JSON序列化/反序列化行为,因为您使用了正确的属性,例如[ WebMethod] 和 [ScriptService]。
  2. 当这个自动魔法的 Microsoft 代码执行时,它会找到一个以 Cat 类作为参数的方法。
  3. 它计算...哦...好吧...好吧,因为我将从 JSON 接收一个 Cat 对象...因此...如果我曾经返回一个 Cat 对象作为 JSON当前 Web 服务类中的任何方法...我将给它一个 __type 属性,以便稍后反序列化回 C# 时可以轻松识别。
  4. Nyah-hahahaha...

重要提示

您可以通过避免将相关类(在我的例子中为 Cat)作为任何参数的参数来避免 __type 属性出现在生成的 JSON 中。您的 Web 服务中的 WebMethods。 因此,在上面的代码中,只需尝试修改 MyMethodA() 以删除 Cat 参数。 这会导致生成 __type 属性。

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.

零崎曲识 2024-07-22 07:55:43

我的 2 美分,无论如何晚了:正如其他人提到的,似乎有两种方法可以防止“__type”属性:

a)保护无参数构造函数

b)避免将类作为参数传递给 Web 方法

如果您永远不需要将类作为参数传递,然后您可以使构造函数“受保护的内部”。 如果您需要创建一个空对象,请添加工厂方法或其他带有虚拟参数的构造函数。

但是,如果您需要将类作为参数传递给 Web 方法,那么您会发现,如果无参数构造函数受到保护,则这将不起作用(ajax 调用失败,大概是因为传入的 json 数据无法反序列化到您的类中) )。

这是我的问题,因此我必须结合使用 (a) 和 (b):保护无参数构造函数并创建一个虚拟派生类,专门用于 Web 方法的参数。 例如:

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() { }
}

任何需要接收 MyClass 的 Web 方法都可以使用 MyClassForParams 来代替:

[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public MyClass DoSomething(MyClassForParams someObject)
{
    // Do something with someObject
    ...
    // Maybe return a MyClass object
    ...
}

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
    ...
}
回眸一遍 2024-07-22 07:55:43

为 JavaScriptTypeResolver 传入 null ,__type 将不会被序列化

JavaScriptSerializer serializer = new JavaScriptSerializer(null);
string json = serializer.Serialize(foo);

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

JavaScriptSerializer serializer = new JavaScriptSerializer(null);
string json = serializer.Serialize(foo);
左秋 2024-07-22 07:55:43

我不确定这是一个好的解决方案,但是如果您使用 Json.net 库,您可以通过添加 [JsonIgnore] 属性来忽略某些属性。

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

吾家有女初长成 2024-07-22 07:55:43

除了 John Morrison 关于 DataContract 类中的内部受保护的内部构造函数的建议(这对于 Web 服务和大多数 WCF 来说效果非常好)之外,您可能还需要添加额外的内容更改您的 web.config 文件。
使用 作为端点行为,而不是 元素,例如:

<endpointBehaviors>
  <behavior name="MyServiceEndpoint">
    <webHttp/>
  </behavior>
</endpointBehaviors>

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>
回忆躺在深渊里 2024-07-22 07:55:43

不要使用 [Serializable] 属性。

下面应该这样做

JavaScriptSerializer ser = new JavaScriptSerializer();
字符串 json = ser.Serialize(objectClass);

Do not use the [Serializable] attribute.

The following should just do it

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

一片旧的回忆 2024-07-22 07:55:43

虽然有点晚了,但还是到这里吧。

当添加到 json 字符串的属性是 List时,我们遇到了同样的问题。 我们所做的是添加另一个属性,它是一个 T 数组,类似的东西。

前。

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

后。

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

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

虽然不是理想的解决方案,但它确实有效。

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.

醉酒的小男人 2024-07-22 07:55:43

这是解决这个问题的方法

    [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);
    }

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);
    }
何以笙箫默 2024-07-22 07:55:43

这应该可以解决它。

在System.WebExtensions.dll中JavaScriptSerializer的私有Seri​​alizeValue方法中,
如果可以解析, __type 将被添加到内部字典中。

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);
    }
}

如果无法确定类型,序列化仍将继续,但类型将被忽略。 好消息是,由于匿名类型继承 getType() 并且返回的名称是由编译器动态生成的,因此 TypeResolver 为 ResolveTypeId 返回 null,并且随后会忽略“__type”属性。

我还采纳了 John Morrison 关于内部构造函数的建议,以防万一,尽管仅使用此方法,我仍然在 JSON 响应中获取 __type 属性。

//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;
    }
}

注意:我使用继承的 JSON 类型转换器从序列化类型中读取 XML 序列化属性以进一步压缩 JSON。 感谢 CodeJournal。 奇迹般有效。

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.

薄暮涼年 2024-07-22 07:55:43

除了 @sean 使用 JavaScriptSerializer 的回答。

当使用 JavaScriptSerializer 并标记方法的 ResponseFormat = WebMessageFormat.Json 时,生成的响应具有双 JSON 编码,而且如果生成的响应是 string,它将被置于双引号之间。

为了避免这种情况,请使用这个优秀答案中的解决方案将内容类型定义为 JSON(覆盖)并流式传输二进制结果JavaScriptSerializer。

上述答案中的代码示例:

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 位于 System.Web.Extensions.dll 中的 System.Web.Script.Serialization 命名空间中默认情况下不引用。

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.

晚雾 2024-07-22 07:55:43
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());
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());
來不及說愛妳 2024-07-22 07:55:43

您可以使用创建自己的返回类型来发送响应。 并且在发送响应时使用对象作为返回类型。因此 _type 属性将被忽略。

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.

゛时过境迁 2024-07-22 07:55:43

以下内容不会删除 __type 属性,而是替换内容。

public class MyClass
{
    //... your code

    //... set __type to your own
    public string __type => "blaah"; // results in: '__type: "blaah"'
}

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"'
}
清音悠歌 2024-07-22 07:55:43

这有点黑客,但这对我有用(使用 C#):

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

适用于 [DataContract][DataContract(Namespace="")]

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="")]

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文