Parse Json into a Dart class object

eye-catch Dart and Flutter

When calling an API via HTTP, the response is generally JSON format. To handle the response, we need to know how to parse a JSON object in Dart language. Let’s try to convert it into Dart class.

Sponsored links

jsonDecode function to parse a json string into a Map

Dart offers a useful function to parse a JSON string.

import 'dart:convert';

final json = '''{
  "name": "Yuto",
  "age": 34,
  "job": "Software Engineer"
}''';
print(json);
// {
//     "name": "Yuto",
//     "age": 34,
//     "job": "Software Engineer"
//   }

final decoded = jsonDecode(json);
print(decoded);
//{name: Yuto, age: 34, job: Software Engineer, nested: {hoge: hoge-vallue, foo: foo-vallue}}

It looks the same but the decoded object has no double-quotes for the keys and values because it is an object rather than a string. The data type becomes dynamic if we don’t specify it but it is actually a Map object. Therefore, we can access the values in the following way.

print(decoded["name"]); // Yuto
print(decoded["age"]);  // 34
print(decoded["age"] is int);   // true

After parsing the JSON object, the number value is assigned as int value as expected. We don’t need a conversion process here.

As I said, we can’t get the value with the following way since it is a Map object.

// NoSuchMethodError: Class '_InternalLinkedHashMap<String, dynamic>' has no instance getter 'name'.
print(decoded.name);

If some methods defined in the Map class are needed, the data type needs to be added.

Map<String, dynamic> decoded = jsonDecode(json);
print(decoded);
// {name: Yuto, age: 34, job: Software Engineer, nested: {hoge: hoge-vallue, foo: foo-vallue}}

print(decoded.entries)
// (MapEntry(name: Yuto), MapEntry(age: 34), MapEntry(job: Software Engineer), MapEntry(nested: {hoge: hoge-vallue, foo: foo-vallue}))
Sponsored links

Parse a json string into a class

There are many cases that the parsed object needs to be passed to another class for further process. In this case, using class is easier than using Map object because IntelliSense shows us possible properties and methods.

Once the object is parsed into a class, we can access the properties with a dot chain.

final person = _Person.fromJson(jsonDecode(json));
print("name: ${person.name}, age: ${person.age}, job: ${person.job}");
// name: Yuto, age: 34, job: Software Engineer

The code above parses the JSON string and then, creates an instance of _Person class. Of course, we need to define the logic.

class _Person {
  final String name;
  final int age;
  final String job;

  _Person(this.name, this.age, this.job);
  _Person.fromJson(Map<String, dynamic> json)
      : name = json["name"],
        age = json["age"],
        job = json["job"];
}

This is called named constructor with an initializer list. Check the following post if you want to know more about constructors.

How to handle nested values

A JSON file or string contains nested objects in many cases. In this case, we need to define the structure in our class. Let’s add an object to the JSON string.

 final json = '''{
  "name": "Yuto",
  "age": 34,
  "job": "Software Engineer",
  "nested": {
    "hoge": "hoge-vallue",
    "foo": "foo-vallue"
  }
}''';

nested property is added. The nested properties can be accessed directly with the following code.

final decoded = jsonDecode(json);
print(decoded["nested"]["hoge"]); // hoge-vallue
print(decoded["nested"]["foo"]);  // foo-vallue

But how can we put them into a class? In TypeScript, we can define an interface in the following way.

interface Person {
    name: string;
    age: number;
    job: string;
    nested: {
        hoge: string;
        foo: string;
    }
}

I tried to write in a similar way but Dart doesn’t offer an inner class. Therefore, we need to define a different class for the nested object.

class _Nested {
  final String hoge;
  final String foo;
  _Nested(this.hoge, this.foo);
  _Nested.fromJson(Map<String, dynamic> json)
      : hoge = json["hoge"],
        foo = json["foo"];
}

class _Person {
  final String name;
  final int age;
  final String job;
  final _Nested nested; // Added

  _Person(this.name, this.age, this.job, this.nested);
  _Person.fromJson(Map<String, dynamic> json)
      : name = json["name"],
        age = json["age"],
        job = json["job"],
        nested = _Nested.fromJson(json["nested"]); // Added
}

With this code, nested properties are accessible via dot chained call.

final person = _Person.fromJson(jsonDecode(json));
print("name: ${person.name}, age: ${person.age}, job: ${person.job}");
// name: Yuto, age: 34, job: Software Engineer

print("nested: { hoge: ${person.nested.hoge}, foo: ${person.nested.foo} }");
// nested: { hoge: hoge-vallue, foo: foo-vallue }

Comments

Copied title and URL