JsonConvert.DeserializeObject не преобразует объект ответа из JSON, как ожидалось - PullRequest
1 голос
/ 31 октября 2019

Я создаю оболочку ActiveCampaign V1.1 для C # .NET и создаю класс, который перечисляет списки контактов, используя (https://www.activecampaign.com/api/example.php?call=list_list)

Объект ответа, возвращаемый после JsonConvert.DeserializeObject<BasicListResponse>(JSON), имеет заполненные не-списочные свойства, но часть списка JSON не преобразуется.

Я пробовал следующие BasicListResponse реализации:

public class Result
{
    [JsonProperty("result_code")]
    public int ResultCode { get; set; }

    [JsonProperty("result_message")]
    public string ResultMessage { get; set; }

    [JsonProperty("result_output")]
    public string ResultOutput { get; set; }
}

public class BasicListResponse : Result
{      
    public Dictionary<string, BasicList> list { get; set; }
}

public class BasicList
{
    [JsonProperty("id")]
    public string Id { get; set; }

    [Required]
    [JsonProperty("name")]
    public string Name { get; set; }

    [JsonProperty("cdate")]
    public DateTime CreatedOn { get; set; }

    [JsonProperty("private")]
    public bool Private { get; set; }

    [JsonProperty("userid")]
    public string UserId { get; set; }

    [JsonProperty("subscriber_count")]
    public int SubscriberCount { get; set; }
}

Я также пытался использовать json2csharp или VS> Paste Special, но выходные данные имеют нумерованные классы, а не список объектов.

Ответ JSON выглядит следующим образом:

{
    "0": {
        "id": "1",
        "name": "xxxxx xxxxxx xxxxxxx",
        "cdate": "2019-10-27 22:43:23",
        "private": "0",
        "userid": "1",
        "subscriber_count": 1
    },
    "1": {
        "id": "2",
        "name": "yyyyy yyyyy yyyyy",
        "cdate": "2019-10-27 22:44:03",
        "private": "0",
        "userid": "1",
        "subscriber_count": 0
    },
    "result_code": 1,
    "result_message": "Success: Something is returned",
    "result_output": "json"
}

Я извлек Dictionary<T,T> в оболочке, носписок всегда НЕДЕЙСТВИТЕЛЕН.

Если я повторно сериализую BasicListResponse, результат будет следующим:

{ "list": null, "result_code": 1, "result_message": "Success: Something is returned", "result_output": "json" }

Я ожидаю, что будет использоваться BasicListResponseкак корневой объект, содержащий List / Array с тремя строковыми result_* свойствами.

Я ценю любую помощь, чтобы решить эту проблему.

Ответы [ 3 ]

3 голосов
/ 31 октября 2019

Ваш JSON в основном представляет собой словарь из пар string - BasicList, дополненный некоторыми другими свойствами общего результата. Этот формат затрудняет работу. (Было бы лучше, если бы данные словаря были в дочернем объекте в JSON.)

Самый простой способ справиться с этой ситуацией - воспользоваться преимуществом Json.Net " Extension Data "feature.

Вот что вам нужно сделать:

  1. Создайте личное Dictionary<string, JToken> свойство в вашем классе BasicListResponse и пометьте его атрибутом [JsonExtensionData]. Это отловит данные словаря во время десериализации.
  2. Измените ваше свойство list на List<BasicList> вместо Dictionary<string, BasicList>. (Я также переименовал бы его с правильной прописной буквой, чтобы соответствовать другим вашим свойствам.)
  3. Добавьте приватный метод OnDeserialized, как показано ниже, чтобы заполнить List<BasicList> из словаря расширений в конце десериализации. process.

Итак, ваш BasicListResponse класс должен выглядеть следующим образом:

public class BasicListResponse : Result
{
    public List<BasicList> List { get; set; }

    [JsonExtensionData]
    private Dictionary<string, JToken> Data { get; set; }

