Краткий экскурс
Фактически каждый программист рано (или поздно) сталкивается с сериализацией.
Сериализация — это процесс перевода структуры данных в последовательность битов, или же в другую структуру данных, которую удобно хранить, передавать.
Десериализация — это обратный процесс. Процесс преобразования сериализованных данных в структуру данных.
Подобные вещи часто встречаются при обмене данными между клиентом и сервером, между двумя клиентами (P2P). В общем много где встречается. Моё первое знакомство было при работе с API одного из российских сервисов.
API — это Application Programming Interface. То есть прослойка между функциональной частью какого-либо программного обеспечения и вашего программного обеспечения.
Например — Вам хочется написать программу, которая будет отображать погоду в Москве. Вам не хочется устанавливать свои метеодатчики и другое оборудование.
Допустим, что есть сервис «Погода в России», который имеет свой API. И данный сервис на бесплатно (или же платно) дает доступ к нему вашим приложениям. Конечно — всегда можно написать парсер нужной страницы и выводить информацию. Но любые изменения в содержимом страницы могут привести к тому, что парсер придется тоже обновлять.
Глупо говорить о преимуществах API по сравнению с парсингом. Это скорость, удобство работы, а также, очень часто, более расширенный функционал.
Так что если сервис предлагает API — глупо отказываться.
Update 08.07.2019
Важный момент.
Класс, который Вы хотите сериализовать/десериализовать, должен соответствовать некоторым требованиям, чтобы не было проблем при работе с ним:
- У класса должен быть обязательно объявлен конструктор без параметров, или не объявлено конструкторов вовсе;
- Лучше использовать свойства, а не поля (с {get; и set;} как в примерах выше;
- Если у свойства отсутствует get’тер, или set’тер — то будьте готовы к тому, что часть функционала JSON-сериализатора будет работать не верно. Например:
- У вас отсутствует публичный GET’тер, но есть SET’тер. Тогда вы сможете десериализовать (JSON в объект) свойство объекта, но сериализовать (объект в JSON) его вы не сможете;
- У вас отсутствует публичный SET’тер, но есть GET’тер. Тогда вы не сможете десериализовать (JSON в объект) свойство объекта, но сможете его сериализовать (объект в JSON);
Функции преобразования и восстановления при сериализации
Не всегда сериализация может быть выполнена полностью автоматически. В жизни встречаются самые разные ситуации. Поэтому мы добавили возможность использовать «самописную» функцию обработки значений при записи в JSON и при чтении из JSON.
В методе ЗаписатьJSON() она называется Функция преобразования и описывается с помощью трёх параметров:
- ИмяФункцииПреобразования;
- МодульФункцииПреобразования;
- ДополнительныеПараметрыФункцииПреобразования.
В методе ПрочитатьJSON() она называется Функция восстановления и для неё есть аналогичные параметры:
- ИмяФункцииВосстановления;
- МодульФункцииВосстановления;
- ДополнительныеПараметрыФункцииВосстановления.
При записи в JSON эта функция полезна потому, что позволяет самостоятельно преобразовать в JSON те типы, которые не подлежат автоматическому преобразованию. Или даже совсем отказаться от их сериализации.
Например, так оказалось, что в записываемой структуре одно из значений — это ссылка на элемент справочника ПодразделенияОрганизаций. Такое значение (ссылка на объект 1С:Предприятия) не может быть автоматически сериализовано средствами платформы. Тогда, используя функцию преобразования, мы можем получить для этого значения его строковое представление в удобном виде. Например, в виде строки «ИП Петров: Отдел рекламы».
Результат выполнения примера:
{ "Фамилия": "Иванов", "Имя": "Иван", "Отчество": "Иванович", "Подразделение": "ИП Петров: Отдел рекламы", "Телефоны": }
При чтении из JSON функция восстановления может использоваться для того, чтобы преобразовать данные JSON в типы 1С, которые не могут являться результатом автоматического преобразования, или для того, чтобы самостоятельно (не автоматически) преобразовать даты JSON в даты 1С:Предприятия.
Десериализация
Теперь нужно эту строку десериализовать обратно в класс. Для этого, естественно, должен быть объявлен соответствующий класс, иначе программа не сможет ничего десериализовать.
Сам код десериализации следующий:
C#
string json = richTextBox.Text;
MyMusic newMusic = JsonConvert.DeserializeObject<MyMusic>(json);
1 |
stringjson=richTextBox.Text; MyMusic newMusic=JsonConvert.DeserializeObject<MyMusic>(json); |
Теперь выведем на экран содержимое десериализованного JSON’а:
На экран должно вывестись следующий текст:
Как видите — с JSON очень удобно работать. Но есть одна маленькая хитрость, которая сильно упростит вашу жизнь при десериализации. Чтобы вам вручную не создавать огромные классы анализируя пример JSON’а, который вам возвращает API — можно его сгенерировать автоматически. В этом вам поможет сайт Json2CSharp. Просто вставьте в него строку с JSON, которую вы получили из API и он сгенерирует вам необходимые классы. Основной класс будет всегда RootObject (в примере был MyMusic), но никто не мешает вам его переименовать в нужный.
Пароль к архиву: котодомик.рф
Сериализация примитивных типов и коллекций в JSON
Вторая группа средств работы с JSON хороша тем, что избавляет вас от рутинной работы по чтению/записи каждого отдельного значения или свойства. При чтении документы JSON отображаются в фиксированный набор типов платформы: Строка, Число, Булево, Неопределено, Массив, ФиксированныйМассив, Структура, ФиксированнаяСтруктура, Соответствие, Дата. Соответственно, в обратную сторону, композиция объектов этих типов позволяет сформировать в памяти и быстро записать в файл структуру JSON. Таким образом, чтение и запись небольшого объема JSON заранее известной структуры можно производить немногими строчками кода.
Основное назначение этих средств мы видим в обмене информацией с внешними системами, чтении конфигурационных файлов в формате JSON.
Сериализацию вы можете выполнять с помощью методов глобального контекста ПрочитатьJSON() и ЗаписатьJSON(). Они работают в связке с объектами ЧтениеJSON и ЗаписьJSON.
В качестве примера десериализации JSON можно рассмотреть чтение массива из двух объектов:
Код 1С:Предприятия, выполняющий десериализацию, может выглядеть следующим образом:
А пример сериализации (записи) в JSON может выглядеть так:
Результат записи:
{ "Фамилия": "Иванов", "Имя": "Иван", "Отчество": "Иванович", "Возраст": 40, "Женат": true, "Телефоны": }
Использование JSON в HTTP интерфейсах приложений
Автоматически генерируемый REST интерфейс прикладных решений
При обращении к REST интерфейсу прикладного решения вы можете получать ответ в формате JSON. Для этого в адресной строке вам нужно указать параметр $format=json. Либо указать MIME тип «application/json» в заголовке Accept HTTP запроса. Например:
Запрос:
GET /TestInfobase/odata/standard.odata/СправочникДляТестов?$format=json HTTP/1.1 MaxDataServiceVersion: 3.0;NetFx Accept: application/json Accept-Charset: UTF-8 User-Agent: Microsoft ADO.NET Data Services
Ответ:
HTTP/1.1 200 OK Content-Length: 9429 Content-Type: application/json;charset=utf-8 Server: Microsoft-IIS/7.5 DataServiceVersion: 3.0 X-Powered-By: ASP.NET Date: Mon, 12 Aug 2013 09:44:07 GMT { "odata.metadata":"http://host/svc/$metadata#СправочникДляТестов", "value":[ { "Ref_Key":guid'cc6a7df3-8cfe-11dc-8ca0-000d8843cd1b', "DataVersion":"AAAAAQAAAAE", "DeletionMark":false, "Parent_Key":guid'bbb079ae-8c51-11db-a9b0-00055d49b45e', "IsFolder":false, "Code":000000025, "Description":"Пинетки", "Поставщик_Key":guid'd1cb82a7-8e8b-11db-a9b0-00055d49b45e', "Поставщик@navigationLinkUrl":"СправочникДляТестов(guid'cc6a7df3-8cfe-11dc-8ca0-000d8843cd1b')/Поставщик", "РеквизитХранилище_Type": "image/jpeg", "РеквизитХранилище_Base64Data@mediaReadLink": "Catalog_ДемоСправочник(guid'cf2b1a24-1b96-11e3-8f11-5404a6a68c42')/РеквизитХранилище_Base64Data", "РеквизитХранилище_Base64Data": <строка с закодированными данными> … }, {…}, {…} ] }
Вы можете управлять объёмом передаваемой информации за счёт изменения детальности представления метаданных в выгрузке. Существуют три уровня: Nometadata, Minimalmetadata и Fullmetadata. По-умолчанию (на примере вверху) используется средний уровень — Minimalmetadata. На уровне Nometadata объём передаваемой информации минимальный, а на уровне Fullmetadata — максимальный. Однако при этом нужно понимать, что сокращение объёма передаваемой информации приводит к более интенсивным вычислениям на клиенте. И наоборот, когда вся информация включается в выгрузку, объём вычислений на клиенте будет минимальным.
Детальность представления метаданных вы можете указать, например, в адресной строке.
Сведения о метаданных не передаются:
GET /TestInfobase/odata/standard.odata/СправочникДляТестов/?$format=application/json;odata= minimalmetadata
Вся информация о метаданных включается в выгрузку:
GET /TestInfobase/odata/standard.odata/СправочникДляТестов/?$format=application/json;odata=fullmetadata
HTTP-сервисы прикладного решения
HTTP-сервисы, реализованные в прикладном решении, также могут возвращать ответ в формате JSON. Для этого вам проще всего сформировать тело ответа в JSON, получить его как строку, а затем установить из этой строки тело HTTP ответа сервиса. При этом желательно указать, что BOM (Byte Order Mark, метка порядка байтов) использоваться не должна.
Последний параметр (ИспользованиеByteOrderMark.НеИспользовать) вы можете и не указывать, если режим совместимости конфигурации не установлен, или он больше чем Версия8_3_5. Потому что в этом случае BOM автоматически будет использоваться только для кодировок UTF-16 и UTF-32, а для UTF-8, UTF-16LE/UTF-16BE, UTF-32LE/UTF-32BE и других она использоваться не будет.
Взаимодействие со сторонними HTTP сервисами
При взаимодействии со сторонними HTTP интерфейсами у вас также может возникнуть необходимость формирования запросов к ним в формате JSON. В этом случае алгоритм ваших действий будет аналогичным. Формируете тело запроса в JSON. Получаете тело в виде строки. Из этой строки устанавливаете тело HTTP запроса. BOM не используете.
Дальнейшее развитие
Мы думаем над тем, чтобы предоставить вам возможность сериализации в JSON прикладных типов 1С:Предприятия: ссылок, объектов, наборов записей и т.д. Поэтому есть вероятность появления ещё одного, третьего уровня средств работы с JSON. Этот уровень позволит вам преобразовывать в JSON любые типы 1С:Предприятия, для которых поддерживается XDTO-сериализация в XML.
Сериализация типа Дата
Данные типа Дата сериализуются в JSON автоматически, а вот обратное преобразование (десериализация) может быть выполнено не всегда. JSON не содержит типа Дата, значения даты представляются в нём строкой. Конечно, существуют некоторые форматы представления дат, но вообще говоря, внешний вид такой строки может быть самым разнообразным.
Для сериализации типа Дата в JSON у метода ЗаписатьJSON() вы можете использовать параметр НастройкиСериализации. Это объект встроенного языка, который позволяет указать, в каком варианте будет записана дата (UTC, локальная дата или локальная дата со смещением) и в каком формате (ISO, JavaScript или Microsoft).
Код 1С:Предприятия:
Текст JSON:
При чтении даты из JSON всё обстоит сложнее. В параметре ИменаСвойствСоЗначениямиДата вы можете перечислить те свойства JSON, значения которых нужно преобразовать в дату 1С:Предприятия (тип Дата). А в параметре ОжидаемыйФорматДаты вам нужно указать, в каком формате эти данные содержатся в JSON (ISO, JavaScript или Microsoft).
Текст JSON:
Код 1С:Предприятия:
Однако если окажется, что в какой-то момент формат данных JSON не совпадает с ожидаемым форматом, будет вызвано исключение.
В такой ситуации, для большей универсальности, вы можете включить те же самые свойства JSON в массив, подлежащий обработке функцией восстановления — ИменаСвойствДляОбработкиВосстановления. И уже в функции восстановления вы самостоятельно десериализуете даты JSON, в каком бы формате они ни были представлены.
Сериализация
В данном примере мы рассмотрим библиотеку «Newtonsoft.Json» для работы с JSON. Почему её? Потому что она удобная.
Для начала её нужно добавить в references.
Для этого нужно нажать ПКМ на References и выбрать «Добавить ссылку…».
Тут можно найти его в поиске (если уже использовали), скачать его через NuGet, или же просто добавить как обычную библиотеку через «Обзор».
На всякий случай оставлю библиотеку тут: скачать Newtonsoft.Json.dll.
Теперь можно приступить к коду.
Для начала нужно добавить NameSpace библиотеки в код. Для этого добавим в using следующую строку:
C#
using System;
// Далее ваши библиотеки
using Newtonsoft.Json;
1 |
usingSystem; // Далее ваши библиотеки usingNewtonsoft.Json; |
Теперь нужно описать класс, который будем сериализовать и десериализовать:
C#
class MyMusic
{
public Track[] Tracks { get; set; }
}
class Track
{
public string Artist { get; set; }
public string Album { get; set; }
public string Title { get; set; }
public string Year { get; set; }
}
1 |
classMyMusic { publicTrackTracks{get;set;} } classTrack { publicstringArtist{get;set;} publicstringAlbum{get;set;} publicstringTitle{get;set;} publicstringYear{get;set;} } |
Будем считать, что пишем программу для ведения учета домашней музыкальной коллекции. Базовый класс — MyMusic, вспомогательный класс — Track.
Теперь добавим несколько треков:
C#
MyMusic myCollection = new MyMusic();
myCollection.Tracks = new Track;
myCollection.Tracks = new Track()
{
Artist=»Artist1″,
Album=»Album1″,
Title=»Title1″,
Year=»2015″
};
myCollection.Tracks = new Track()
{
Artist = «Artist2»,
Album = «Album2»,
Title = «Title2»,
Year = «2015»
};
myCollection.Tracks = new Track()
{
Artist = «Artist3»,
Album = «Album3»,
Title = «Title3»,
Year = «2015»
};
1 |
MyMusic myCollection=newMyMusic(); myCollection.Tracks=newTrack3; myCollection.Tracks=newTrack() { Artist=»Artist1″, Album=»Album1″, Title=»Title1″, Year=»2015″ }; myCollection.Tracks1=newTrack() { Artist=»Artist2″, Album=»Album2″, Title=»Title2″, Year=»2015″ }; myCollection.Tracks2=newTrack() { Artist=»Artist3″, Album=»Album3″, Title=»Title3″, Year=»2015″ }; |
Теперь сериализуем этот класс и выведем на RichTextBox:
C#
string serialized = JsonConvert.SerializeObject(myCollection);
richTextBox.Text = serialized;
1 |
stringserialized=JsonConvert.SerializeObject(myCollection); richTextBox.Text=serialized; |
В результате чего мы должны получить такую строку на выходе:
{«Tracks»:}
1 | {«Tracks»{«Artist»»Artist1″,»Album»»Album1″,»Title»»Title1″,»Year»»2015»},{«Artist»»Artist2″,»Album»»Album2″,»Title»»Title2″,»Year»»2015»},{«Artist»»Artist3″,»Album»»Album3″,»Title»»Title3″,»Year»»2015»}} |