Conversão automática de Json usando json_serializable #1
Tags: flutter, dart, json
Published: 2020-02-21 00:40:08

Depois de muito tempo, estou de volta na publicação de artigos, neste artigo vamos vou mostrar prar vocês como simplicar aquela tarefa chata que ninguém quer fazer, a redução de boilerplate quando estamos trabalhando com conversão de de uma classe Dart para json e vice-versa.

Todo desenvolvedor Flutter já se deparou com o problema de escrever as funções `fromJson` e `toJson` quanto estamos trabalhando com base de dados ou mesmo consumindo recursos de algum servidor. É um trabalho simples para projetos bem pequenos, mas quando vamos trabalhar com projetos grande chega a ser uma tarefa difícil de gerir a longo prazo.

Por este motivo websites como o [Quicktype](http://quicktype.io/) (um conversor de Json para classes de diferentes linguagens, incluindo Dart) se tornaram famosos e são ferramentas que estão sendo cada vez mais usadas no dia-a-dia dos desenvolvedores.

Eu vou mostrar para você um jeito mais simples de fazer isso usando um pacote Dart que gera as funções `fromJson` e `toJson` para você.

#json_serializable

É um pacote da linguagem Dart que simplifica a conversão de uma classe Dart para json e vice-versa através da geração de código, continue lendo para ver o funcionamento dela.

Do contrário ao [Quicktype](http://quicktype.io/) este pacote evita ter classes conversoras de de objectos, já que todo trabalho sujo é transparente para o desenvolvedor em um arquivo separado.

Vamos dizer que temos a seguinte classe `User` em nosso projeto, e precisamos fazer com ele suporte conversão automática para json, tudo o que precisamos fazer é:
- [x] Adicionar `part 'user.g.dart';` (user, é o nome do ficheiro onde se encontra a classe)
- [x] Anotar a classe com a anotação `@JsonSerializable()`
- [x] Configurar a classe para usar as funcões gerada pelo pacote.

Com todos os passos feitos, teremos uma classe parecida com:

```dart
part of 'user.dart';

@JsonSerializable()
class User {
  final String firstName;
  final String lastName;
  final DateTime dateOfBirth;
  User({this.firstName, this.lastName, this.dateOfBirth});

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

  Map<String, dynamic> toJson() => _$UserToJson(this);
}
```

De momento a IDE poderá mostrar erros nas últimas linhas da classe, isso porque não geramos o codigo necessário para o funcionamento da classe. Após a execução de `flutter pub run build_runner build` teremos o problema resolvido e vai aparecer um novo ficheiro `user.g.dart` com o seguinte:

```dart
part of 'user.dart';

User_$UserFromJson(Map<String, dynamic> json) {
  return User(
    firstName: json['firstName'] as String,
    lastName: json['lastName'] as String,
    dateOfBirth: DateTime.parse(json['dateOfBirth'] as String),
  );
}

Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{
      'firstName': instance.firstName,
      'lastName': instance.lastName,
      'dateOfBirth': instance.dateOfBirth.toIso8601String(),
    };
```

Este é o arquivo que contém o código gerado, o codigo necessário para a conversão automática da sua classe para json. Não se preocupe, você não precisa saber da existência deste arquivo, e nem precisa usar ele.


## Conversão de Classes usando outras classes como propiedades.

Por padrão este pacote não suporta conversão de propriedades de tipos desconhecidos, o que pode causar problema se você tiver uma classe do tipo:

```dart
@JsonSerializable()
class User {
  final Location location;
  User({this.location});

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

  Map<String, dynamic> toJson() => _$UserToJson(this);
}
```

O que o pacote vai fazer é tentar converter a instância de `location` para string, e uma vez que ela não é string irá disparar uma esceção de conversão.

Para resolver este problema, nós devemos fazer duas coisas: dizer ao pacote para que chame a função `fromJson`/`toJson` em objectos desconhecidos, e adicionar tais funções em nossos objectos.

1. Setando a propiedade `explicitToJson` para `true`, o pacote vai então chamar `fromJson`/`toJson` sempre que precisar converter um tipo de dado desconhecido, neste caso quando ela precisar converter a propiedade `location`.

```dart
@JsonSerializable(explicitToJson: true)
class User {
  ...
}
```

2. Definir `fromJson`/`toJson` na classe que precisa de conversão.

```dart
part of 'location.dart';

@JsonSerializable()
class Location {
  final double lat;
  final double lgn;
  User({this.lat, this.lng});

  factory Location.fromJson(Map<String, dynamic> json) => _$LocationFromJson(json);

  Map<String, dynamic> toJson() => _$LocationToJson(this);
}
```

E com isso a conversão automática agora funciona com objectos de tipos desconhecidos, ou seja com classes criadas por nós.

OBS: o pacote pode ser encontrado [aqui](https://pub.dev/packages/json_serializable).


Por hoje é tudo, segue para a parte dois, onde veremos conceitos mais avançados deste pacote.