Десериализация данных JSON в C # с использованием JSON.NET

144

Я относительно новичок в работе с данными C # и JSON и ищу руководства. Я использую C # 3.0, с .NET3.5SP1 и JSON.NET 3.5r6.

У меня есть определенный класс C #, который мне нужно заполнить из структуры JSON. Однако не каждая структура JSON для записи, получаемой из веб-службы, содержит все возможные атрибуты, определенные в классе C #.

Я делал то, что кажется неправильным, сложным способом, и просто выбирал каждое значение одно за другим из JObject и преобразовывал строку в желаемое свойство класса.

JsonSerializer serializer = new JsonSerializer();
var o = (JObject)serializer.Deserialize(myjsondata);

MyAccount.EmployeeID = (string)o["employeeid"][0];

Каков наилучший способ десериализации структуры JSON в класс C # и обработки возможных пропущенных данных из источника JSON?

Мой класс определяется как:

  public class MyAccount
  {

    [JsonProperty(PropertyName = "username")]
    public string UserID { get; set; }

    [JsonProperty(PropertyName = "givenname")]
    public string GivenName { get; set; }

    [JsonProperty(PropertyName = "sn")]
    public string Surname { get; set; }

    [JsonProperty(PropertyName = "passwordexpired")]
    public DateTime PasswordExpire { get; set; }

    [JsonProperty(PropertyName = "primaryaffiliation")]
    public string PrimaryAffiliation { get; set; }

    [JsonProperty(PropertyName = "affiliation")]
    public string[] Affiliation { get; set; }

    [JsonProperty(PropertyName = "affiliationstatus")]
    public string AffiliationStatus { get; set; }

    [JsonProperty(PropertyName = "affiliationmodifytimestamp")]
    public DateTime AffiliationLastModified { get; set; }

    [JsonProperty(PropertyName = "employeeid")]
    public string EmployeeID { get; set; }

    [JsonProperty(PropertyName = "accountstatus")]
    public string AccountStatus { get; set; }

    [JsonProperty(PropertyName = "accountstatusexpiration")]
    public DateTime AccountStatusExpiration { get; set; }

    [JsonProperty(PropertyName = "accountstatusexpmaxdate")]
    public DateTime AccountStatusExpirationMaxDate { get; set; }

    [JsonProperty(PropertyName = "accountstatusmodifytimestamp")]
    public DateTime AccountStatusModified { get; set; }

    [JsonProperty(PropertyName = "accountstatusexpnotice")]
    public string AccountStatusExpNotice { get; set; }

    [JsonProperty(PropertyName = "accountstatusmodifiedby")]
    public Dictionary<DateTime, string> AccountStatusModifiedBy { get; set; }

    [JsonProperty(PropertyName = "entrycreatedate")]
    public DateTime EntryCreatedate { get; set; }

    [JsonProperty(PropertyName = "entrydeactivationdate")]
    public DateTime EntryDeactivationDate { get; set; }

  }

И пример JSON для разбора:

{
    "givenname": [
        "Robert"
    ],
    "passwordexpired": "20091031041550Z",
    "accountstatus": [
        "active"
    ],
    "accountstatusexpiration": [
        "20100612000000Z"
    ],
    "accountstatusexpmaxdate": [
        "20110410000000Z"
    ],
    "accountstatusmodifiedby": {
        "20100214173242Z": "tdecker",
        "20100304003242Z": "jsmith",
        "20100324103242Z": "jsmith",
        "20100325000005Z": "rjones",
        "20100326210634Z": "jsmith",
        "20100326211130Z": "jsmith"
    },
    "accountstatusmodifytimestamp": [
        "20100312001213Z"
    ],
    "affiliation": [
        "Employee",
        "Contractor",
        "Staff"
    ],
    "affiliationmodifytimestamp": [
        "20100312001213Z"
    ],
    "affiliationstatus": [
        "detached"
    ],
    "entrycreatedate": [
        "20000922072747Z"
    ],
    "username": [
        "rjohnson"
    ],
    "primaryaffiliation": [
        "Staff"
    ],
    "employeeid": [
        "999777666"
    ],
    "sn": [
        "Johnson"
    ]
}
Говинд Мальвия
источник

