Creating a C# class from convoluted JSON object where dynamic variable would be class name

Viewed 66

I'm trying to query the most important information from wikipedia articles using the wikimedia API. Within my code I have the following line:

WikiArticleModel article = await response.Content.ReadAsAsync<WikiArticleModel>().ConfigureAwait(false);

This is a example of the way my JSON object looks like when testing on the article from the planet Jupiter:

{
    "batchcomplete": "",
    "query": {
        "normalized": [
            {
                "from": "jupiter",
                "to": "Jupiter"
            }
        ],
        "pages": {
            "38930": {
                "pageid": 38930,
                "ns": 0,
                "title": "Jupiter",
                "extract": ">>> Her comes the first section of the article, which I deleted to make 
                  this shorter <<<",
                "description": "Fifth planet from the Sun and largest planet in the Solar System",
                "descriptionsource": "local",
                "original": {
                    "source": "https://upload.wikimedia.org/wikipedia/commons/2/2b/Jupiter_and_its_shrunken_Great_Red_Spot.jpg",
                    "width": 940,
                    "height": 940
                }
            }
        }
    }
}

The question is now, how should my WikiArticleModel class look like? Using the build-in VS Studio too "Paste JSON as class" I get the following result:

public class WikiArticleModel
{
    public string batchcomplete { get; set; }
    public Query query { get; set; }
}

public class Query
{
    public Normalized[] normalized { get; set; }
    public Pages pages { get; set; }
}

public class Pages
{
    public _38930 _38930 { get; set; }
}

public class _38930
{
    public int pageid { get; set; }
    public int ns { get; set; }
    public string title { get; set; }
    public string extract { get; set; }
    public string description { get; set; }
    public string descriptionsource { get; set; }
    public Original original { get; set; }
}

public class Original
{
    public string source { get; set; }
    public int width { get; set; }
    public int height { get; set; }
}

public class Normalized
{
    public string from { get; set; }
    public string to { get; set; }
}

Which is OK and what I would expect, except for the class _38930, which is just the pageid and would change with every query. What is the correct way to deserialize this object? Or is it a better approach to just get a object as response and fill the model class manually in this case?

Additionally, I actually only need certain parameters from the JSON object (e.g. title, extract, description,..) - is there a way to get these directly into a model class containing only the properties I need?

2 Answers

This is the way to do it natively, Pages is actually a Dictionary<int, Page>.

public class WikiArticleModel
{
    public string batchcomplete { get; set; }
    public Query query { get; set; }
}

public class Query
{
    public List<Normalized> normalized { get; set; }

    public Pages pages { get; set; }
}

[JsonDictionary]
public class Pages : Dictionary<int, Page> { }

public class Page
{
    public int pageid { get; set; }
    public int ns { get; set; }
    public string title { get; set; }
    public string extract { get; set; }
    public string description { get; set; }
    public string descriptionsource { get; set; }
    public Original original { get; set; }
}

public class Original
{
    public string source { get; set; }
    public int width { get; set; }
    public int height { get; set; }
}

public class Normalized
{
    public string from { get; set; }
    public string to { get; set; }
}

I would recommend using JObject.Parse from Newtonsoft.Json.Linq and parsing it based on the name of the keys that page has. Something like this,

public class Page
{
    public int pageid { get; set; }
    public int ns { get; set; }
    public string title { get; set; }
    public string extract { get; set; }
    public string description { get; set; }
    public string descriptionsource { get; set; }
    public Original original { get; set; }
}

public class Original
{
    public string source { get; set; }
    public int width { get; set; }
    public int height { get; set; }
}

public class Normalized
{
    public string from { get; set; }
    public string to { get; set; }
}

// you can deserialize like this,

var jobj = JObject.Parse(json);
var props = ((JObject)jobj["query"]["pages"]).Properties();
Page page = JsonConvert.DeserializeObject<Page>(jobj["query"]["pages"][props.First().Name].ToString());

You can use a foreach loop on each of the properties of pages and iterate through those as well (instead of using props.First().

Related