Skip to main content

Parsing JSON Data

1. Using dart:convert Library

Method Description Return Type Example
jsonDecode() Parses a JSON string into a Dart object dynamic
final jsonStr = '{"name": "John", "age": 30}';
final parsed = jsonDecode(jsonStr);
print(parsed['name']); // John
jsonEncode() Converts a Dart object into a JSON string String
final data = {'name': 'John', 'age': 30};
final jsonStr = jsonEncode(data);
print(jsonStr); // {"name":"John","age":30}
Note: The dart:convert library is part of the Dart SDK and does not require additional dependencies.

2. Decoding JSON Strings

Method Description Return Type Example
jsonDecode() Decodes a JSON string into a Dart object (Map, List, etc.) dynamic
final jsonStr = '{"name": "Flutter", "version": "3.19"}';
final data = jsonDecode(jsonStr);
print(data['name']); // Flutter
Note: jsonDecode throws a FormatException if the input string is not valid JSON.

Example: Handling Nested JSON

final jsonStr = '''
{
  "user": {
    "name": "John",
    "age": 30,
    "hobbies": ["reading", "coding"]
  }
}''';
final data = jsonDecode(jsonStr);
print(data['user']['name']); // John
print(data['user']['hobbies'][0]); // reading

3. Encoding Dart Objects

Property Value Description
jsonEncode String jsonEncode(Object? object) Encodes a Dart object into a JSON string.
JsonEncoder JsonEncoder encoder = JsonEncoder.withIndent(' '); Allows customization of JSON encoding, including indentation.

Example: Encoding a Map to JSON

import 'dart:convert';

void main() {
  Map<String, dynamic> data = {
    "name": "John Doe",
    "age": 30,
    "city": "New York"
  };

  String jsonString = jsonEncode(data);
  print(jsonString);
}

Example: Encoding with Indentation

import 'dart:convert';

void main() {
  Map<String, dynamic> data = {
    "name": "John Doe",
    "age": 30,
    "city": "New York"
  };

  JsonEncoder encoder = JsonEncoder.withIndent('  ');
  String jsonString = encoder.convert(data);
  print(jsonString);
}
Note: jsonEncode throws a TypeError if the object contains unsupported types like DateTime or custom classes without a toJson() method.
Warning: Ensure that all data to be encoded is serializable to JSON. Non-serializable types will cause runtime errors.

4. Creating Model Classes

Property Type Description
name String Name of the user
email String Email address of the user
age int Age of the user

Example: User Model Class

class User {
  final String name;
  final String email;
  final int age;

  User({
    required this.name,
    required this.email,
    required this.age,
  });

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      name: json['name'] as String,
      email: json['email'] as String,
      age: json['age'] as int,
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'name': name,
      'email': email,
      'age': age,
    };
  }
}

5. Handling Nested JSON Objects

Property Type Description
jsonObject Map<String, dynamic> Root JSON object containing nested data
nestedObject Map<String, dynamic> Nested object within the root JSON
listOfObjects List<Map<String, dynamic>> List of nested JSON objects

Example: Parsing Nested JSON

final json = {
  "user": {
    "name": "John Doe",
    "address": {
      "street": "123 Main St",
      "city": "Anytown"
    },
    "hobbies": [
      {"name": "Reading", "level": "advanced"},
      {"name": "Coding", "level": "intermediate"}
    ]
  }
};

final user = json["user"] as Map<String, dynamic>;
final name = user["name"];
final street = user["address"]["street"];
final hobbyName = (user["hobbies"] as List<dynamic>)[0]["name"];
Note: When parsing nested JSON, always check for null values before accessing nested properties to prevent runtime errors.

6. Parsing JSON Arrays

Property Description Example
jsonArray Array of JSON objects to parse [{"name": "John", "age": 30}, {"name": "Jane", "age": 25}]

Example: Parsing JSON Array

import 'dart:convert';

void main() {
  String jsonArray = '[{"name": "John", "age": 30}, {"name": "Jane", "age": 25}]';
  List<dynamic> parsedArray = jsonDecode(jsonArray);
  for (var person in parsedArray) {
    print("Name: ${person['name']}, Age: ${person['age']}");
  }
}

7. Using json_serializable Package

The json_serializable package is a powerful code generation tool that simplifies JSON serialization and deserialization in Flutter applications. It generates boilerplate code for converting between Dart objects and JSON, reducing manual work and potential errors.

Feature Description Notes
@JsonSerializable() Annotation to mark a class for JSON serialization Must be applied to the class definition
toJson() Method to convert object to JSON map Generated automatically by codegen
fromJson() Factory constructor to create object from JSON Generated automatically by codegen

Example: Basic Usage

@JsonSerializable()
class User {
  String name;
  int age;
  String email;

  User({
    required this.name,
    required this.age,
    required this.email,
  });

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  Map<String, dynamic> toJson() => _$UserToJson(this);
}

Example: Handling Nested Objects

@JsonSerializable()
class Address {
  String street;
  String city;