    [OnDeserialized]
    private void OnDeserialized(StreamingContext context)
    {
        List = Data?.OrderBy(kvp => kvp.Key)
                    .Select(kvp => kvp.Value.ToObject<BasicList>())
                    .ToList();
    }
}

Есть еще одна небольшая проблема, которую нам нужно решить: в вашем BasicList классе у вас естьопределили свойство Private как bool, но в JSON это строка, содержащая число. Это приведет к сбою десериализации из-за несовместимых типов. Чтобы заставить его работать, вы можете либо изменить Private на строку, а затем обработать интерпретацию значения в другом месте вашего кода, либо сделать что-то вроде этого:

public class BasicList
{
    ...

    [JsonProperty("private")]
    private string PrivateAsString { get; set; }

    [JsonIgnore]
    public bool Private
    {
        get { return PrivateAsString != "0"; }
        set { PrivateAsString = value ? "1" : "0"; }
    }

    ...
}

С этими изменениями выможно десериализовать в ваш класс BasicListResponse, и все должно работать правильно.

var result = JsonConvert.DeserializeObject<BasicListResponse>(json);

Рабочая демонстрация здесь: https://dotnetfiddle.net/9MmSuG

0 голосов
/ 31 октября 2019

Попробуйте

"list":{
    "0": {
        "id": "1",
        "name": "xxxxx xxxxxx xxxxxxx",
        "cdate": "2019-10-27 22:43:23",
        "private": "0",
        "userid": "1",
        "subscriber_count": 1
    },
    "1": {
        "id": "2",
        "name": "yyyyy yyyyy yyyyy",
        "cdate": "2019-10-27 22:44:03",
        "private": "0",
        "userid": "1",
        "subscriber_count": 0
    },
    "result_code": 1,
    "result_message": "Success: Something is returned",
    "result_output": "json"
}
0 голосов
/ 31 октября 2019

Не самое элегантное решение, но вы можете структурировать свои классы следующим образом:

public class Results
{
    [JsonExtensionData]
    public IDictionary<string, JToken> Items;

    [JsonProperty("result_code")]
    public long ResultCode { get; set; }

    [JsonProperty("result_message")]
    public string ResultMessage { get; set; }

    [JsonProperty("result_output")]
    public string ResultOutput { get; set; }
}

public class Result
{
    public long Id { get; set; }

    public string Name { get; set; }

    public DateTime Cdate { get; set; }

    public long Private { get; set; }

    public long UserId { get; set; }

    public long SubscriberCount { get; set; }

    public override string ToString()
    {
        return $"Id={Id},Name={Name},CDate={Cdate.ToString()},Private={Private},UserId={UserId},SubscriberCount={SubscriberCount}";
    }
}

Тогда вы можете десериализовать свой json и отобразить свой IDictionary<string, JToken> Items в IEnumerable<Result>. Обратите внимание, что JsonExtensionData должно быть IDictionary<string, JToken> в этом случае.

var deserializedJson = JsonConvert.DeserializeObject<Results>(json);

var results = deserializedJson
    .Items
    .Select(entry => new Result
    {
        Id = (long)entry.Value["id"],
        Name = (string)entry.Value["name"],
        Cdate = DateTime.TryParse((string)entry.Value["cdate"], out var cdate) ? cdate : DateTime.MinValue,
        Private = (long)entry.Value["private"],
        UserId = (long)entry.Value["userid"],
        SubscriberCount = (long)entry.Value["subscriber_count"]
    });

Console.WriteLine($"result_code={deserializedJson.ResultCode},result_message={deserializedJson.ResultMessage},result_output={deserializedJson.ResultOutput}");
foreach (var result in results)
{
    Console.WriteLine(result.ToString());
}

Выход:

result_code=1,result_message=Success: Something is returned,result_output=json
Id=1,Name=xxxxx xxxxxx xxxxxxx,CDate=27/10/2019 10:43:23 PM,Private=0,UserId=1,SubscriberCount=1
Id=2,Name=yyyyy yyyyy yyyyy,CDate=27/10/2019 10:44:03 PM,Private=0,UserId=1,SubscriberCount=0
...