Как не сериализовать свойство __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="")]