Работаем с JSON в C#. Сериализация и десериализация

На сегодняшний день JSON входит в число основных форматов представления сложных структур и обмена данными. Поэтому все основные языки программирования имеют встроенную поддержку для работы с ним. C# не исключение.

Данная статья написана в 2016 году для .NET Framework. Работе с JSON в .NET Core посвящена отдельная, более поздняя, статья- «Работаем с JSON в .NET Core (C#). Сериализация и десериализация«.

Сериализация в JSON

Существует два основных способа преобразования (сериализации) объектов .NET в JSON:

Использование класса DataContractJsonSerializer

Класс DataContractJsonSerializer сериализует объекты .NET в поток (Stream) в виде текста в формате JSON или XML сопоставимый с JSON.

Для того чтобы сериализация объекта стала возможна он должен быть отмечен атрибутом DataContract, а его члены подлежащие сериализации атрибутом DataMember. Чтобы эти атрибуты и сам класс DataContractJsonSerializer стали доступны, необходимо подключить к проекту сборку System.Runtime.Serialization.

В качестве примера создадим класс, который описывает организацию.

Создадим объект класса DataContractJsonSerializer для сериализации объектов класса Company.

Обратите внимание. Сериализуемый тип передаётся в качестве параметра конструктора.

Далее необходимо создать поток для сериализованных данных.

Или XmlWriter для XML сопоставимого с JSON. Например:

Сам процесс сериализации осуществляется с помощью метода WriteObject класса DataContractJsonSerializer. В качестве первого параметра ему передаётся либо поток, либо объект XmlWriter. В качестве второго параметра сериализуемый объект.

В итоге получается или поток с текстом в формате JSON.

Или XML сопоставимый с JSON.

Использование класса JavaScriptSerializer

Класс JavaScriptSerializer расположен в пространстве имён System.Web.Script.Serialization (требует подключения сборки System.Web.Extensions).

Его отличительная особенность в том, что он сериализует объекты в JSON возвращая обычную строку (string) с текстом в формате JSON. При этом совершенно не требуется обозначать сериализуемый объект или его члены специальными атрибутами.

Конструктор класса JavaScriptSerializer не требует передачи каких либо  параметров, а для выполнения сериализации объект передаётся в качестве единственного параметра методу Serialize.

Десериализация из JSON

Данные сериализованные в JSON можно получить обратно.

Использование класса DataContractJsonSerializer

Десериализация производится при помощи метода ReadObject. В качестве единственного параметра ему передаётся поток с текстом в формате JSON или объект XmlReader для чтения XML сопоставимого с JSON.

При десериализации из потока необходимо установить указатель на позицию соответствующую началу данных в формате JSON (в простейшем случае это 0)..

Если требуется десериализовать данные их XML сопоставимого с JSON, создаём XmlReader для этого XML (в данном примере XML хранится в файле) и передаём его в метод ReadObject класса DataContractJsonSerializer.

Использование класса JavaScriptSerializer

Для десериализации у класса JavaScriptSerializer есть два метода:

  • Deserialize.
    Обобщённый метод. Десериализует строку в формате JSON в заданный .NET объект;
  • DeserializeObject.
    Десериализует строку в формате JSON в словарь в формате Dictionary .

Пример использования метода Deserialize:

Пример использования метода DeserializeObject:

Метод Deserialize выполняет простую десериализацию, восстанавливая из JSON исходный объект. Метод DeserializeObject возвращает коллекцию, структура которой соответствует структуре исходного JSON. Что даёт возможность работать с JSON как с обычной коллекцией C# извлекая из неё для последующей обработки лишь необходимые данные.

Таким образом, в зависимости от требований задачи, можно выбрать наиболее подходящее средство для работы с JSON.

8 комментариев

  1. Добрый день. Спасибо за статью. Кратко и понятно. На тривиальных объектах все работает. Но как сериализовавывать коллекции, массивы, IDictionary коллекции и коллекции содержащие экземпляры классов наследников?

    1. Добрый день. Принцип, что показан в статье является общим для любых объектов. Те же массив или коллекцию можно также обозначить атрибутом [DataMember] и при сериализации они будут сконвертированы в JSON. Другое дело словари. Но и здесь есть сравнительно простое решение: serializer.Serialize(keys.ToDictionary(item => item.Key.ToString(), item => item.Value.ToString())); С коллекциями, которые содержат экземпляры классов наследников всё значительно сложнее. В общем случае такие коллекции объявляются с указанием базового класса, который не редко является ещё и абстрактным. Поэтому здесь лучше исходить из конкретной задачи.

      1. Добрый день. Хочу использовать JSon для сохранения настроек формы (Размер, Положение, Видимые колонки в гриде) в компактном читаемом виде. Провожу эксеперементы с JavaScriptSerializer. — По поводу коллекий вроде разобрался. Этот класс автоматически поддерживает коллекции и Dictionary без всяких дополнительных настроек (В отличии от XmlSerializer). — По поводу коллекий, содержащих классы наследники тоже понятно. Есть специальный конструктор. new JavaScriptSerializer(new SimpleTypeResolver()); При таком создании JavaScriptSerializer сохраняет вместе с объектом и его тип в тэге __type. { «__type»: «EhLib.FormSerializator, EhLibControlLibrary, Version=0.98.19.0, Culture=neutral, PublicKeyToken=null», «Location»: { «__type»: «System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a», «IsEmpty»: false, «X»: 78, «Y»: 78 }, … При десериализации тэг использутся для определения создаваемого типа. — Остался вопрос, по определению в run-time какие свойства класса сохранять. У меня не работают методы ShouldSerialize[Имя свойства]() для указания того нужно ли сохранять свойство. Подскажите, JavaScriptSerializer действительно не поддерживает ShouldSerialize? Если не поддерживает, то как этим управлять?

        1. Насколько мне известно, JavaScriptSerializer на самом деле не дружит с шаблоном ShouldSerialize. Но, к счастью это не единственный способ «отловить» изменение параметра. Как вариант можно использовать событие OnPropertyChanged. Сама по себе задача сохранения только изменённых свойств вполне решаема, только в данном случае придётся поработать руками, реализуя собственный механизм. Вообще, для настроек форм и не только в .NET есть специализированный инструментарий основанный на XML, но это, во-первых, тема для отдельного разговора, во-вторых, насколько это подходит для решаемой Вами задачи (всё-таки механизм работы с настройками «из коробки» хранит XML-файлы в профиле пользователя)…

  2. C механизмом XML уже поработали. Не устраивает генерация слишком «больших» файлов в которых 95% это описание и только 5% сами данные. Вот пример файла user.config который содержит информацию только об одной форме всего с одним гридом. user_xml.config А вот та же информация сохраненная в виде json. user_json.config

    1. Ни разу даже не слышал о том, чтобы из-за стандартного механизма работы с настройками падала производительность. К стати, ссылки, которые Вы приводите, из документации к библиотеке компонентов EhLib для Delphi. У Microsoft всё несколько по другому. В прочем, здесь уже «хозяин — барин». Если такой вариант по тем или иным причинам для Вас более приемлем, вопросов нет.

  3. Приветствую! Подскажите, как реализовать сериализацию в определенное место в файле json. Есть файл с данной структурой: { «Groups»: [ { «UniqueId»: «…», «Title»: «…», «Subtitle»: «…», «ImagePath»: «…», «Description»: «…», «Items»: [ { «UniqueId»: «…», «Title»: «…», «Subtitle»: «…», «ImagePath»: «…», «Description»: «…», «Time»: «…», «Client»: «…», «Agent»: «…», «DateM»: «…», «Revision»: «…», «Mashine»: «…», «IsNew»: false, «Tools»: [ { «Title»: «…», «Position»: «…», «Insert»: «…», «ImagePath»: «…», «LF»: «…», «Adapter»: «…», «VC»: «…», «FNFZ»: «….», «AP»: «…», «RE»: «…», «NVF»: «…», «DCTDZ»: «…» }, …… Можно добавлять/редактировать или удалять определенные строки?

    1. К сожалению, сериализовать или десериализовать можно только объект целиком.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *