Как не сериализовать свойство __type на объектах JSON

Каждый объект, возвращаемый с WebMethod объекта ScriptService, переносится в объект JSON с данными в свойстве с именем d. Это нормально. Но я не хочу, чтобы дополнительное свойство __type было передано клиенту, поскольку я выполняю ручную обработку с помощью jQuery.

Возможно ли это?

Ответ 1

Я обнаружил, что если я сделаю конструктор по умолчанию для моего класса, то мой __type:ClassName возвращает что-либо, кроме public, он не будет сериализовать часть __type:ClassName.

Вы можете объявить ваш конструктор по умолчанию protected internal ClassName() { }

Ответ 2

Решение John не работало для меня, поскольку тип, который я возвращаю, находится в отдельной 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)

Ответ 3

Я пробовал некоторые из этих предложений с помощью .NET 4 WCF-службы, и они, похоже, не работают - ответ JSON по-прежнему включает в себя __type.

Самый простой способ, с помощью которого я обнаружил удаление типа, - это изменить поведение конечной точки с enableWebScript на webHttp.

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

Поведение по умолчанию enableWebScript требуется, если вы используете клиент ASP.NET AJAX, но если вы манипулируете JSON с помощью JavaScript или jQuery, то, вероятно, лучшим вариантом будет поведение webHttp.

Ответ 4

Если вы используете ServiceStack.Text JSON Serializer, вам просто нужно:

JsConfig.ExcludeTypeInfo = true;

Эта функциональность была автоматически добавлена ​​обратно в v2.28, но приведенный выше код сохраняет это из сериализации. Вы также можете изменить это поведение на Type с помощью:

JsConfig<Type>.ExcludeTypeInfo = true;

Ответ 5

Передайте значение null для JavaScriptTypeResolver, а тип __ не будет сериализован

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

Ответ 6

Я не уверен, что это хорошее решение, но если вы используете библиотеку Json.net, вы можете игнорируйте некоторые свойства, добавив атрибут [JsonIgnore].

Ответ 7

В дополнение к совету Джона Моррисона в внутреннем или защищенном внутреннем конструкторе в вашем классе DataContract, который отлично работает для веб-служб и большинства WCF, вам может потребоваться внесите дополнительные изменения в ваш файл web.config. Вместо элемента <enableWebScript/> используйте <webHttp/> для вашей конечной точки, например:

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

Ответ 8

Я думаю, что я сузил основную причину таинственного появления "__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().

Даже если это разные методы!

Моя теория такова:

  • При написании веб-сервисов, подобных этому, код Microsoft автоматически подключает поведение JSON для сериализации/десериализации для вас, поскольку вы использовали правильные атрибуты, такие как [WebMethod] и [ScriptService].
  • Когда этот автоматический магия кода Microsoft выполняется, он находит метод, который принимает класс Cat в качестве параметра.
  • Это цифры... о... хорошо.... ну, так как я буду получать объект Cat от JSON.... поэтому... если я когда-либо вернул объект Cat как JSON из любого метода в текущем классе веб-службы... Я дам ему свойство __type, поэтому его будет легко идентифицировать позже при десериализации на С#.
  • Nyah-Хахаха...

Важная заметка о прохождении

Вы можете избежать наличия свойства __type в вашем сгенерированном JSON, избегая при этом в рассматриваемом классе (Cat в моем случае) в качестве параметра для любого из ваших WebMethods в вашем веб-сервисе. Итак, в приведенном выше коде просто попробуйте изменить MyMethodA(), чтобы удалить параметр Cat. Это приводит к созданию свойства __type не.

Ответ 9

Не используйте атрибут [Serializable].

Следующее должно просто сделать это

JavaScriptSerializer ser = новый JavaScriptSerializer(); string json = ser.Serialize(objectClass);

Ответ 10

Немного поздно, но вот здесь.

У нас была такая же проблема, когда свойство, добавляемое в строку json, было List <T> . Мы сделали еще одно свойство, которое было массивом 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 { }
}

Хотя это не идеальное решение, он делает трюк.

Ответ 11

Вот способ обойти это

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

Ответ 12

Это должно решить проблему.

В частном методе SerializeValue JavaScriptSerializer в System.WebExtensions.dll, __type добавляется во внутренний словарь, если он может быть разрешен.

От рефлектора:

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 возвращает null для ResolveTypeId, а атрибут "__type" впоследствии игнорируется.

Я также принял совет Джона Моррисона с внутренним конструктором на всякий случай, хотя, используя только этот метод, я все еще получал свойства __type в своем ответе JSON.

//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, который читает атрибуты Serialization XML из сериализованных типов для дальнейшего сжатия JSON. Благодаря CodeJournal. Работает как шарм.

Ответ 13

Мои 2 цента, хотя и поздно в тот же день: как уже отмечали другие, существует, по-видимому, два способа предотвратить свойство "__type":

a) Защитите конструктор без параметров

b) Избегайте передачи класса в качестве параметра веб-методу

Если вам никогда не нужно передавать класс в качестве параметра, вы можете сделать конструктор "protected internal". Если вам нужно создать пустой объект, добавьте метод factory или какой-либо другой конструктор с фиктивным параметром.

Однако, если вам нужно передать класс в качестве параметра веб-методу, вы обнаружите, что это не будет работать, если конструктор без параметров будет защищен (вызов ajax не выполняется, по-видимому, поскольку переданные в json данные не могут быть десериализованы в ваш класс).

Это была моя проблема, поэтому мне пришлось использовать комбинацию (a) и (b): защитить конструктор без параметров и создать производный класс манекена, который будет использоваться исключительно для параметров для веб-методов. Например:

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, вместо этого использует MyClassForParams:

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

Ответ 14

В дополнение к @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.Script.Serialization имен System.Web.Script.Serialization найденном в System.Web.Extensions.dll которое по умолчанию нет ссылок.

Ответ 15

Я решил объявить конструктор по умолчанию protected ClassName internal() {} См. Также ответ Джона Моррисона

Ответ 16

Это немного взломать, но это сработало для меня (используя С#):

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