Как реализовать пользовательскую сериализацию JSON из веб-службы ASP.NET? - PullRequest
13 голосов
/ 02 октября 2008

Какие есть варианты для сериализации при возврате экземпляров пользовательских классов из WebService?

У нас есть некоторые классы с рядом свойств класса дочерней коллекции, а также с другими свойствами, которые могут быть или не быть установлены в зависимости от использования. Эти объекты возвращаются из ASP.NET .asmx WebService, украшенного атрибутом ScriptService, поэтому сериализуются посредством сериализации JSON при возврате различными методами WebMethods.

Проблема заключается в том, что при серийной обработке все открытые свойства возвращаются независимо от того, используются они или нет, а также возвращают имя класса и другую информацию более подробным образом, чем хотелось бы, если бы вы хотели ограничить объем трафика.

В настоящее время для возвращаемых классов мы добавили пользовательские конвертеры JavaScript, которые обрабатывают сериализацию JSON, и добавили их в файл web.config, как показано ниже:

<system.web.extensions>
  <scripting>
    <webServices>
      <jsonSerialization>
        <converters>
          <add name="CustomClassConverter" type="Namespace.CustomClassConverter" />
        </converters>
      </jsonSerialization>
    </webServices>
  </scripting>
</system.web.extensions>

Но для этого требуется специальный конвертер для каждого класса. Есть ли другой способ изменить стандартную сериализацию JSON, либо путем расширения службы, создания настраиваемого сериализатора и т. П.?

Сопровождение
@marxidad:

Мы используем класс DataContractJsonSerializer в других приложениях, однако мне не удалось выяснить, как применить его к этим службам. Вот пример того, как настроены сервисы:

[ScriptService]
public class MyService : System.Web.Services.WebService
{
    [WebMethod]
    public CustomClass GetCustomClassMethod
    {
        return new customClass();
    }
}

WebMethods вызывается с помощью javascript и возвращает данные, сериализованные в JSON. Единственный способ, которым мы смогли изменить сериализацию, - это использовать конвертеры JavaScript, как указано выше?

Есть ли способ указать WebService использовать пользовательский DataContractJsonSerializer? Будь то конфигурация web.config, украшение службы атрибутами и т. Д.?

Обновление
Ну, мы не смогли найти какой-либо способ переключить из коробки JavaScriptSerializer, кроме как для создания отдельных JavaScriptConverter, как указано выше.

Что мы сделали для того, чтобы избежать создания отдельного конвертера, - это создание общего JavaScriptConverter. Мы добавили пустой интерфейс к классам, которые мы хотели обработать, и SupportedTypes, который вызывается при запуске веб-службы, использует отражение, чтобы найти любые типы, которые реализуют интерфейс, подобный этому:

public override IEnumerable<Type> SupportedTypes
{
  get
  {
    foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
    {
      AssemblyBuilder dynamicAssemblyCheck = assembly as AssemblyBuilder;
      if (dynamicAssemblyCheck == null)
      {
        foreach (Type type in assembly.GetExportedTypes())
        {
          if (typeof(ICustomClass).IsAssignableFrom(type))
          {
            yield return type;
          }
        }
      }
    }
  }
}

Реальная реализация немного отличается, так что тип кэшируется, и мы, вероятно, реорганизуем его для использования пользовательских атрибутов, а не пустого интерфейса.

Однако с этим мы столкнулись с немного другой проблемой при работе с пользовательскими коллекциями. Обычно они просто расширяют общий список, но вместо самого List <> используются пользовательские классы, потому что в классах коллекции обычно есть пользовательская логика, сортировка и т. Д.

Проблема в том, что метод Serialize для JavaScriptConverter возвращает словарь, который сериализован в JSON в виде пар имя-значение со связанным типом, тогда как список возвращается в виде массива. Таким образом, классы коллекции не могут быть легко сериализованы с использованием конвертера. Решением для этого было просто не включать эти типы в SupportedTypes конвертера, и они отлично сериализуются в виде списков.

Итак, сериализация работает, но когда вы пытаетесь передать эти объекты другим способом в качестве параметра для вызова веб-службы, десериализация прерывается, потому что они не могут быть введены, обрабатывается как список словарей строк / объектов , который не может быть преобразован в список любого пользовательского класса, который содержит коллекция. Единственный способ справиться с этим - создать универсальный класс, представляющий собой список строковых / объектных словарей, который затем преобразует список в соответствующий пользовательский класс коллекции, а затем изменяет любые параметры веб-службы, чтобы вместо них использовать универсальный класс. .

Я уверен, что здесь есть масса проблем и нарушений "лучших практик", но он выполняет работу за нас, не создавая тонны пользовательских классов конвертера.

Ответы [ 5 ]

2 голосов
/ 01 июня 2009

Я знаю, что этот поток некоторое время был тихим, но я подумал, что предложил бы, если вы переопределите свойство SupportedTypes JavaScriptConverter в своем пользовательском конвертере, вы можете добавить типы, которые должны использовать конвертер. Это может войти в файл конфигурации при необходимости. Таким образом, вам не понадобится специальный конвертер для каждого класса.

Я пытался создать универсальный конвертер, но не мог понять, как его идентифицировать в файле web.config. Хотелось бы узнать, справился ли кто-нибудь еще.

Я понял эту идею, пытаясь решить вышеуказанную проблему, и наткнулся на статью Ника Берарди «Создание более точного сериализатора JSON .NET» (Google google it).

работал для меня:)

Спасибо всем.

2 голосов
/ 29 апреля 2009

Если вы не используете сгенерированные кодом классы, вы можете украсить свои свойства с помощью ScriptIgnoreAttribute , чтобы указать сериализатору игнорировать определенные свойства. Сериализация XML имеет похожий атрибут.

Конечно, вы не можете использовать этот подход, если хотите вернуть некоторые свойства класса при одном вызове метода службы и разные свойства одного и того же класса при другом вызове метода службы. Если вы хотите сделать это, верните анонимный тип в методе сервиса.

[WebMethod]
[ScriptMethod]
public object GimmieData()
{
    var dalEntity = dal.GimmieEntity(); //However yours works...

    return new
       {
           id = dalEntity.Id,
           description = dalEntity.Desc
       };

}

Сериализатору может быть наплевать на тип объекта, который вы ему отправляете, так как он все равно превращает его в текст.

Я также считаю, что вы могли бы реализовать ISerializable на вашем объекте данных (как частичный класс, если у вас есть объекты данных кодового поколения) для получения детального контроля над процессом сериализации, но я не пробовал.

1 голос
/ 14 октября 2008

Если вы используете .NET 3.x (или можете), лучшим выбором будет служба WCF.

Вы можете выборочно контролировать, какие свойства сериализуются клиенту с помощью атрибута [DataMember]. WCF также позволяет более детально контролировать сериализацию и десериализацию JSON, если вы этого хотите.

Это хороший пример для начала: http://blogs.msdn.com/kaevans/archive/2007/09/04/using-wcf-json-linq-and-ajax-passing-complex-types-to-wcf-services-with-json-encoding.aspx

0 голосов
/ 02 октября 2008

Не указывайте мне на эту работу наверняка, но я считаю, что это то, что вы ищете.

[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public XmlDocument GetXmlDocument()
{
    XmlDocument xmlDoc = new XmlDocument();
    xmlDoc.LoadXml(_xmlString);
    return xmlDoc;
}
0 голосов
/ 02 октября 2008

Можно использовать класс System.Runtime.Serialization.Json.DataContractJsonSerializer в сборке System.ServiceModel.Web.dll.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...