0

I am trying to de-serialize json in a parent class. One of my items is a Map of a Map, which I'd like to de-serialize into a Types class. I am struggling with parsing this because of the nested map in a map and I'm unclear on the proper syntax. At the end of the day I want a List in the parent class. The items within the types json block are dynamic, so there could be a type of critical, notice, etc. with varying descriptions.

Json sample:

{
   "types":{
      "alert":{
         "description":"Action item."
      },
      "question":{
         "description":"Select an applicable response."
      },
      "info":{
         "description":"This is an information message, no action required."
      }
   }
}

Types class:

class Types {
  final String name;
  final String description;

  Types({
    required this.name,
    required this.description,
  });

  factory Types.fromJson(String id, Map<String, dynamic> json) {
    return Types(
      name: id,
      description: json['description'] == null ? '' : json['description'],
    );
  }
}

Parent class:

class Parent {
  final String id;
  final String name;
  final String description;
  final Features features;
  final List<Types> types;
  final List<String> users;

  Parent({
    required this.id,
    required this.name,
    required this.description,
    required this.features,
    required this.types,
    required this.users,
  });

  factory Parent.fromJson( Map<String, dynamic> json) {
    return Parent(
      id: json['id'] == null ? '' : json['id'],
      name: json['name'] == null ? '' : json['name'],
      description: json['description'] == null ? '' : json['description'],
      features: json['features'] == null
          ? Features()
          : Features.fromJson(json['features']),
      types: json['types'] == null ? [] : // ??? How to deserialize Map{Map{}} ,
      users: json['users'] == null ? [] : List<String>.from(json['users']),
    );
  }
}

Any and all help is appreciated. Likewise if there is a better way to store this data I am open to that. The types class allows me to add future fields to it if necessary.

Thank you.

1 Answer 1

2

The general idea is to loop over each key/value pair in json['types'] and create an instance of Types for each one.

import 'dart:convert';

void main(List<String> args) {
  final json = jsonDecode('''
  {
    "id": "test id",
    "name": "test name",
    "description": "test description",
    "types":{
      "alert":{
         "description":"Action item."
      },
      "question":{
         "description":"Select an applicable response."
      },
      "info":{
         "description":"This is an information message, no action required."
      }
    }
  }
  ''');
  print(Parent.fromJson(json));
}

class Parent {
  final String id;
  final String name;
  final String description;
  final List<Types> types;
  // ignoring features and users fields

  Parent({
    required this.id,
    required this.name,
    required this.description,
    required this.types,
  });

  // prefer initializer lists to factory constructors
  // when you are only creating instances of the same class
  //
  // also prefer: 
  //     json['id'] ?? ''
  // to:
  //     json['id'] == null ? '' : json['id']
  // 
  Parent.fromJson(Map<String, dynamic> json)
      : id = json['id'] ?? '',
        name = json['name'] ?? '',
        description = json['description'] ?? '',
        // json['types'] is a map so you will have to loop
        // over each of its entries (key/value pairs) and
        // instantiate a new Types class for each entry.
        types = [
          for (final entry in (json['types'] ?? {}).entries)
            Types.fromJson(entry.key, entry.value),
        ];

  @override
  String toString() =>
      'Parent(id: $id, name: $name, description: $description, types: $types)';
}

class Types {
  final String name;
  final String description;

  Types({
    required this.name,
    required this.description,
  });

  Types.fromJson(String id, Map<String, dynamic> json)
      : name = id,
        description = json['description'] ?? '';

  @override
  String toString() => 'Types(name: $name, description: $description)';
}
Sign up to request clarification or add additional context in comments.

2 Comments

This makes sense, Thank you! I was trying to do everything with .map and having issues parsing the deeper map.
It would certainly be possible to do it by calling .map() on the entries such as (json['types'] as Map<String, dynamic>?)?.entries.map((entry) => Types.fromJson(entry.key, entry.value)).toList() ?? [] but I find the above approach more straightforward.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.