Nithin Sai
Nithin Sai

Reputation: 1013

How to parse dynamic JSON keys in dart

I have an API that returns JSON with dynamic keys, i.e. the key value changes on every GET request.

Ex: This is the format of the JSON.

I have a book model in dart,

class Book {
  String id = "";
  String title = "";
  
  Book();
  
  Book.fromJson(Map<String, dynamic> json) {
    id = json['id'];
    title = json['title'];
  }
  
  static List<Book> listFromJson(List<dynamic> json) {
    return json.map((val) => Book.fromJson(val)).toList();
  }
}

and JSON response,

{
    "Recommended For You" : [
        {
            "id" : "001",
            "title" : "title001"
        },
        ...
    ],
    "Thrillers" : [
        {
            "id" : "005",
            "title" : "title005"
        },
        ...
    ]
}

How to parse this into lists of books according to the genre, given that genres (like Thrillers) can change(i.e. it is dynamic and may change for each user)?

Edit 1

Thrillers and Recommended For You aren't constant, some times it may change to Horror and Romantic or something else. It is dynamic

Edit 2 : The solution

Thanks to @lrn's solution, I came up with a new Class

class Recommendations {
  String recommendationType;
  List<Book> recommendations;

  @override
  String toString() {
    return 'Recomendations[title: $recommendationType]';
  }

  Recommendations.fromJson(MapEntry<String, dynamic> json) {
    if (json == null) return;
    this.recommendationType = json.key;
    this.recommendations = Book.listFromJson(json.value);
  }

  static List<Recommendations> listFromJson(Map<String, dynamic> json) {
    return json == null
        ? List<Recommendations>.empty()
        : json.entries
            .map((value) => new Recommendations.fromJson(value))
            .toList();
  }
}

Upvotes: 6

Views: 4430

Answers (2)

lrn
lrn

Reputation: 71623

If you don't know the keys, but do know that it's a JSON map where the values are the book lists you're looking for, I'd write:

List<Book> parseCategorizedBooksJson(Map<String, dynamic> json) =>
  [for (var books in json.values) ...Book.listFromJson(books)];

or

List<Book> parseCategorizedBooksJson(Map<String, dynamic> json) =>
  [for (var books in json.values) 
     for (var book in books) Book.fromJson(book)];

(which avoids building intermediate lists of books).

With this you simply iterate the values of the outer map, and ignore the keys. JSON maps are just plain Dart Maps and you can access the keys as .keys (and then go trough them without needing to known them ahead of time) and access the values as .values (and completely ignore the keys).

Each value is a list of books, which you can parse using your existing static functions.

To include the genre, you have to say how you want the genre remembered. The simplest is to retain the data as a map:

var categoriedBooks = {for (var genre in json) 
    genre: Book.listFromJson(json[genre])
};

This builds a map with the same keys (the genres), but where the values are now lists of Books.

Upvotes: 4

Akif
Akif

Reputation: 7640

Your response model contains two different arrays, recommendedForYou and thrillers. So, you can handle it like this:

// To parse this JSON data, do
//
//     final responseModel = responseModelFromJson(jsonString);

import 'dart:convert';

ResponseModel responseModelFromJson(String str) => ResponseModel.fromJson(json.decode(str));

String responseModelToJson(ResponseModel data) => json.encode(data.toJson());

class ResponseModel {
    ResponseModel({
        this.recommendedForYou,
        this.thrillers,
    });

    List<Book> recommendedForYou;
    List<Book> thrillers;

    factory ResponseModel.fromJson(Map<String, dynamic> json) => ResponseModel(
        recommendedForYou: json["Recommended For You"] == null ? null : List<Book>.from(json["Recommended For You"].map((x) => Book.fromJson(x))),
        thrillers: json["Thrillers"] == null ? null : List<Book>.from(json["Thrillers"].map((x) => Book.fromJson(x))),
    );

    Map<String, dynamic> toJson() => {
        "Recommended For You": recommendedForYou == null ? null : List<dynamic>.from(recommendedForYou.map((x) => x.toJson())),
        "Thrillers": thrillers == null ? null : List<dynamic>.from(thrillers.map((x) => x.toJson())),
    };
}

class Book {
    Book({
        this.id,
        this.title,
    });

    String id;
    String title;

    factory Book.fromJson(Map<String, dynamic> json) => Book(
        id: json["id"] == null ? null : json["id"],
        title: json["title"] == null ? null : json["title"],
    );

    Map<String, dynamic> toJson() => {
        "id": id == null ? null : id,
        "title": title == null ? null : title,
    };
}

You can use this site to convert your json to any dart model.

Upvotes: 0

Related Questions