Reputation: 25
I am creating an ActiveCampaign V1.1 wrapper for C#.NET and creating a class that lists the ContactLists using (https://www.activecampaign.com/api/example.php?call=list_list)
The response object returned after JsonConvert.DeserializeObject<BasicListResponse>(JSON)
does have non-list properties populated, but the list portion of the JSON is not converted.
I have tried the following BasicListResponse
implementations:
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; }
}
I have also tried to use json2csharp or VS > Paste Special but the output has numbered classes rather than a list of objects.
The JSON response is as follows:
{
"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"
}
I extracted the Dictionary<T,T>
in the wrapper but the list is always NULL.
If I re-serialize the BasicListResponse
the results are as follows
{ "list": null, "result_code": 1, "result_message": "Success: Something is returned", "result_output": "json" }
I expect the BasicListResponse
to be used as a root object containing a List/Array with three string result_*
properties.
I appreciate any help to resolve this.
Upvotes: 1
Views: 2999
Reputation: 129827
Your JSON basically represents a dictionary of string
-BasicList
pairs, comingled with some other properties about the overall result. This format makes it more difficult to work with. (It would have been better if the dictionary data were in child object in the JSON.)
The easiest way to handle this situation is to take advantage of Json.Net's "Extension Data" feature.
Here's what you would need to do:
Dictionary<string, JToken>
property in your BasicListResponse
class and mark it with the [JsonExtensionData]
attribute. This will catch the dictionary data during deserialization.list
property be a List<BasicList>
instead of a Dictionary<string, BasicList>
. (I would also rename it with proper capitalization to be consistent with your other properties.) OnDeserialized
method as shown below to populate the List<BasicList>
from the extension dictionary at the end of the deserialization process.So your BasicListResponse
class should look like this:
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();
}
}
There is one other small problem we need to address: in your BasicList
class you have defined the Private
property as bool
, but in the JSON it is a string containing a number. This will cause deserialization to fail due to incompatible types. To make it work, you can either change Private
to be a string and then handle the interpretation of the value elsewhere in your code, or you can do something like this:
public class BasicList
{
...
[JsonProperty("private")]
private string PrivateAsString { get; set; }
[JsonIgnore]
public bool Private
{
get { return PrivateAsString != "0"; }
set { PrivateAsString = value ? "1" : "0"; }
}
...
}
With these changes you can deserialize to your BasicListResponse
class and everything should work properly.
var result = JsonConvert.DeserializeObject<BasicListResponse>(json);
Working demo here: https://dotnetfiddle.net/9MmSuG
Upvotes: 3
Reputation: 26335
Not the most elegant solution, but you could structure your classes like this:
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}";
}
}
Then you could deserialize your json and map your IDictionary<string, JToken> Items
to a IEnumerable<Result>
. Note that JsonExtensionData
needs to be IDictionary<string, JToken>
in this case.
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());
}
Output:
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
Upvotes: 0
Reputation: 129
Try
"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"
}
Upvotes: -1