argamanza
argamanza

Reputation: 1142

Serialization of JSON with unknown key

I'm using the json_serializable pub package for all my RESTful API needs, and it worked great, but now i'm trying to serialize content from Wikipedia.

My problem is with the response structure, one of the keys is unknown beforehand and dynamic according to the returned page ID, here's an example:

I would like to get only the intro part of a page, my query is: https://en.wikipedia.org/w/api.php?action=query&format=json&prop=extracts&redirects=1&exintro=1&explaintext=1&titles=Stack%20Overflow

The response will be:

{
    "batchcomplete": "",
    "query": {
        "pages": {
            "21721040": {
                "pageid": 21721040,
                "ns": 0,
                "title": "Stack Overflow",
                "extract": "Stack Overflow is a privately held website, the flagship site of ......."
            }
        }
    }
}

Eventually i would like to get only the internal extract string, but "on my way" there i have the page id under pages.

I can't find how i can get an object when i cannot tell what is the key, neither using json_serializable package nor explicitly writing the JSON import code. Is it possible?

PS, i do think i found a way that required two API calls - if i add the indexpageids flag and set it to true, i will receive an additional dictionary entry called pageids with the page id numbers as string, then i can use the retrieved string in the second API call.

I still don't know the exact way i'll do it but i'm pretty sure it's possible, but sending 2 API requests every time is expensive and i would like to know if there's a more elegant way to do it.

Upvotes: 2

Views: 2074

Answers (3)

Björn Jonsson
Björn Jonsson

Reputation: 29

How would you do if you have another level of dynamic keys inside the dynamic key? I had to edit the generated json_serializable for my second class to use json only instead of the generated json['key'].

Page _$PageFromJson(Map<String, dynamic> json) {
  return new Page((json as Map<String, dynamic>)?.map((k, e) =>
  new MapEntry(
      k, e == null ? null : new SecondPage.fromJson(e as Map<String, dynamic>))));
}

It feels like there is some better way? Because this generated code gets overwritten everytime I make changes in any file and run generator..

Im new to SO so I cant rate answers and write comments. But boformer's answer helped me but then I ran into this second question.

Edit: I found the solution by trial and error, used:

class Query {
  final Map<String, Map<String, SecondPage>> pages;

Upvotes: 0

Dhiraj Sharma
Dhiraj Sharma

Reputation: 4859

You can get keys of json & then use that key to fetch value as

var response = await http.get(API_ENDPOINT);
var responseJson = json.decode(response.body);
Map<String, dynamic> json =responseJson['query']['pages'];
String pageId = json.keys.toList()[0]; // 0 for first page, you can iterate for multiple pages
firstPage = json[pageId]; 
String title = firstPage["title"];

Upvotes: 1

boformer
boformer

Reputation: 30103

Make sure that you use the latest version of json_serializable. Use a Map<String, Page> for key-value pairs:

@JsonSerializable()
class Query {
  final Map<String, Page> pages;

  Query(this.pages);
  factory Query.fromJson(Map<String, dynamic> json) => _$QueryFromJson(json);
  Map<String, dynamic> toJson() => _$QueryToJson(this);
}

Just to prove that it works, here is what json_serializable is generating:

Query _$QueryFromJson(Map<String, dynamic> json) {
  return new Query((json['pages'] as Map<String, dynamic>)?.map((k, e) =>
      new MapEntry(
          k, e == null ? null : new Page.fromJson(e as Map<String, dynamic>))));
}

Upvotes: 1

Related Questions