Десеріалізація даних 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


32
У візуальній студії також є опція Вставити JSON як класи в спеціальному меню «Правка-вставка».
Джонатан Аллен

76

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

JsonConvert.DeserializeObject<MyAccount>(myjsondata);

Будь-які поля, що відсутні у даних JSON, повинні просто залишити NULL.

ОНОВЛЕННЯ:

Якщо рядок 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Рядок нижче проста відповідь від виклику 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;для доступу до цих класів.


7
Любіть використовувати <динамічний>. Однак мені довелося це зробити, щоб він працював: результат ["Id"]. Значення та результат ["Ім'я"]. Значення
fredw

1
Хоча динаміка є приємною альтернативою, наведені вище приклади не використовують "магічні рядки", а натомість використовують сильно типізовані дженерики, які оновлюються під час звичайних методів рефакторингу VS (за винятком випадків, коли вигляд у 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 рядки


2
це PopulateObject - Популяція відсутня в об'єктній моделі.
амок

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

Ось скріншот налаштувань, які я використовував, дозволяючи отримати дивні назви властивостей, які веб-версія не могла врахувати.

Генератор класу Xamasoft JSON


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 ... ви хочете використовувати Список для цих типів даних. і Список для скажіть accountstatusexpmaxdate ... Я думаю, що у вас, наприклад, дати неправильно відформатовані дати, настільки невизначені, що ще є неправильним у вашому прикладі.

Це стара публікація, але хотіла відзначити проблеми.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.