Ответы:

277

использование

var rootObject =  JsonConvert.DeserializeObject<RootObject>(string json);

Создайте свои классы на JSON 2 C #


Документация по Json.NET: Сериализация и десериализация JSON с помощью Json.NET

w.donk
источник
32
Visual Studio также имеет параметр «Вставить JSON как классы» в специальном меню «Редактировать-вставить».
Джонатан Аллен
76

Вы пытались использовать универсальный метод DeserializeObject?

JsonConvert.DeserializeObject<MyAccount>(myjsondata);

Любые пропущенные поля в данных JSON должны просто быть пустыми.

ОБНОВИТЬ:

Если строка JSON является массивом, попробуйте это:

var jarray = JsonConvert.DeserializeObject<List<MyAccount>>(myjsondata);

jarrayдолжен быть List<MyAccount>.

ДРУГОЕ ОБНОВЛЕНИЕ:

Исключение, которое вы получаете, не согласуется с массивом объектов - я думаю, что у сериализатора возникли проблемы с вашим типизированным по словарю accountstatusmodifiedbyсвойством.

Попробуйте исключить accountstatusmodifiedby свойство из сериализации и посмотрите, поможет ли это. Если это произойдет, вам может потребоваться представить это свойство по-другому.

Документация: Сериализация и десериализация JSON с Json.NET

Дейв Сверски
источник
Спасибо. Однако я получаю сообщение об ошибке «Невозможно десериализовать массив JSON в тип« System.String ».» когда он пытается десериализовать (например) массив данного имени JSON в строку класса GivenName. Атрибуты JSON, которые я определил как строковые в классе C #, представляют собой только когда-либо одноэлементные массивы. Вот почему я начал выбирать значения по одному, когда столкнулся с проблемой такого рода в процессе десериализации. Другое волшебство, которое я пропускаю?
Итак ... DateTime AccountStatusExpiration(например) не обнуляется, как определено в коде. Что нужно сделать, чтобы сделать его обнуляемым? Просто поменять DateTimeна DateTime??
Хэмиш Грубиджан
50

Ответ воспроизведен с https://stackoverflow.com/a/10718128/776476

Вы можете использовать dynamicтип C #, чтобы упростить задачу. Этот метод также упрощает ре-факторинг, поскольку он не зависит от волшебных строк.

Json

Приведенная jsonниже строка представляет собой простой ответ на вызов HTTP API и определяет два свойства: Idи Name.

{"Id": 1, "Name": "biofractal"}

C #

Используйте JsonConvert.DeserializeObject<dynamic>()для десериализации этой строки в динамический тип, а затем просто получите доступ к ее свойствам обычным способом.

var results = JsonConvert.DeserializeObject<dynamic>(json);
var id = results.Id;
var name= results.Name;

Примечание . Ссылка NuGet для сборки NewtonSoft: http://nuget.org/packages/newtonsoft.json . Не забудьте добавить: using Newtonsoft.Json;для доступа к этим классам.

biofractal
источник
7
Люблю использование <dynamic>. Тем не менее, я должен был сделать это, чтобы заставить его работать: result ["Id"]. Value and result ["Name"]. Value
fredw
1
Хотя динамическая и является хорошей альтернативой, в приведенных выше примерах не используются «магические строки», а вместо этого используются строго типизированные обобщения, которые обновляются в ходе обычных методов рефакторинга VS (если только внутри View в MVC, который выходит за рамки этого вопроса).
gcoleman0828
4
У вас все еще есть «волшебные струны», теперь они просто скрыты благодаря использованию динамического!
Ян Рингроз
В самом деле, биофрактал имеет обратное отношение к «волшебным струнам», как указывает @ IanRingrose. Динамический объект, возвращаемый по DeserializeObject<dynamic>определению, не «проверен на тип». Таким образом, следующие строки results.Idи results.Nameне могут быть проверены во время компиляции. Любые ошибки будут возникать во время выполнения. Сравните это со строго типизированным вызовом, таким как DeserializeObject<List<MyAccount>>. Ссылки на эти свойства могут быть подтверждены во время компиляции. Использование dynamic, хотя это может быть удобно, вводит «волшебные строки» в качестве имен свойств; снижение надежности ИМХО.
ToolmakerSteve
8

