How to get json.net to serialize class members derived from List <T>?
I created the PagedResult<T> : List<T> class, which contains several items added to work with one of our components. However, when I run json deserializer, it only serializes the list. If I mark up a derived class with [JsonObject] and [JsonProperty] , then it will only serialize members of the derived class, not the list. How do I get both?
By default, Json.Net will handle any class that implements IEnumerable as an array. You can override this behavior by decorating the class with the [JsonObject] attribute, but then, as you have already seen, only the properties of the object are serialized. The list itself will not be serialized because it is not provided through a public property (rather, it is provided through the GetEnumerator() method).
If you want both, you can either do as @Konrad suggested and provide an open property in your derived class to JsonConverter list, or you can write your own JsonConverter to serialize everything as you like. The following is an example of the latter approach.
Assuming your PagedResult<T> class looks something like this:
class PagedResult<T> : List<T> { public int PageSize { get; set; } public int PageIndex { get; set; } public int TotalItems { get; set; } public int TotalPages { get; set; } } You can make a converter for this as follows:
class PagedResultConverter<T> : JsonConverter { public override bool CanConvert(Type objectType) { return (objectType == typeof(PagedResult<T>)); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { PagedResult<T> result = (PagedResult<T>)value; JObject jo = new JObject(); jo.Add("PageSize", result.PageSize); jo.Add("PageIndex", result.PageIndex); jo.Add("TotalItems", result.TotalItems); jo.Add("TotalPages", result.TotalPages); jo.Add("Items", JArray.FromObject(result.ToArray(), serializer)); jo.WriteTo(writer); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { JObject jo = JObject.Load(reader); PagedResult<T> result = new PagedResult<T>(); result.PageSize = (int)jo["PageSize"]; result.PageIndex = (int)jo["PageIndex"]; result.TotalItems = (int)jo["TotalItems"]; result.TotalPages = (int)jo["TotalPages"]; result.AddRange(jo["Items"].ToObject<T[]>(serializer)); return result; } } (Note also that with this approach, the [JsonObject] and [JsonProperty] not required, since the knowledge of what to serialize is encapsulated in the converter class.)
Here is a demo demonstrating the converter in action:
class Program { static void Main(string[] args) { PagedResult<string> result = new PagedResult<string> { "foo", "bar", "baz" }; result.PageIndex = 0; result.PageSize = 10; result.TotalItems = 3; result.TotalPages = 1; JsonSerializerSettings settings = new JsonSerializerSettings(); settings.Converters.Add(new PagedResultConverter<string>()); settings.Formatting = Formatting.Indented; string json = JsonConvert.SerializeObject(result, settings); Console.WriteLine(json); } } Output:
{ "PageSize": 10, "PageIndex": 0, "TotalItems": 3, "TotalPages": 1, "Items": [ "foo", "bar", "baz" ] } If you don't want to write your own JsonConverter or use the JSON attributes ( JsonObjectAttribute ), then you can simply use the extension methods:
class PagedResult<T> : List<T> { public int PageSize { get; set; } public int PageIndex { get; set; } public int TotalItems { get; set; } public int TotalPages { get; set; } } public static class Extensions { public static string ToPagedJson<T>(this PagedResult<T> pagedResult) { return JsonConvert.SerializeObject(new { PageSize = pagedResult.PageSize, PageIndex = pagedResult.PageIndex, TotalItems = pagedResult.TotalItems, TotalPages = pagedResult.TotalPages Items = pagedResult }); } }