  Address({
    required this.street,
    required this.city,
  });

  factory Address.fromJson(Map<String, dynamic> json) => _$AddressFromJson(json);
  Map<String, dynamic> toJson() => _$AddressToJson(this);
}

@JsonSerializable()
class User {
  String name;
  Address address;

  User({
    required this.name,
    required this.address,
  });

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  Map<String, dynamic> toJson() => _$UserToJson(this);
}

Example: Custom Field Names

@JsonSerializable()
class User {
  String name;
  @JsonKey(name: 'user_age')
  int age;

  User({
    required this.name,
    required this.age,
  });

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  Map<String, dynamic> toJson() => _$UserToJson(this);
}
Note: The json_serializable package requires a build step to generate the serialization code. Run flutter pub run build_runner build or flutter pub run build_runner watch to generate the code.
Warning: Ensure all fields in the annotated class are either final or have a corresponding required parameter in the factory constructor.

Key Benefits

  • Eliminates boilerplate code for JSON serialization
  • Provides compile-time safety with generated methods
  • Supports nested objects and custom field names
  • Handles null safety automatically

8. Using json_annotation Package

The json_annotation package is a core part of the JSON serialization ecosystem in Flutter, providing annotations and code generation capabilities to simplify working with JSON data.

Annotation Description Usage
@JsonSerializable Marks a class to be serialized/deserialized using json_serializable. @JsonSerializable()
@JsonKey Customizes how a field is serialized/deserialized, including renaming or ignoring fields. @JsonKey(name: 'custom_name')
@JsonKey(ignore: true) Excludes a field from serialization/deserialization. @JsonKey(ignore: true)

Example: Basic Usage of json_annotation

@JsonSerializable()
class User {
  String name;
  int age;

  User({
    required this.name,
    required this.age,
  });

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  Map<String, dynamic> toJson() => _$UserToJson(this);
}

Example: Custom Field Mapping

@JsonSerializable()
class Product {
  String name;
  @JsonKey(name: 'product_price')
  double price;

  Product({
    required this.name,
    required this.price,
  });

  factory Product.fromJson(Map<String, dynamic> json) => _$ProductFromJson(json);
  Map<String, dynamic> toJson() => _$ProductToJson(this);
}
Note: The $<ClassName>FromJson and $<ClassName>ToJson methods are generated by the json_serializable build runner. You must run flutter pub run build_runner build after making changes to your model classes.
Warning: Ensure that all fields in your model class are either final or have a corresponding required parameter in the factory constructor to avoid runtime errors.

Key Features of json_annotation

  • Type Safety: Ensures compile-time validation of JSON structure.
  • Customization: Allows renaming fields and ignoring specific ones.
  • Code Generation: Reduces boilerplate by generating serialization methods.

9. Generating Serialization Code

Example: JSON serialization with json_serializable

@JsonSerializable()
class User {
  final String name;
  final int age;

  User({
    required this.name,
    required this.age,
  });

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  Map<String, dynamic> toJson() => _$UserToJson(this);
}
Feature Description Notes
json_serializable @JsonSerializable() annotation Generates serialization code at compile time
@JsonKey @JsonKey(name: 'user_name') Custom field mapping
fromJson factory User.fromJson(Map<String, dynamic> json) Deserialization constructor
toJson Map<String, dynamic> toJson() Serialization method
Note: Requires build_runner to generate code.
Warning: Generated code must be included in version control.

10. Handling Null Safety in JSON Parsing

Property Description Example
json.decode() Decodes a JSON string into a Map<String, dynamic> final Map<String, dynamic> jsonMap = json.decode('{"name": "John", "age": 30}');
json.decode() Handles null values by defaulting to null final Map<String, dynamic> jsonMap = json.decode('{"name": null}');

Example: Safe Access with Null-aware Operators

final String? name = jsonMap['name'] as String?;
final int? age = jsonMap['age'] as int?;

Example: Using Null-aware Operators for Nested Values

final Map<String, dynamic>? user = jsonMap['user'] as Map<String, dynamic>?;
final String? firstName = user?['firstName'] as String?;
Note: When parsing JSON, always consider null safety to prevent runtime errors.
Warning: Direct casting without null checks can lead to NullPointerException.

11. Creating Type-Safe JSON Models

Property Type Description Example
name String Name of the user "John Doe"
age int Age of the user 30
email String Email address of the user "john.doe@example.com"
isActive bool Status indicating if the user is active true

Example: JSON Model Class

class User {
  final String name;
  final int age;
  final String email;
  final bool isActive;

  User({
    required this.name,
    required this.age,
    required this.email,
    required this.isActive,
  });

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      name: json['name'] as String,
      age: json['age'] as int,
      email: json['email'] as String,
      isActive: json['isActive'] as bool,
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'name': name,
      'age': age,
      'email': email,
      'isActive': isActive,
    };
  }
}
Note: The fromJson factory constructor is used to parse JSON data into a Dart object, ensuring type safety.

Was this article helpful?