Ты можешь использовать:

JsonConvert.PopulateObject(json, obj);

здесь: jsonстрока json, objцелевой объект. См: пример

Примечание: PopulateObject()не удалит данные списка obj, после того Populate(), как obj'sчлен списка будет содержать свои исходные данные и данные из строки json

bbants
источник
2
это PopulateObject - Заполнить не в объектной модели.
amok
1
Это сработало идеально для меня! У меня были проблемы, когда у меня была довольно сложная строка JSON, когда я пытался привести ее к объектам C #, что-либо, помеченное как «NotNull», могло бы отсутствовать в объекте, даже если оно изначально присутствовало в строке JSON. Очень странно. Я использовал этот метод, и он работал ОТЛИЧНО!
jward01
3

Опираясь на ответ bbant, это мое полное решение для десериализации JSON с удаленного URL.

using Newtonsoft.Json;
using System.Net.Http;

namespace Base
{
    public class ApiConsumer<T>
    {
        public T data;
        private string url;

        public CalendarApiConsumer(string url)
        {
            this.url = url;
            this.data = getItems();
        }

        private T getItems()
        {
            T result = default(T);
            HttpClient client = new HttpClient();

            // This allows for debugging possible JSON issues
            var settings = new JsonSerializerSettings
            {
                Error = (sender, args) =>
                {
                    if (System.Diagnostics.Debugger.IsAttached)
                    {
                        System.Diagnostics.Debugger.Break();
                    }
                }
            };

            using (HttpResponseMessage response = client.GetAsync(this.url).Result)
            {
                if (response.IsSuccessStatusCode)
                {
                    result = JsonConvert.DeserializeObject<T>(response.Content.ReadAsStringAsync().Result, settings);
                }
            }
            return result;
        }
    }
}

Использование будет как:

ApiConsumer<FeedResult> feed = new ApiConsumer<FeedResult>("http://example.info/feeds/feeds.aspx?alt=json-in-script");

Где FeedResultкласс, сгенерированный с использованием Xamasoft JSON Class Generator

Вот снимок экрана с настройками, которые я использовал, учитывая странные имена свойств, которые веб-версия не может объяснить.

Xamasoft JSON Class Generator

Кайл Фалконер
источник
1

Я нашел свой Я построил мой объект неправильно. Я использовал http://json2csharp.com/, чтобы сгенерировать свой класс объектов из JSON. Как только у меня был правильный Oject, я мог читать без проблем. Норбит, ошибка Нуба. Думаю, я добавлю это на случай, если у вас возникнет та же проблема.

Адам
источник
1

Вы можете попробовать проверить некоторые из генераторов классов онлайн для получения дополнительной информации. Тем не менее, я считаю, что некоторые ответы были полезны. Вот мой подход, который может быть полезен.

Следующий код был сделан с учетом динамического метода.

dynObj = (JArray) JsonConvert.DeserializeObject(nvm);

foreach(JObject item in dynObj) {
 foreach(JObject trend in item["trends"]) {
  Console.WriteLine("{0}-{1}-{2}", trend["query"], trend["name"], trend["url"]);
 }
}

Этот код в основном позволяет получить доступ к членам, содержащимся в строке Json. Просто по-другому без необходимости занятий. query, trendИ urlявляются объекты , содержащиеся в строке Json.

Вы также можете использовать этот сайт . Не доверяйте классам на 100%, но вы поняли идею.

Эдвард Ньюгейт
источник
0

Предполагая, что ваши примеры данных верны, ваше имя и другие записи, заключенные в квадратные скобки, являются массивами в JS ... вы захотите использовать List для этих типов данных. и список, скажем, accountstatusexpmaxdate ... Я думаю, что в вашем примере даты неверно отформатированы, хотя и неуверенно относительно того, что еще неверно в вашем примере.

Это старый пост, но хотелось бы отметить проблемы.

Tracker1
источник