google / json_serializable.dart

Generates utilities to aid in serializing to/from JSON.
https://pub.dev/packages/json_serializable
BSD 3-Clause "New" or "Revised" License
1.55k stars 395 forks source link

Generated `toJson` does not reference `toJson` from property type. #1293

Closed khainke closed 1 year ago

khainke commented 1 year ago

It is encouraged in the documentation to use custom types as property types. The documentation states:

If you own/control the desired type, add a fromJson constructor and/or a toJson() function to the type. Note: while you can use json_serializable for these types, you don't have to! The generator code only looks for these methods. It doesn't care how they were created.

But, in fact, the generation does not work in this way. (while using latest versions). The generated toJson does not reference the toJson from my custom type. Therefore, the generated code is not correct and not usable. In the following example, i construct a User class with an address property of my custom type Address. For the generation to be usable, the generated toJson for user would have to reference the Address.toJson, but the generated code does not do so. Instead only user.address is referenced directly. But this is only a reference to the object, no serialization...

Details to this issue:

In order to route, prioritize, and act on this, please include the entire output of either dart --version or flutter --version, depending on what you're using.

Flutter 3.7.3 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 9944297138 (4 weeks ago) • 2023-02-08 15:46:04 -0800
Engine • revision 248290d6d5
Tools • Dart 2.19.2 • DevTools 2.20.1

package versions

json_annotation 4.8.0
json_serializable 6.6.1
build_runner 2.3.3

user.dart

import 'package:json_annotation/json_annotation.dart';

import 'address.dart';

part 'user.g.dart';

@JsonSerializable()
class User {
  final String firstName;
  final String lastName;
  final Address? address;
  final String? phoneNumber;

  const User({
    required this.firstName,
    required this.lastName,
    this.address,
    this.phoneNumber,
  });

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

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

address.dart

import 'package:json_annotation/json_annotation.dart';

part 'address.g.dart';

@JsonSerializable()
class Address {
  final String street;
  final int number;
  final String? note;

  const Address({
    required this.street,
    required this.number,
    this.note,
  });

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

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

Expected generated toJson for User:

Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{
      'firstName': instance.firstName,
      'lastName': instance.lastName,
      'address': instance.address.toJson(),
      'phoneNumber': instance.phoneNumber,
    };

Actual generated toJson for User:

Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{
      'firstName': instance.firstName,
      'lastName': instance.lastName,
      'address': instance.address,
      'phoneNumber': instance.phoneNumber,
    };
kevmoo commented 1 year ago

See https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonSerializable/explicitToJson.html