Как не сериализовать свойство __type на объектах JSON
Каждый объект, который я возвращаю из WebMethod
ScriptService
в объект JSON с данными в свойстве с именем d
. Ничего страшного. Но я не хочу, чтобы дополнительное свойство __type
клиенту, поскольку я выполняю ручную обработку с помощью jQuery.
Является ли это возможным?
- Что такое JSON?
- Разница между @JsonIgnore и @JsonBackReference, @JsonManagedReference
- Служба REST Spring: получение JSON из запроса
- Сохраняется ли порядок элементов в списке JSON?
- Джерси 415 Неподдерживаемый тип материала
- Как я могу объединить два объекта JObject?
- Как передать массив JSON в качестве параметра в URL-адресе
- Угловые 2+ и наблюдаемые: не могут привязываться к 'ngModel', поскольку это не известное свойство 'select'
- Удаление десериализации JSON в объект с помощью Json.NET
- Запрос даты с ISODate в mongodb, похоже, не работает
- Действия controllerа ASP.NET MVC, которые возвращают JSON или частичный html
- Добавить виртуальный атрибут для вывода json
- Возврат ответа JSON с сайта Servlet на Javascript / JSP
Ну, это было давно, как вы спросили. Я обнаружил, что если я сделаю конструктор по умолчанию моего classа, что мой веб-метод вернет что-либо, кроме публичного, он не будет сериализовать часть __type: ClassName.
Вы можете захотеть объявить свой protected internal ClassName(){}
конструктор по умолчанию protected internal ClassName(){}
Решение Джона не работало для меня, поскольку тип, который я возвращаю, находится в отдельной DLL. Я имею полный контроль над этой DLL, но я не могу построить свой возвращаемый тип, если конструктор является внутренним.
Я задавался вопросом, может ли быть тип возврата, являющийся публичным типом в библиотеке, – я делал много Ajax и раньше не видел этого.
Быстрые тесты:
-
Временно переместил объявление типа возвращаемого значения в App_Code. Все еще получить
__type
. -
То же самое и применил защищенный внутренний конструктор на JM. Это сработало (поэтому он получает голос).
Странно, я не получаю __type
с общим типом возврата:
[WebMethod] public static WebMethodReturn> GetAccountCredits()
Однако решение для меня состояло в том, чтобы оставить мой тип возврата в DLL, но изменить тип возвращаемого объекта WebMethod на объект , т. Е.
[WebMethod] public static object ApplyCredits(int addonid, int[] vehicleIds)
вместо
[WebMethod] public static WebMethodReturn ApplyCredits(int addonid, int[] vehicleIds)
Я пробовал некоторые из этих предложений с помощью службы .NET 4 WCF, и они, похоже, не работают – ответ JSON по-прежнему включает __type.
Самый простой способ, который я обнаружил, чтобы удалить подсказку типа, – это изменить поведение конечной точки с enableWebScript на webHttp.
По умолчанию действие enableWebScript требуется, если вы используете клиент ASP.NET AJAX, но если вы манипулируете JSON с помощью JavaScript или jQuery, то поведение webHttp, вероятно, является лучшим выбором.
Если вы используете ServiceStack.Text JSON Serializer, вам просто нужно:
JsConfig.ExcludeTypeInfo = true;
Эта функциональность была автоматически добавлена обратно в v2.28 , но приведенный выше код сохраняет это из сериализации. Вы также можете изменить это поведение по Type
с помощью:
JsConfig.ExcludeTypeInfo = true;
Передайте значение null для JavaScriptTypeResolver, и тип __ не будет сериализован
JavaScriptSerializer serializer = new JavaScriptSerializer(null); string json = serializer.Serialize(foo);
Я не уверен, что это хорошее решение, но если вы используете библиотеку Json.net , вы можете игнорировать некоторые свойства, добавив атрибут [JsonIgnore] .
Я думаю, что я сузил основную причину таинственного появления «__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 и принимает class Cat как параметр …. __type будет добавлен в JSON, возвращенный из вызова другого метода: MyMethodB ().
Хотя они разные методы!
Моя теория такова:
- При написании таких веб-сервисов код Microsoft автоматически подключает поведение JSON для сериализации / десериализации, поскольку вы использовали правильные атрибуты, такие как [WebMethod] и [ScriptService].
- Когда этот автоматический магия кода Microsoft выполняется, он находит метод, который принимает class Cat в качестве параметра.
- Это цифры … о … хорошо …. хорошо, так как я буду получать объект Cat от JSON …. поэтому … если я когда-либо верну объект Cat как JSON из любого метода в текущем веб-сервисе class … Я дам ему свойство __type, поэтому его будет легко идентифицировать позже, когда десериализуем обратно на C #.
- Nyah-Хахаха …
Важно Take-Away Примечание
Вы можете избежать наличия свойства __type в вашем сгенерированном JSON, избегая при этом в рассматриваемом classе (Cat в моем случае) в качестве параметра для любого из ваших WebMethods в вашем веб-сервисе. Итак, в приведенном выше коде просто попробуйте изменить MyMethodA (), чтобы удалить параметр Cat. Это приводит к тому, что свойство __type не генерируется.
В дополнение к совету Джона Моррисона о внутреннем или защищенном внутреннем конструкторе в вашем classе DataContract, который отлично работает для веб-сервисов и большинства WCF, возможно, вам придется внести дополнительные изменения в ваш файл web.config. Вместо элемента
используйте
для своих конечных пользователей, например:
Немного поздно в stream, но здесь идет.
У нас была такая же проблема, когда свойство, добавляемое в строку json, было List
До.
[DataMember] public List People { get; set; }
После.
public List People { get; set; } [DataMember(Name = "People")] public Person[] Persons { get { return People.ToArray(); } private set { } }
Хотя это не идеальное решение, он делает трюк.
Не используйте атрибут [Serializable].
Следующее должно просто сделать это
JavaScriptSerializer ser = новый JavaScriptSerializer (); string json = ser.Serialize (objectClass);
Это должно решить проблему.
В частном методе 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 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 foos = new List (); rtnFoos.Add( new Foo(){ Bar=99 }}; var rtn = from g in foos.AsEnumerable() select g; return rtn; } }
Примечание. Я использую унаследованный конвертер типа JSON, который считывает атрибуты Serialization XML из сериализованных типов для дальнейшего сжатия JSON. С благодарностью CodeJournal . Работает как шарм.
Мои 2 цента, хотя и поздно в тот же день: как говорили другие, существует, по-видимому, два способа предотвратить свойство «__type»:
a) Защитите конструктор без параметров
b) Избегайте передачи classа в качестве параметра веб-методу
Если вам никогда не нужно передавать class в качестве параметра, вы можете сделать конструктор «защищенным внутренним». Если вам нужно создать пустой объект, добавьте фабричный метод или какой-нибудь другой конструктор с фиктивным параметром.
Однако, если вам нужно передать class в качестве параметра веб-методу, вы обнаружите, что это не будет работать, если конструктор без параметров будет защищен (вызов ajax не выполняется, предположительно, поскольку переданные в json данные не могут быть десериализованы в ваш class ).
Это была моя проблема, поэтому мне пришлось использовать комбинацию (a) и (b): защитить конструктор без параметров и создать производный class манекена, который будет использоваться исключительно для параметров для веб-методов. Например:
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 ... }
Я решил объявить конструктор по умолчанию protected ClassName internal () {} См. Также ответ Джона Моррисона
Это немного взломать, но это сработало для меня (используя 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="")]