Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Замечание
Эта статья о DataContractJsonSerializer. В большинстве сценариев, связанных с сериализированием и десериализацией JSON, рекомендуется использовать API в пространстве имен System.Text.Json.
JSON (нотация объектов JavaScript) — это формат данных, специально предназначенный для использования кодом JavaScript, выполняемым на веб-страницах в браузере. Это формат данных по умолчанию, используемый службами AJAX ASP.NET, созданными в Windows Communication Foundation (WCF).
Этот формат также можно использовать при создании служб AJAX без интеграции с ASP.NET . В этом случае XML является значением по умолчанию, но можно выбрать JSON.
В результате, если вам нужна поддержка JSON, но вы не создаете службу AJAX, DataContractJsonSerializer позволяет напрямую сериализовать объекты .NET в данные JSON и десериализовать такие данные обратно в экземпляры типов .NET. Описание этого действия см. в разделе "Практическое руководство. Сериализация и десериализация данных JSON".
При работе с JSON поддерживаются те же типы .NET, с некоторыми исключениями, которые поддерживаются DataContractSerializer. Список поддерживаемых типов см. в разделе "Типы, поддерживаемые сериализатором контракта данных". Сюда входят большинство примитивных типов, большинство типов массивов и коллекций, а также сложные типы, использующие DataContractAttribute и DataMemberAttribute.
Сопоставление типов .NET с типами JSON
В следующей таблице показано соответствие типов .NET и типов JSON/JavaScript при сопоставлении с процедурами сериализации и десериализации.
Типы .NET | JSON/JavaScript | Примечания. |
---|---|---|
Все числовые типы, например Int32Decimal илиDouble | Номер | Специальные значения, такие как Double.NaN , Double.PositiveInfinity и Double.NegativeInfinity не поддерживаются и приводят к недопустимому json. |
Enum | Номер | См. раздел "Перечисления и JSON" далее в этой статье. |
Boolean | Булев тип | |
String, Char | Струна | |
TimeSpan, , GuidUri | Струна | Формат этих типов в формате JSON аналогичен формату в XML (например, временной интервал в формате длительности ISO 8601, GUID в формате "12345678-ABCD-ABCD-ABCD-1234567890AB" и URI в его обычной строковой форме, как "http://www.example.com" ). Для получения точной информации смотрите Справочник по схеме контракта данных. |
XmlQualifiedName | Струна | Формат — "name:namespace" (всё, что находится перед первым двоеточием, является именем). Либо имя, либо пространство имен может отсутствовать. Если пространство имен отсутствует, двоеточие также может быть опущено. |
Command типа System.Windows.Input.ICommand |
Массив чисел | Каждое число представляет значение одного байта. |
DateTime | DateTime или String | См. статью "Даты и время" и JSON далее в этой статье. |
DateTimeOffset | Сложный тип | См. статью "Даты и время" и JSON далее в этой статье. |
Типы XML и ADO.NET (XmlElement, XElement. Массивы XmlNode, ISerializable, DataSet"). |
Струна | См. раздел "Типы XML" и "JSON" этой статьи. |
DBNull | Пустой сложный тип | -- |
Коллекции, словари и массивы | Массив | См. раздел "Коллекции, словари и массивы" этой статьи. |
Сложные типы (с применением DataContractAttribute или SerializableAttribute) | Сложный тип | Элементы данных становятся членами составного типа данных в JavaScript. |
Сложные типы, реализующие интерфейс ISerializable | Сложный тип | То же, что и другие сложные типы, но некоторые ISerializable типы не поддерживаются — подробнее см. Поддержка ISerializable. |
Null значение для любого типа |
Недействительный | Типы значений, допускающие значение NULL, также поддерживаются и сопоставляются с JSON так же, как и типы значений, не допускающие значения NULL. |
Перечисления и JSON
Значения элементов перечисления обрабатываются как числа в ФОРМАТЕ JSON, что отличается от того, как они обрабатываются в контрактах данных, где они включаются в качестве имен элементов. Дополнительные сведения об обработке контракта данных см. в разделе "Типы перечисления в контрактах данных".
Например, если у вас есть
public enum Color {red, green, blue, yellow, pink}
, сериализацияyellow
создает число 3, а не строку "желтый".Все
enum
члены поддерживают сериализацию. Атрибуты EnumMemberAttribute и NonSerializedAttribute игнорируются, если используются.Можно десериализовать несуществующее
enum
значение - например, значение 87 может быть десериализовано в предыдущее перечисление цветов, даже если соответствующее имя для этого цвета не определено.Флаги
enum
не являются особыми и обрабатываются так же, как и любые другиеenum
.
Даты и время и JSON
Формат JSON не поддерживает непосредственно даты и время. Однако они очень часто используются и ASP.NET AJAX обеспечивает специальную поддержку этих типов. При использовании прокси ASP.NET AJAX в .NET тип DateTime полностью соответствует типу DateTime
в JavaScript.
При неиспользовании ASP.NET тип DateTime представлен в формате JSON в виде строки с особым форматом, описанным в разделе "Дополнительные сведения" данной темы.
DateTimeOffset представляется в формате JSON в виде сложного типа: {"DateTime":d ateTime", "OffsetMinutes":offsetMinutes}. Элемент
offsetMinutes
— это смещение по местному времени относительно времени по Гринвичу (GMT), также называемого координированным всемирным временем (UTC), связанное с расположением интересующего события. ЧленdateTime
представляет момент времени, когда произошло событие интереса (опять же, он становитсяDateTime
в JavaScript, когда используется ASP.NET AJAX, и строкой, когда это не так). При сериализацииdateTime
член всегда сериализуется в GMT. Таким образом, если описывается 3:00 утра по времени Нью-Йорка,dateTime
имеет временной компонент 8:00, аoffsetMinutes
составляет 300 (минус 300 минут или 5 часов относительно GMT).Замечание
DateTime и DateTimeOffset объекты, сериализованные в JSON, сохраняют только сведения в миллисекундах точности. Значения меньше миллисекунды (микросекунды/наносекунды) теряются при сериализации.
Типы XML и JSON
Типы XML становятся строками JSON.
Например, если элемент данных "q" типа XElement содержит <abc/>, json имеет значение {"q":"<abc/>"}.
Существуют некоторые специальные правила, определяющие способ упаковки XML- дополнительные сведения см. в разделе "Дополнительные сведения" далее в этой статье.
Если вы используете ASP.NET AJAX и не хотите использовать строки в JavaScript, а вместо этого хотите использовать XML DOM, задайте значение XML для свойства ResponseFormat или установите свойство WebGetAttribute в XML для ResponseFormat.
Коллекции, словари и массивы
Все коллекции, словари и массивы представлены в формате JSON в виде массивов.
Любая настройка, которая использует этот CollectionDataContractAttribute объект, игнорируется в представлении JSON.
Словари не являются способом работы непосредственно с JSON. Словарь<string, object> возможно, не поддерживается в WCF так же, как ожидалось при взаимодействии с другими технологиями JSON. Например, если "abc" сопоставляется с "xyz", а "def" сопоставляется с 42 в словаре, представление JSON не выглядит как {"abc":"xyz","def":42}, а как [{"Key":"abc","Value":"xyz"},{"Key":"def","Value":42}]
Если вы хотите работать с JSON напрямую (доступ к ключам и значениям динамически без предварительного определения жесткого контракта), у вас есть несколько вариантов:
Рекомендуется использовать пример сериализации JSON с слабой типизацией (AJAX).
Рассмотрите возможность использования интерфейса ISerializable и конструкторов десериализации. Эти два механизма позволяют получить доступ к парам ключ/значение JSON при сериализации и десериализации соответственно, но не работают в сценариях частичного доверия.
Рекомендуется использовать сопоставление JSON с XML вместо использования сериализатора.
Полиморфизм в контексте сериализации относится к возможности сериализации производного типа, в котором ожидается базовый тип. Существуют специальные JSON-правила при полиморфном использовании коллекций, например, при назначении коллекции на Object. Эта проблема более подробно рассматривается в разделе "Дополнительные сведения" далее в этой статье.
Дополнительные сведения
Порядок элементов данных
Порядок элементов данных не важен при использовании JSON. В частности, даже если Order задано, данные JSON по-прежнему можно десериализировать в любом порядке.
Типы JSON
Тип JSON не должен соответствовать предыдущей таблице при десериализации. Например, Int
обычно сопоставляется с числом JSON, но также может быть успешно десериализирован из строки JSON, если эта строка содержит допустимое число. То есть {"q":42} и {"q":"42"} допустимы, если есть Int
член данных с именем q.
Полиморфизм
Полиморфная сериализация подразумевает возможность сериализации производного типа в тех случаях, когда ожидается его базовый тип. Поддержка сериализации JSON в WCF осуществляется аналогично тому, как поддерживается сериализация XML. Например, можно сериализовать MyDerivedType
там, где ожидается MyBaseType
, или сериализовать Int
там, где ожидается Object
.
При десериализации производного типа информация о типе может быть утеряна, если ожидается базовый тип, за исключением случаев, когда происходит десериализация сложного типа. Например, если Uri сериализуется там, где ожидается Object, это приводит к строке JSON. Если эта строка десериализуется обратно в Object, возвращается .NET String. Десериализатор не знает, что строка изначально была типом Uri. Как правило, при ожидании Object все строки JSON десериализируются как строки .NET, а все массивы JSON, используемые для сериализации коллекций, словарей и массивов, десериализируются как .NET Array типа Object, независимо от того, какой был исходный тип. Булево значение JSON сопоставляется с .NET Boolean. Однако при ожидании Object числа JSON десериализируются как .NET Int32, Decimal или Double, где наиболее подходящий тип выбирается автоматически.
При десериализации в тип интерфейса, DataContractJsonSerializer работает так, как если бы объявленный тип был объектом.
При работе с собственными базовыми и производными типами, использование KnownTypeAttribute, ServiceKnownTypeAttribute или эквивалентного механизма обычно требуется. Например, если у вас есть операция с Animal
возвращаемым значением и она фактически возвращает экземпляр Cat
, производный от Animal
, вам следует либо применить KnownTypeAttribute к типу Animal
, либо ServiceKnownTypeAttribute к операции и указать тип Cat
в этих атрибутах. Дополнительные сведения см. в разделе "Известные типы контракта данных".
Дополнительные сведения о работе полиморфной сериализации и обсуждении некоторых ограничений, которые должны соблюдаться при использовании, см. в разделе "Дополнительные сведения" далее в этой статье.
Управление версиями
Функции версионирования контракта данных, включая интерфейс IExtensibleDataObject, полностью поддерживаются в формате JSON. Кроме того, в большинстве случаев можно десериализировать тип в одном формате (например, XML), а затем сериализовать его в другой формат (например, JSON) и сохранить данные в IExtensibleDataObject. Дополнительные сведения см. в Forward-Compatible договорах о данных. Помните, что JSON не упорядочен так, чтобы все сведения о заказе были потеряны. Кроме того, JSON не поддерживает несколько пар "ключ-значение" с одинаковым именем ключа. Наконец, все операции на IExtensibleDataObject изначально полиморфны - то есть их производные типы назначаются на Object, базовый тип для всех типов.
JSON в URL-адресах
При использовании конечных точек ASP.NET AJAX с командой HTTP GET (с помощью атрибута WebGetAttribute ) входящие параметры отображаются в URL-адресе запроса вместо текста сообщения. JSON поддерживается даже в URL-адресе запроса, поэтому, если у вас есть операция, принимающая Int
с именем "число" и Person
сложного типа с именем "p", то URL-адрес может выглядеть следующим образом.
http://example.com/myservice.svc/MyOperation?number=7&p={"name":"John","age":42}
Если вы используете элемент управления диспетчера сценариев AJAX ASP.NET для вызова службы, этот URL-адрес автоматически создается прокси-сервером и не отображается. JSON нельзя использовать в URL-адресах на конечных точках не ASP.NET AJAX.
Дополнительные сведения
Поддержка ISerializable
Поддерживаемые и неподдерживаемые типы ISerializable
Как правило, типы, реализующие ISerializable интерфейс, полностью поддерживаются при сериализации и десериализации JSON. Однако некоторые из этих типов (включая некоторые типы .NET Framework) реализуются таким образом, чтобы аспекты сериализации, относящиеся к JSON, приводили к неправильной десериализации:
При этом ISerializableтип отдельных элементов данных никогда заранее не известен. Это приводит к полиморфной ситуации, аналогичной десериализации типов в объект. Как упоминалось ранее, это может привести к потере сведений о типе в ФОРМАТЕ JSON. Например, тип, сериализующий
enum
в своей ISerializable реализации и пытающийся десериализировать непосредственно вenum
(без соответствующего приведения), завершается ошибкой, так какenum
сериализуется с помощью чисел в формате JSON, и числа JSON десериализуются во встроенные числовые типы .NET (Int32, десятичный или double). Таким образом, утрачен факт, что число раньше было значениемenum
.Тип ISerializable , зависящий от определенного порядка десериализации в конструкторе десериализации, также может не выполнять десериализацию некоторых данных JSON, так как большинство сериализаторов JSON не гарантируют какой-либо определенный порядок.
Типы фабрики
Хотя интерфейс в целом поддерживается в формате JSON, все типы, которые требуют использования функции "фабричный тип" (то есть возвращающие экземпляр типа, отличного от IObjectReference, который реализует интерфейс), не поддерживаются.
Формат проводной связи DateTime
DateTime значения отображаются как строки JSON в виде "/Date(700000+0500)/", где первое число (700000 в приведенном примере) — это число миллисекунд в часовом поясе GMT, обычное время (без перехода на летнее время) с полуночи, 1 января 1970 года. Число может быть отрицательным для представления более ранних времён. Часть, состоящая из "+0500" в примере, является необязательной и указывает, что время является Local типом - то есть должно быть преобразовано в локальный часовой пояс при десериализации. Если он отсутствует, время десериализуется как Utc. Фактическое число ("0500" в этом примере) и его знак (+ или -) игнорируются.
При сериализации времена DateTime, Local и Unspecified записываются со смещением, а Utc записывается без него.
Клиентский JavaScript-код ASP.NET AJAX автоматически преобразует такие строки в экземпляры JavaScript DateTime
. Если существуют другие строки, имеющие аналогичную форму, которая не является типом DateTime в .NET, они также преобразуются.
Преобразование выполняется только в том случае, если экранируются символы "/", (т. е. JSON выглядит как "\/Date(700000+0500)\/"), и по этой причине кодировщик JSON в WCF (включено через WebHttpBinding) всегда экранирует символ "/".
XML в строках JSON
XmlElement
XmlElement сериализуется в неизменном виде, без обёртки. Например, член данных "x" типа XmlElement , содержащего <abc/,> представлен следующим образом:
{"x":"<abc/>"}
Массивы XmlNode
Array объекты типа XmlNode упаковываются в элемент с именем ArrayOfXmlNode в стандартном пространстве имен контракта данных для типа. Если "x" представляет собой массив, содержащий узел атрибута "N" в пространстве имен "ns", который содержит "value" и пустой узел элемента "M", представление выглядит следующим образом.
{"x":"<ArrayOfXmlNode xmlns=\"http://schemas.datacontract.org/2004/07/System.Xml\" a:N=\"value\" xmlns:a=\"ns\"><M/></ArrayOfXmlNode>"}
Атрибуты в пустом пространстве имен в начале массивов XmlNode (перед другими элементами) не поддерживаются.
Типы IXmlSerializable, такие как XElement и DataSet
ISerializable типы подразделяются на "типы контента", "типы dataSet" и "типы элементов". Определения этих типов см. в разделе XML и типов ADO.NET в контрактах данных.
Типы Content и DataSet сериализуются аналогично Array объектам, описанным XmlNode в предыдущем разделе. Они упаковываются в элемент, имя и пространство имен которого соответствуют имени контракта данных и пространству имен заданного типа.
Типы элементов, такие как XElement, сериализуются, как есть, аналогично XmlElement, ранее обсуждавшимся в этой статье.
Полиморфизм
Сохранение сведений о типе
Как упоминалось ранее, полиморфизм поддерживается в JSON с некоторыми ограничениями. JavaScript является слабо типизированным языком, и идентичность типов, как правило, не представляет проблемы. Однако при использовании JSON для обмена данными между строго типизированной системой (.NET) и слабо типизированной системой (JavaScript) полезно сохранить удостоверение типа. Например, типы с именами контрактов данных "Square" и "Circle" являются производными от типа с именем контракта данных "Shape". Если "Circle" отправляется из .NET в JavaScript и позже возвращается в метод .NET, который ожидает "Shape", полезно, чтобы на стороне .NET знали, что объект, о котором идет речь, изначально был "Circle" - в противном случае любые данные, относящиеся к производному типу, например, элемент данных radius у класса "Circle", могут быть потеряны.
Чтобы сохранить удостоверение типа, при сериализации сложных типов в JSON можно добавить "указание типа", а десериализатор распознает указание и действует соответствующим образом. "указание типа" : это пара "ключ-значение JSON" с именем ключа "__type" (два символа подчеркивания, следующими за словом "тип"). Значением является строка JSON формы "DataContractName:DataContractNamespace" (все, что находится до первой двоеточия, — это имя). Используя предыдущий пример, "Circle" можно сериализовать следующим образом.
{"__type":"Circle:http://example.com/myNamespace","x":50,"y":70,"radius":10}
Указание типа очень похоже на xsi:type
атрибут, определенный стандартом экземпляра схемы XML, и используется при сериализации или десериализации XML.
Элементы данных, называемые "__type", запрещены из-за потенциального конфликта с указанием типа.
Уменьшение размера типовых подсказок
Чтобы уменьшить размер сообщений JSON, префикс пространства имен для данных контракта по умолчанию (http://schemas.datacontract.org/2004/07/
) заменяется символом "#". (Чтобы сделать эту замену обратимой, используется правило экранирования: если пространство имен начинается с символов "#" или "\", они дополняются дополнительным символом "\"). Таким образом, если "Circle" является типом в пространстве имен .NET "MyApp.Shapes", то его пространство имен контракта данных по умолчанию — это http://schemas.datacontract.org/2004/07/MyApp
. Фигуры и представление JSON приведены следующим образом.
{"__type":"Circle:#MyApp.Shapes","x":50,"y":70,"radius":10}
При десериализации распознаются как сокращенные (#MyApp.Shapes), так и полные (http://schemas.datacontract.org/2004/07/MyApp.Shapes
) имена.
Положение указания типа в объектах JSON
Обратите внимание, что указание типа должно отображаться сначала в представлении JSON. Это единственный случай, когда порядок пар "ключ-значение" важен при обработке JSON. Например, ниже указан недопустимый способ указать указание типа.
{"x":50,"y":70,"radius":10,"__type":"Circle:#MyApp.Shapes"}
Как WCF, так и клиентские страницы ASP.NET AJAX всегда сначала выдают подсказку типа.
Указания типов применяются только к сложным типам
Нет способа вывести указание типа для несложных типов. Например, если операция имеет тип возвращаемого Object значения, но возвращает круг, представление JSON может быть как показано ранее, а сведения о типе сохраняются. Однако если Uri возвращается, представление JSON будет строкой, и факт того, что строка использовалась для представления Uri, теряется. Это относится не только к примитивным типам, но и к коллекциям и массивам.
Когда выводятся подсказки типа
Указания типов могут значительно увеличить размер сообщения (один из способов устранения этой проблемы — использовать более короткие пространства имен контракта данных, если это возможно). Поэтому следующие правила определяют, создаются ли подсказки типов:
При использовании ASP.NET AJAX подсказки типов всегда генерируются по возможности, даже если нет базового или производного присваивания, например, даже если объект типа Circle присваивается объекту того же типа Circle. (Это необходимо для полного включения процесса вызова из слабо типизированной среды JSON в строго типизированную среду .NET без удивительной потери информации.)
При использовании служб AJAX без интеграции ASP.NET подсказки типов создаются только при наличии базового или производного связывания, то есть, если Circle связан с Shape или Object, но не при связывании с Circle. Это обеспечивает минимальную информацию, необходимую для правильной реализации клиента JavaScript, что повышает производительность, но не защищает от потери информации типа в неправильно разработанных клиентах. Избегайте полностью базовых и производных назначений на сервере, если вы хотите избежать решения этой проблемы на клиенте.
При использовании типа DataContractSerializer параметр конструктора
alwaysEmitTypeInformation
позволяет выбирать между двумя предыдущими режимами, при этом режим по умолчанию — "false
" (подсказки типа выдаются только при необходимости).
Повторяющиеся имена элементов данных
Сведения о производных типах присутствуют в том же объекте JSON вместе с сведениями о базовом типе и могут располагаться в любом порядке. Например, Shape
можно представить следующим образом.
{"__type":"Shape:#MyApp.Shapes","x":50,"y":70}
В то время как Circle может быть представлен следующим образом.
{"__type":"Circle:#MyApp.Shapes","x":50, "radius":10,"y":70}
Если базовый Shape
тип также содержал элемент данных с именем "radius
", это приводит к конфликту при сериализации (так как объекты JSON не могут иметь повторяющиеся имена ключей) и десериализации (поскольку неясно, "radius" относится к Shape.radius
или Circle.radius
). Таким образом, хотя концепция "скрытия свойств" (члены данных с одним и тем же именем в базовых и производных классах) обычно не рекомендуется в классах контрактов данных, это фактически запрещено при работе с JSON.
Полиморфизм и типы, реализующие IXmlSerializable
IXmlSerializable типы могут быть полиморфно назначены друг другу, как обычно, если выполняются требования известных типов, в соответствии с обычными правилами контракта данных. Однако сериализация типа IXmlSerializable вместо Object приводит к потере информации о типе, так как результатом является строка JSON.
Полиморфизм и определенные типы интерфейсов
Запрещено сериализовать тип коллекции или тип, реализующий IXmlSerializable, если ожидается неколлекционный тип, который не является IXmlSerializable (кроме случаев с Object). Например, пользовательский интерфейс IMyInterface
и тип MyType
реализуют как IEnumerable<T> типа int
, так и IMyInterface
. Запрещено возвращать MyType
операцию с типом возвращаемого значения IMyInterface
. Это связано с тем, что MyType
необходимо сериализовать в виде массива JSON и требуется указание типа, как указано ранее, нельзя добавить указание типа для массивов, только для составных типов.
Известные типы и конфигурация
Все механизмы известных типов, используемые ими DataContractSerializer , также поддерживаются таким же образом DataContractJsonSerializer. Оба сериализатора считывают один и тот же элемент конфигурации, <dataContractSerializer> в <system.runtime.serialization>, чтобы обнаружить известные типы, добавленные через файл конфигурации.
Коллекции, назначенные объекту
Коллекции, назначенные объекту, сериализуются так, как если они являются коллекциями, реализующими IEnumerable<T>: массив JSON с каждой записью, которая имеет указание типа, если это сложный тип. Например, List<T> типа Shape
, назначен в Object, выглядит следующим образом.
[{"__type":"Shape:#MyApp.Shapes","x":50,"y":70},
{"__type":"Shape:#MyApp.Shapes","x":58,"y":73},
{"__type":"Shape:#MyApp.Shapes","x":41,"y":32}]
При десериализации обратно в Object:
Shape
должен находиться в списке известных типов. Наличие List<T> в составе известных типов типаShape
не оказывает влияния. Обратите внимание, что в этом случае не нужно добавлятьShape
, к известным типам при сериализации – это выполняется автоматически.Коллекция десериализуется как Array типа Object, которая содержит экземпляры
Shape
.
Производные коллекции, назначенные базовым коллекциям
При назначении производной коллекции базовой коллекции коллекция обычно сериализуется так, как если бы она была коллекцией базового типа. Однако если тип элемента производной коллекции не может быть назначен типу элемента базовой коллекции, создается исключение.
Подсказки типов и словари
Когда словарь назначается к Object, каждая запись ключа и значения в словаре обрабатывается как если бы она была назначена к Object и получает подсказку типа.
Когда сериализуются типы словарей, объект JSON, содержащий элементы "Ключ" и "Значение", не зависит от установки alwaysEmitTypeInformation
и включает указание типа только в случае, если это требуется предыдущими правилами коллекции.
Допустимые имена ключей JSON
Сериализатор XML-кодирует имена ключей, которые не являются допустимыми XML-именами. Например, элемент данных с именем "123" будет иметь закодированное имя, например "_x0031__x0032__x0033_", так как "123" является недопустимым именем XML-элемента (начинается с цифры). Аналогичная ситуация может возникнуть с некоторыми международными наборами символов, недопустимыми в XML-именах. Описание этого эффекта XML для обработки JSON см. в разделе "Сопоставление между JSON и XML".