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

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

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

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

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

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

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

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

Создадим объект класса 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 <string, object>.

Пример использования метода 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. К сожалению, сериализовать или десериализовать можно только объект целиком.

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

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