Ответ 1
Добавление параметра пространства имен к контракту данных делает трюк.
[DataContract(Namespace = "")]
Я пытаюсь преобразовать иерархию классов в строку Json с помощью DataContractJsonSerializer
в службе WCF.
поведение по умолчанию для сериализации производного класса заключается в добавлении к объекту следующей пары значений ключа:
"__type":"ClassName:#Namespace"
Моя проблема в том, что пространства имен длинны, и они раздувают строку Json. Я хотел бы как-то вмешаться в сериализацию и вывести это вместо этого:
"__type":"ClassName"
и при десериализации снова вмешаться, чтобы указать на правильное пространство имен (которое я знаю во время выполнения).
Есть ли способ сделать такое?
Добавление параметра пространства имен к контракту данных делает трюк.
[DataContract(Namespace = "")]
Эта страница описывает обстоятельства, при которых испускается свойство __type. Короче говоря, в WCF, если вы используете производный тип и KnownTypeAttribute, тогда вы получите свойство __type.
Пример:
Предположим, что
[DataContract]
[KnownType(typeof(Subscriber))]
public class Person { ... }
[DataContract]
public class Subscriber : Person { ... }
Этот код генерирует свойство __type:
var o = new Subscriber("Fleming");
var serializer = new DataContractJsonSerializer(typeof(Person));
serializer.WriteObject(Console.OpenStandardOutput(), o);
Но этот код не делает:
var o = new Subscriber("Fleming");
var serializer = new DataContractJsonSerializer(typeof(Subscriber));
serializer.WriteObject(Console.OpenStandardOutput(), o);
Обратите внимание, что второй snip использует DCJS с тем же типом, что и сериализуемый объект.
Чтобы избежать __type, не используйте производные типы, а если быть точным, используйте сериализатор, введенный в тип, который вы фактически сериализуете. Если сериализация выполняется неявно с помощью метода WCF, то метод следует набирать соответствующим образом. В моем примере это означает, что вы должны использовать тип возврата "Абонент", а не родительский тип "Лицо".
. __type передается в поток JSON методом (private) WriteServerTypeAttribute на (внутренний) System.Runtime.Serialization.Json.XmlJsonWriter. Насколько я могу судить, нет общедоступного, документированного, поддерживаемого способа изменить это.
Чтобы этого избежать, вам, возможно, потребуется вернуть строку из метода WCF, выполнить сериализацию самостоятельно и выполнить последующую обработку испускаемого JSON.
Если вы не возражаете против объекта __type, но просто хотите удалить квалификационное пространство имен из значения, тогда поместите свои типы в глобальное пространство имен. Другими словами, помещайте их вне любого объявления namespace
в коде.
Пример. Когда типы данных находятся в пространстве имен, и когда я использовал производный тип, сериализованный JSON выглядит следующим образом:
{
"__type":"Subscriber:#My.Custom.Namespace",
"Index":604455,
"Name":"Fleming",
"Id":580540
}
Когда типы данных находятся в глобальном пространстве имен, это выглядит так:
{
"__type":"Subscriber:#",
"Index":708759,
"Name":"Fleming",
"Id":675323
}
Ответ на cheeso был отличным. Я обнаружил уточнение для очистки поля __type, хотя:
Вместо удаления вашего подкласса из его пространства имен вы можете добавить свойство, подобное следующему:
[DataMember(Name = "__type")]
public string SubclassType
{
get
{
return "Subscriber";
}
set { }
}
Вы все еще застряли с уродливым именем "__type", но я обнаружил, что, поскольку я возвращал список подтипов, я все равно хотел указать имя типа. Вы даже можете вернуть значение "" для дальнейшего уменьшения размера ответа. Вы также можете просто объявить свойство как:
public string __type
но я обнаружил, что для акцентирования взлома, поэтому я застрял с соответствующим именем свойства и затем переименовал его.
-Joey
Примечание. Я набрал этот ответ ниже и позже понял, что DataContractResolver в настоящее время не поддерживается DataContractJsonSerializer. Тем не менее, это может быть вскоре со следующей версией фреймворка. Это также полезно, если вы смотрите больше, чем просто JSON.
**
Вы можете сделать это с помощью DataContractResolver, который позволяет настраивать типы для xsi: type (__type) и наоборот.
Чтобы сделать это, просмотрите это сообщение в блоге DataContractResolver, плюс эта концептуальная тема, плюс этот образец
@Cheeso писал (а):
Чтобы этого избежать, вам может потребоваться вернуть строку из WCF метод, выполнить сериализацию самостоятельно и выполнить последующую обработку испускаемый JSON.
Вот как я реализовал эту пост-обработку. Я думал, что отправлю его здесь JIC, это может помочь кому-то другому.
Сначала создадим шаблон, чтобы показать, как я создаю строку JSON:
// Instantiate & populate the object to be serialized to JSON
SomeClass xyz = new SomeClass();
... populate it ...
// Now serialize it
DataContractJsonSerializer ser = new DataContractJsonSerializer(xyz.GetType()); // Note xyz.GetType()
... serialize the object to json, many steps omitted here for brevity ...
string json = sr.ReadToEnd();
(Сериализация основана на примерах из https://msdn.microsoft.com/en-us/library/bb412179%28v=vs.110%29.aspx)
Обратите внимание, что [DataContract]
on SomeClass
содержит не синтаксис (name="")
, который я видел в другом месте. Это удаляет пространство имен из __type за счет необходимости ВСЕ вашего DataContract attrs, что загромождает ваш код. Вместо этого мой пост-процессор обрабатывает имя сборки в поле __type.
И теперь строка json
содержит текст JSON, но, к сожалению, включает в себя все, что "__type", которое вы не хотите, но не можете подавить.
Итак, вот мой пост-обрабатывающий код, который удаляет его:
// This strips out that unsuppressable __type clutter generated by the KnownType attributes
Attribute[] attrs = Attribute.GetCustomAttributes(xyz.GetType());
foreach (Attribute attr in attrs)
{
if (attr is KnownTypeAttribute)
{
KnownTypeAttribute a = (KnownTypeAttribute)attr;
string find = "\"__type\":\"" + a.Type.ReflectedType.Name + "." + a.Type.Name + ":#" + a.Type.Namespace + "\",";
json = json.Replace(find, "");
}
}
Это делает несколько предположений, особенно в том, что поле __type заканчивается запятой, которое предполагает, что за ним следует другое поле, хотя (а) у моих объектов всегда есть как минимум 1 поле, и (б) я обнаружил, что Поле __type всегда является первым в сериализованном объекте.
Как всегда, вам, возможно, придется что-то приспособить к вашей ситуации, но я считаю, что это хорошо работает для моего.
Несколько раз назад я решил эту проблему. Я использую DataContractJsonSerializer У вас будет __type в json, если ваш метод для сериализации имеет параметр базового класса, но вы даете ему subClass как параметр. Подробнее:
[DataContract]
[KnownType(typeof(B))]
public abstract class A
{
[DataMember]
public String S { get; set; }
}
[DataContract]
public class B : A
{
[DataMember]
public Int32 Age { get; set; }
}
public static String ToJson<T>(this T value)
{
var serializer = new DataContractJsonSerializer(typeof(T));
using (var stream = new MemoryStream())
{
serializer.WriteObject(stream, value);
return Encoding.UTF8.GetString(stream.ToArray());
}
}
У вас есть два метода тестирования:
public static void ReadTypeDerived(A type)
{
Console.WriteLine(ToJson(type));
}
и
public static void ReadType<T>(T type)
{
Console.WriteLine(ToJson(type));
}
В первом тесте у вас есть
"{\" __ типа \ ": \" B: # ConsoleApplication1\ "\" S \ ":\" Vv \ ",\" \ "Возраст: 10}"
Во втором:
"{\" S\ ": \" Vv\ "\" Возраст \ ": 10}"