DMarlon Studio

Lección 18 · 18 de 20

JSON con fromJson y toJson · Dart

Lea y escriba JSON en Dart con jsonDecode y jsonEncode, y conviértalo en objetos con tipos mediante fromJson y toJson, como en Flutter.

Dart · JSON con fromJson y toJson en DMarlon Studio: el código del ejemplo y su salida en la terminal

`dart:convert` trae lo necesario para trabajar con JSON. `jsonDecode` convierte el texto en estructuras de Dart: los objetos JSON pasan a Map<String, dynamic>, las listas a List, y los números, textos, booleanos y null a sus tipos. body es lo que podría responder una API: una lista con dos clientes (dos cadenas seguidas en el código se unen en una sola).

Trabajar con mapas por toda la aplicación es frágil, así que se convierten en objetos con tipo. El factory `Customer.fromJson` lee cada clave y la convierte a su tipo con as: as String? acepta el null de Pablo, y List<String>.from(json['tags'] ?? const []) convierte la lista dinámica en una lista de textos, o usa una vacía si la clave falta. Si el JSON trae un tipo inesperado, falla aquí, en un solo sitio, y no en mitad de la interfaz.

`toJson` hace el camino inverso: devuelve un mapa con tipos básicos. No hace falta llamarlo: `jsonEncode` llama por su cuenta a toJson() de cualquier objeto que no sepa convertir, y por eso jsonEncode(customers.first) funciona directamente.

Si el texto no es JSON válido (aquí, una coma sobrante), jsonDecode lanza FormatException. Esta pareja fromJson/toJson es la convención que siguen los modelos de cualquier aplicación Flutter; paquetes como json_serializable solo escriben este mismo código por usted.

Código · main.dart

import 'dart:convert';class Customer {  final int id;  final String name;  final String? email;  final List<String> tags;  Customer({required this.id, required this.name, this.email, this.tags = const []});  // JSON arrives as Map<String, dynamic>: read each key with its type.  factory Customer.fromJson(Map<String, dynamic> json) => Customer(        id: json['id'] as int,        name: json['name'] as String,        email: json['email'] as String?,        tags: List<String>.from(json['tags'] ?? const []),      );  Map<String, dynamic> toJson() => {'id': id, 'name': name, 'email': email, 'tags': tags};}const body = '[{"id": 17, "name": "Carmen Ruiz", "email": "carmen@example.com",'    ' "tags": ["vip"]}, {"id": 18, "name": "Pablo Gil", "email": null}]';void main() {  final list = jsonDecode(body) as List; // text -> Dart lists and maps  final customers = [for (final item in list) Customer.fromJson(item)];  for (final c in customers) {    print('#${c.id} ${c.name} <${c.email ?? 'no email'}> ${c.tags}');  }  print(jsonEncode(customers.first)); // jsonEncode calls toJson() for you  try { jsonDecode('{"id": 19,}'); } on FormatException catch (e) { print('Bad JSON: ${e.message}'); }}

Salida · dart run main.dart

#17 Carmen Ruiz <carmen@example.com> [vip]
#18 Pablo Gil <no email> []
{"id":17,"name":"Carmen Ruiz","email":"carmen@example.com","tags":["vip"]}
Bad JSON: Unexpected character

Para recordar

  • jsonDecode convierte texto JSON en mapas y listas de Dart.
  • fromJson convierte el mapa en un objeto con tipos.
  • toJson devuelve un mapa, y jsonEncode lo usa automáticamente.
  • Use as Tipo? para los campos que pueden ser null.
  • Un JSON mal formado lanza FormatException.

Ejercicio

Añada a Customer un campo bool active que en el JSON se llame is_active y valga true si falta, e inclúyalo en cada línea de la salida y en toJson.