Flutter/Dart - Convert (Deserialize) JSON String to Object

While developing a Flutter application (or any application using Dart language), you may face a situation where you get the data of an object in the form of JSON string and you need to convert it to a Dart object. This tutorial shows you a simple way how to convert a JSON-formatted text to a Dart object, from simple to complex structure.

For example, we are going to deserialize JSON text to an instance of a class. Here is a class named Item along with the constructor.

  class Item {
    int id;
    String name;
    double price;
    int stock;
    bool active;

    Item({this.id, this.name, this.price, this.stock, this.active});
}

Simple JSON

On the first case, the JSON is very simple. It has neither array nor nested structure. The value of each key is either integer, double, string or boolean.

  {  
    "id":1,
    "name":"Item One",
    "price":100.0,
    "stock":10,
    "active":true
  }

The JSON text needs to be encoded (parsed) to JSON object first. The easiest way is using `json.decode` method with the text as the argument. That method is available after importing `dart:convert`. After that, we need to create a function for converting the JSON object to Item instance. As we expect to return a new instance of Item, it's better to implement it as a constructor named fromJson.

  import 'dart:convert';

  class Item {
    int id;
    String name;
    double price;
    int stock;
    bool active;

    Item({this.id, this.name, this.price, this.stock, this.active});
    factory Item.fromJson(Map<String, dynamic> json) => _itemFromJson(json);
  }

  Item _itemFromJson(Map<String, dynamic> json) {
    return Item(
      id: json['id'] as int,
      name: json['name'] as String,
      price: json['price'] as double,
      stock: json['stock'] as int,
      active: json['active'] as bool,
    );
  }

Inside _itemFromJson, we need to map each property to use which key of parsed JSON object. It means the key on the JSON text is not necessarily to be the same as the class property

Below is the usage example of the factory constructor fromJson.

  void main() {
    String text = '{"id": 1, "name": "Item 1", "price": 100.0, "stock": 10, "active": true}';
    Item item = Item.fromJson(json.decode(text));
    print(item.name);
  }

JSON with Array

Now the Item class has a new property locations whose type is List<String>.

  {  
     "id":1,
     "name":"Item One",
     "price":100.0,
     "stock":10,
     "active":true,
     "locations":[  
        "a",
        "b",
        "c"
     ]
  }

The _itemFromJson needs to be modified. The List class has named factory constructor from which accepts Iterable elements. It means we can pass the parsed JSON of locations which can be obtained using json['locations']. But don't forget to check if the JSON object doesn't contain locations. Here's the modification you need inside _itemFromJson

  Item _itemFromJson(Map<String, dynamic> json) {
    var locationsJson = json['locations'];
    List<String> locations = locationsJson != null ? new List.from(locationsJson) : null;

    return Item(
      // Other arguments
      locations: locations, // Add locations as property and constructor parameter
    );
}

JSON with Nested Object Structure

There is a new property of Item named dimensions which is an object.

  {  
     "id":1,
     "name":"Item One",
     "price":100.0,
     "stock":10,
     "active":true,
     "locations":[  ],
     "dimensions":{  
        "width":100.5,
        "depth":201.1,
        "height":50.1
     }
  }

The idea is quite simple. You need to create a class for the nested structure. Then, implement the fromJson class. Here's the example.

  class Dimensions {
    double width;
    double depth;
    double height;

    Dimensions({this.width, this.depth, this.height});
    factory Dimensions.fromJson(Map<String, dynamic> json) => _dimensionsFromJson(json);
  }

And below is the implementation of _dimensionsFromJson function.

  Dimensions _dimensionsFromJson(Map<String, dynamic> json) => Dimensions(
    width: json['width'] as double,
    depth: json['depth'] as double,
    height: json['height'] as double,
  );

Here is the modified _otemFromJson function. Just use Dimensions.fromJson after checking if the dimensions value is not null.

  Item _itemFromJson(Map<String, dynamic> json) {
    var dimensionsJson = json['dimensions'];
    Dimensions dimensions = dimensionsJson != null ? Dimensions.fromJson(dimensionsJson) : null;

    return Item(
      // Other arguments
      dimensions: dimensions, // Add dimensions as property and constructor parameter
    );
  }

If the inner class still has nested structure, you need to create another class for it. Do it recuresively until you don't find any nested structure.

JSON with Array Containing Objects

We're adding another property of Item. List<Purchase> purchases is a list whose elements are object.

  {  
     "id":1,
     "name":"Item One",
     "price":100.0,
     "stock":10,
     "active":true,
     "locations":[  ],
     "dimensions":{  },
     "purchases":[  
        {  
           "amount":1,
           "customer_name":"Cust One"
        },
        {  
           "amount":2,
           "customer_name":"Cust Two"
        }
     ]
}

The solution for that structure is by mapping each element of parsed array to a Purchase instance.

First you need to define the Purchase class along with constructor and fromJson factory constructor.

  class Purchase {
    int amount;
    String customerName;

    Purchase({this.amount, this.customerName});
    factory Purchase.fromJson(Map<String, dynamic> json) => _purchaseFromJson(json);
  }

  Purchase _purchaseFromJson(Map<String, dynamic> json) => Purchase(
    amount: json['amount'] as int,
    customerName: json['customer_name'] as String,
  );

Then, get the parsed JSON array as List, so you can map its elements. For each elements, return a new instance of Purchase using fromJson. After that, convert the map result to List.

  Item _itemFromJson(Map<String, dynamic> json) {
    var purchasesJson = json['purchases'] as List;
    List<Purchase> purchases = purchasesJson != null
        ? purchasesJson.map((i) => Purchase.fromJson(i)).toList()
        : null;

    return Item(
      // Other arguments
      purchases: purchases, // Add purchases as property and constructor parameter
); }

That's all about how to deserialize JSON string to a Dart object.