2

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?

Roland Deschain
  • 2,211
  • 19
  • 50

2 Answers2

2

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; }
}
Roland Deschain
  • 2,211
  • 19
  • 50
NIKER
  • 464
  • 2
  • 9
  • This is, of course, the same as the answers in the proposed duplicate. – Heretic Monkey Jun 26 '20 at 15:25
  • @HereticMonkey In essence yes, but I for a newby like me concerning working with JSON, I this answer help me solve and understand my specific problem easier. While the general solution is the same, I would leave this as is. – Roland Deschain Jun 26 '20 at 15:29
  • @RolandDeschain Stack Overflow exists to create a knowledge base of questions and answers, not to have every individual permutation of characters that could possibly make a question. Having a single place to gather good, high quality answers to the same question is the goal. Duplicate questions are fine -- they can act as signposts pointing people to the correct answers -- but they should be closed as such. See [the help center article on duplicates](https://stackoverflow.com/help/duplicates). – Heretic Monkey Jun 26 '20 at 15:47
  • While the anwers are pretty much identical, the questions are not. In this example it is not immediatelly obvious you are looking at a dictionary (because of the nesting and single item), especially to an inexperienced programmer. – NIKER Jun 26 '20 at 16:02
1

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().

Jawad
  • 11,028
  • 3
  • 24
  • 37