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

时间:2009-03-09 17:59:01

标签: c# asp.net json asmx javascriptserializer

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

有可能吗?

17 个答案:

答案 0 :(得分:38)

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

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

答案 1 :(得分:23)

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

我想知道返回类型是否是库中的公共类型甚至可能是原因 - 我一直在做很多Ajax,之前没有见过这个。

快速测试:

  • 暂时将返回类型声明移动到App_Code中。仍然可以__type序列化。

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

奇怪的是,我没有使用通用返回类型__type

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

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

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

而不是

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

答案 2 :(得分:16)

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

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

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

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

答案 3 :(得分:10)

如果您正在使用ServiceStack.Text JSON Serializer,则只需:

JsConfig.ExcludeTypeInfo = true;

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

JsConfig<Type>.ExcludeTypeInfo = true;

答案 4 :(得分:3)

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

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

答案 5 :(得分:3)

我想我已经缩小了神秘出现的“__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服务时,Microsoft的代码会自动为您添加JSON序列化/反序列化行为,因为您使用了正确的属性,如[WebMethod]和[ScriptService]。
  2. 当执行此自动魔术Microsoft代码时,它会找到一个将Cat类作为参数的方法。
  3. 它数字......哦......好吧......因为我将从JSON收到一个Cat对象....因此......如果我返回一个Cat对象作为当前Web服务类中任何方法的JSON ...我将给它一个__type属性,以便以后在反序列化回C#时很容易识别。
  4. Nyah-哈哈哈哈...
  5. 重要的带走注意事项

    您可以避免在生成的JSON中出现__type属性,避免将有问题的类(我的情况下为Cat)作为Web服务中任何WebMethods的参数。因此,在上面的代码中,只需尝试修改MyMethodA()以删除Cat参数。这会导致__type属性生成 not

答案 6 :(得分:2)

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

答案 7 :(得分:2)

除了John Morrison对DataContract类中内部受保护的内部构造函数的建议之外,对于Web服务和大多数WCF,它的工作效果非常好,您可能需要在web.config文件中进行其他更改。 而不是<enableWebScript/>元素使用<webHttp/>作为您的endpointBehaviors,例如:

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

答案 8 :(得分:1)

不要使用[Serializable]属性。

以下应该这样做

  

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

答案 9 :(得分:1)

这是解决问题的方法

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

答案 10 :(得分:1)

我的2美分,不管当天晚些时候:正如其他人所提到的,似乎有两种方法可以阻止“__type”属性:

a)保护无参数构造函数

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

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

但是,如果您需要将类作为参数传递给Web方法,那么您会发现如果无参数构造函数受到保护,这将无法工作(ajax调用失败,可能是因为传入的json数据无法反序列化进入你的班级。)

这是我的问题,所以我不得不使用(a)和(b)的组合:保护无参数构造函数并创建一个虚拟派生类,专门用于Web方法的参数。 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() { }
}

任何需要在MyClass中使用的Web方法都会改为使用MyClassForParams:

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

答案 11 :(得分:1)

线程有点晚了,但这里就是。

当添加到json字符串的属性是List&lt; T&gt;时,我们遇到了同样的问题。我们所做的是添加另一个属性,它是一个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 { }
}

虽然不是理想的解决方案,但却可以解决问题。

答案 12 :(得分:0)

这应该解决它。

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

来自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。像魅力一样。

答案 13 :(得分:0)

除了@sean使用JavaScriptSerializer的答案。

使用JavaScriptSerializer并标记方法的ResponseFormat = WebMessageFormat.Json时,结果响应具有双JSON编码,如果结果响应为string,则将其用双引号引起来。

为避免这种情况,请使用this excellent answer中的解决方案将内容类型定义为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.Script.Serialization中的System.Web.Extensions.dll名称空间中,默认情况下未引用。

答案 14 :(得分:0)

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

答案 15 :(得分:-1)

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

答案 16 :(得分:-7)

这有点像黑客,但这对我有用(使用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="")]