суббота, 22 июня 2013 г.

Json и C#.


Json - формат передачи данных, который буквально за несколько лет стал вровень с XML, а в некоторых аспектах даже перегнал его. Facebook, Twitter, и многие популярные сервисы используют этот формат для выдачи данных в своих API.
Существует куча сторонних библиотек для работы с Json в .net, но зачем пользоваться сторонними продуктами, если в .net присутствует всё необходимое для этих целей.
Для примера будем использовать такой Json:

  {  
   "firstName": "Иван",  
   "lastName": "Иванов",  
   "address": {  
     "streetAddress": "Московское ш., 101, кв.101",  
     "city": "Ленинград",  
     "postalCode": 101101  
   },  
   "phoneNumbers": [  
     "812 123-1234",  
     "916 123-4567"  
   ]  
 }  

Как видите, имеется некая информация о человеке: имя, фамилия, адрес, и его контактные телефоны. При работе с Json, данные могут сериализовываться/десериализовываться. То есть, любой класс описанный в C# мы свободно можем сериализовать и получить чистый Json. Десериализация работает по-другому: на входе у нас имеется Json, а на выходе должен получиться экземпляр класса, со свойствами описанными в Json. Естественно, что этот класс должен быть перед десериализацией подобающим образом определён.
Опишем класс Person:
 public class Person  
   {  
     public string firstName { get; set; }  
     public string lastName { get; set; }  
     public Address address { get; set; }  
     public string[] phoneNumbers { get; set; }  
     public class Address  
     {  
       public string streetAddress { get; set; }  
       public string city { get; set; }  
       public int postalCode { get; set; }  
     }  
   }  
Как видите, свойства класса должны соответствовать полям, что находятся в Json.
Для работы с Json необходимо подключить Reference System.Runtime.Serialization. Там находится пространство имён Json, его и будем использовать. Нам нужен класс DataContractJsonSerializer. Код для десериализции будет следующий:
 using System.Runtime.Serialization.Json;  
 ...
  private void ParseBtn_Click(object sender, EventArgs e)  
     {        
       DataContractJsonSerializer json = new DataContractJsonSerializer(typeof(Person));  
       string fileContent=System.IO.File.ReadAllText("json.txt");  
       Person person = (Person)json.ReadObject(new System.IO.MemoryStream(Encoding.UTF8.GetBytes(fileContent)));  
     }  

Один из конструкторов DataContractJsonSerializer принимает тип класса в который будет происходить десериализация. Далее мы вызываем метод ReadObject, принимающий поток с данными Json. 
Если всё было сделано верно, то экземпляр класса Person будет создан и его свойства будут заполнены в соответствии с Json.
В дальнейшем, можно вывести эти данные, например так:
  FNameLbl.Text = person.firstName;  
       LNameLbl.Text = person.lastName;  
       PhonesLbl.Text = string.Join(", ", person.phoneNumbers);  
       StreetLbl.Text = person.address.streetAddress;  
       CityLbl.Text = person.address.city;  
       PostalLbl.Text = person.address.postalCode.ToString();  
Всё должно работать, но есть один нюанс. Как вы наверно уже поняли, десериализация требует наличия в классе свойств, которые по имени совпадают с полями в Json. Так, например, в нашем Json все поля написаны с прописной буквы, и потому в классе Person тоже все свойства описаны идентично. Сами понимаете, что с точки зрения C# это недопустимо. Свойства класса должны начинаться с заглавной буквы.
Дабы решить эту проблему и не привязывать десериализацию к именам свойств класса, необходимо использовать атрибуты свойств и классов.
Наш класс с использованием атрибутов будет выглядеть так:
   [DataContract]  
   public class Person  
   {  
     [DataMember(Name = "firstName")]  
     public string FirstName { get; set; }  
     [DataMember(Name = "lastName")]  
     public string LastName { get; set; }  
     [DataMember(Name = "address")]  
     public Address PersonAddress { get; set; }  
     [DataMember(Name = "phoneNumbers")]  
     public string[] PersonPhones { get; set; }  
   }  
   [DataContract]  
   public class Address  
   {  
     [DataMember(Name = "streetAddress")]  
     public string StreetAddress { get; set; }  
     [DataMember(Name = "city")]  
     public string City { get; set; }  
     [DataMember(Name = "postalCode")]  
     public int Postal { get; set; }  
   }  
Теперь наш класс соответствует негласным правилам C#.
Скачать исходники можно тут 

10 комментариев:

  1. Как десериализовать такое

    {

    "Window":{
    "Official Webite":{"value":"Официальный Сайт"}
    }

    }

    То есть так получить value в Window

    ОтветитьУдалить
    Ответы
    1. public class Root
      {
      [DataMember(Name = "Window")]
      public Window Wnd { get; set; }
      }
      public class Window
      {
      [DataMember(Name = "Official Webite")]
      public Site OffSite { get; set; }
      }
      public class Site
      {
      [DataMember(Name = "value")]
      public string Value{ get; set; }
      }

      Удалить
  2. А как быть если у меня такой массив?
    "vehicles":[
    {
    "id":53839,
    },
    {,
    "id":268511792,
    }
    ],

    Уже голову сломал..... никак не хочет читать это

    ОтветитьУдалить
    Ответы
    1. Этот комментарий был удален автором.

      Удалить
    2. [DataContract]
      public class Vehicles
      {
      [DataMember(Name = "vehicles")]
      public Id[] Vehicles { get; set; }
      }

      [DataContract]
      public class Id
      {
      [DataMember(Name = "id")]
      public string Id { get; set; };
      }

      Удалить
  3. Этот комментарий был удален автором.

    ОтветитьУдалить
  4. "id": 5,
    "first_name": "Иван",
    "last_name": "Горпинич",
    "photo_id": 15,
    "online": 0,
    "screen_name": "power007"
    а как такое можно десериализовать?

    ОтветитьУдалить