JSON.NET — исключить свойства определенного типа во время выполнения

#c# #.net #json #serialization #json.net

#c# #.net #json #сериализация #json.net

Вопрос:

Мне интересно, как исключить / лишить сериализации определенные свойства данного типа (ов) (или их коллекции) с использованием Json.Сетевая библиотека? Я пытался написать свой собственный преобразователь контрактов (наследующий от DefaultContractResolver), но безуспешно.

Я знаю, что я мог бы использовать DataAnnotations, украсив исключенные свойства с помощью ScriptIgnoreAttribute, но это неприменимо в моем сценарии. Сериализованные объекты могут быть практически любыми, поэтому я не знаю, какие свойства исключить во время разработки. Я знаю только типы свойств, которые не следует сериализовывать.

Это выглядит как довольно простая задача, но, к сожалению, я нигде не смог найти достойного решения…

КСТАТИ — я не привязан к Json.Сетевая библиотека — если это можно легко сделать с помощью сериализаторов по умолчанию / других .NET JSON, это было бы одинаково хорошим решением для меня.

Обновить

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

В принципе, типы объектов, которые я получаю и сериализую, могут иметь динамические свойства типа, наследуемого от IDynamicMetaObjectProvider. Я не собираюсь описывать все детали, но DynamicMetaObject, возвращаемый из метода GetMetaObject этих объектов, не имеет DynamicMetaObject.Реализован метод GetDynamicMemberNames (выдает исключение NotImplementedException…). Подводя итог — проблема в том, что эти объекты (которые мне нужно исключить) не позволяют перечислять их свойства, что Json.СЕТЕВОЙ сериализатор пытается сделать это за кулисами. Я всегда заканчиваю тем, что выдаю NotImplementedException.

Комментарии:

1. Итак, чтобы уточнить, вы хотите исключить из сериализации свойства, значения которых реализуют IDynamicMetaObjectProvider ? Я очень хорошо знаком с динамическими типами, но я не уверен, что в вашем случае вы на самом деле сериализуете. Похоже, что вы сериализуете обычные классы и хотите исключить определенные свойства для них, которые ссылаются на динамические экземпляры. В то же время вы также указали, что вам неизвестны типы или базовые типы элементов, которые вы сериализуете — просто они иногда ссылаются на свойства, которые являются динамическими и не могут быть сериализованы. Пожалуйста, опубликуйте простой пример.

2. Именно это! Корневой объект, который я сериализую, может быть любым, но иногда у него может быть динамическое свойство (которое ссылается на некоторый объект IDynamicMetaObjectProvider или на коллекцию некоторых), который я хочу исключить из сериализации. Сериализованный объект не является динамическим, динамичны только исключенные свойства.

3. Достаточно ли для вашей потребности не полностью исключать эти свойства, вместо этого сериализовав их как пустые объекты? Таким образом, свойство с именем Foo, экземпляр которого является динамическим, будет сериализовано как Foo: {}

4. @Jamie: Этого было бы вполне достаточно, да. Они не будут десериализованы в . СЕТЕВЫЕ объекты, но только повторно используемые на стороне клиента в JS.

5. Знаете ли вы тип самих значений динамических свойств? Например, являются ли они экземплярами DynamicObject или какого-либо другого известного типа, реализующего IDynamicMetaObjectProvider , или они также являются полностью случайными классами из других библиотек, неизвестных во время разработки?

Ответ №1:

Я пробовал как сериализацию WCF JSON, так и System.Web.Script.Сериализация.JavaScriptSerializer. Я обнаружил, что если вы хотите надежно контролировать процесс сериализации и не хотите быть связанными атрибутами и хаками, чтобы все работало, то JavaScriptSerializer это правильный путь. Это включено в .ЧИСТЫЙ стек и позволяет создавать и регистрировать JavaScriptConverter подклассы для выполнения пользовательской сериализации типов.

Единственное ограничение, которое я обнаружил, которое может вызвать у вас проблему, заключается в том, что вы не можете легко зарегистрировать конвертер для преобразования всех подклассов Object (иначе говоря, один конвертер для управления ими всеми). Вам действительно нужно знать общие базовые классы или предварительно зарегистрировать набор типов путем сканирования сборки. Однако сериализация свойств полностью оставлена на ваше усмотрение, поэтому вы можете решить, используя простое отражение, какие свойства сериализовать и как.

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

В моем случае я поддерживал клиентский API, который не совсем соответствовал структуре серверных классов, и мы хотели гораздо более простой синтаксис JSON, который был бы удобен для глаз, и JavaScriptSerializer каждый раз делал свое дело. Просто дайте мне знать, если вам понадобятся несколько примеров кода для начала.

Комментарии:

1. @Jamie: Спасибо за информацию, но, к сожалению, я не могу знать точный тип сериализованного объекта во время разработки, поскольку они могут быть любыми. Я знаю только точные типы, которые не должны быть сериализованы

2. Нужно ли выполнять пользовательскую сериализацию для всех типов, найденных в . Сам NET framework или просто типы в ваших собственных проектах или сборках, на которые вы ссылаетесь. Если использовать более позднюю версию, вы могли бы легко зарегистрировать преобразователи для всех типов в своих сборках (меньше исключений по вашему выбору). Это может быть простой передачей чего-то вроде assembly. GetTypes(). Где(t => t.IsClass) во время процесса регистрации.

3. @Jamie: Приложение имеет модульную структуру, поэтому сборки, из которых исходят объекты, могут быть добавлены / удалены во время выполнения. Кстати, сканирование всех сборок и сбор всех типов только для выполнения простой сериализации JSON выглядит излишеством.

4. Согласен. К счастью, мне не пришлось прибегать к этому подходу. Однако это мое единственное разочарование в JavaScriptSerializer , поскольку это требует, чтобы вы указывали поддерживаемые типы для конвертера заранее, вместо того, чтобы разрешить вам указывать предикат Func<Type, bool> для указания поддержки на лету, а затем кэшировать результат во внутренней хэш-таблице, чтобы не оказывать негативного влияния на производительность.

5. Поскольку вам известны точные типы экземпляров, во время выполнения которых вы хотите избежать сериализации, вы можете зарегистрировать, JavaScriptConverter которое возвращает эти типы из свойства SupportedTypes . Затем просто переопределите сериализацию в конвертере и верните пустую хэш-таблицу (или потенциально null), указывающую, что вы не хотите ничего сериализовывать.

Ответ №2:

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

Комментарии:

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

Ответ №3:

Рассматривали ли вы возможность использования свойства префикса ShouldSerialize для исключения свойства вашего конкретного типа во время выполнения?

 public class Employee
{
  public string Name { get; set; }
  public Employee Manager { get; set; }

  public bool ShouldSerializeManager()
  {
    return (Manager != this);
  }
}
  

Комментарии:

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