google / built_value.dart

Immutable value types, enum classes, and serialization.
https://pub.dev/packages/built_value
BSD 3-Clause "New" or "Revised" License
867 stars 184 forks source link

serialize and deserialize fails for Enum Map #494

Open Ashok-Varma opened 6 years ago

Ashok-Varma commented 6 years ago

I have a variableof type BuiltMap<T extends EnumClass, double> in my model. serialize and deserialize of this model to json is failing

for this variable in json the library is expecting the key to surround two quotes.

on serialization of sample model. i get this {"basic":1.0,"allowances":{"\"MEDICAL_REIMBURSEMENT\"":2.0}} as output which is invalid json. and also deserialization fails unless i give two quotes

Sample model

abstract class Salary implements Built<Salary, SalaryBuilder> {
  static Serializer<Salary> get serializer => _$salarySerializer;

  @nullable
  double get basic;

  @nullable
  BuiltMap<Allowance, double> get allowances;

  Salary._();

  factory Salary([updates(SalaryBuilder b)]) = _$Salary;

  Salary rebuild(void updates(SalaryBuilder b));
  SalaryBuilder toBuilder();
}

Salary parseSalary(String jsonStr){
  final parsed = json.decode(jsonStr);
  Salary salary = standardSerializers.deserializeWith(Salary.serializer, parsed);
  return salary;
}

String toJson(Salary salary){
  var serializedMap = standardSerializers.serializeWith(Salary.serializer, salary);
  return jsonEncode(serializedMap);
}

@BuiltValueEnum(wireName: 'Allowance')
class Allowance extends EnumClass {
  static Serializer<Allowance> get serializer => _$allowanceSerializer;

  @BuiltValueEnumConst(wireName: 'MEDICAL_REIMBURSEMENT')
  static const Allowance MEDICAL_REIMBURSEMENT = _$MEDICAL_REIMBURSEMENT;
  @BuiltValueEnumConst(wireName: 'FIXED_MEDICAL_ALLOWANCE')
  static const Allowance FIXED_MEDICAL_ALLOWANCE = _$FIXED_MEDICAL_ALLOWANCE;
  @BuiltValueEnumConst(wireName: 'CHILD_EDUCATION')
  static const Allowance CHILD_EDUCATION = _$CHILD_EDUCATION;
  @BuiltValueEnumConst(wireName: 'CHILD_HOTEL_FEE')
  static const Allowance CHILD_HOTEL_FEE = _$CHILD_HOTEL_FEE;
  @BuiltValueEnumConst(wireName: 'ENTERTAINMENT')
  static const Allowance ENTERTAINMENT = _$ENTERTAINMENT;
  @BuiltValueEnumConst(wireName: 'LTA')
  static const Allowance LTA = _$LTA;
  @BuiltValueEnumConst(wireName: 'UNIFORM')
  static const Allowance UNIFORM = _$UNIFORM;
  @BuiltValueEnumConst(wireName: 'FUEL')
  static const Allowance FUEL = _$FUEL;
  @BuiltValueEnumConst(wireName: 'TELEPHONE')
  static const Allowance TELEPHONE = _$TELEPHONE;
  @BuiltValueEnumConst(wireName: 'OTHERS')
  static const Allowance OTHERS = _$OTHERS;

  const Allowance._(String name) : super(name);

  static BuiltSet<Allowance> get values => _$vls;
  static Allowance valueOf(String name) => _$vlOf(name);
}
@SerializersFor(const [
  Salary,
  Allowance,
])
Serializers serializers = _$serializers;

Serializers standardSerializers =
(serializers.toBuilder()..addPlugin(new StandardJsonPlugin())).build();
davidmorgan commented 6 years ago

Thanks.

This is a tricky case because JSON maps only allow strings as keys. The enum instances are easily represented as strings, but StandardJsonPlugin doesn't know that.

I'll try to take a look at some point, maybe there's something we can do better here.

By the way, I don't think you need the BuiltValueEnumConst annotations.

Ashok-Varma commented 6 years ago

@davidmorgan i didn't use BuiltValueEnumConst in the beginning. But i thought that might solve the issue. So gave them a try, almost tried all possible variations.

Is there any walk around to this problem.

  1. i can't use standard dart enums => no serializers.
davidmorgan commented 6 years ago

The best workaround I can think of is to use BuiltMap<String, double> and to use Allowance.name and Allowance.valueOf to convert to and from String. But that's not a great solution. I'll try to have a look this week or next.

Ashok-Varma commented 6 years ago

Hai @davidmorgan , Any update on this issue ??

davidmorgan commented 6 years ago

Sorry, I've been pretty slow in getting to this. Hoping to take a look soon; ~next week.

Ashok-Varma commented 5 years ago

Hai @davidmorgan, many people are still waiting for this fix.

davidmorgan commented 5 years ago

Sorry for that. I have an idea for a fix, but unfortunately it's quite complicated; it involves changing the serializer interfaces so a serializer declares if it's always going to return a string. (Currently we have two types of serializer: primitive and structured. We would instead have three, string, primitive and structured).

I expect this will happen at some point but I'm afraid it's hard to predict when.

jffiorillo commented 5 years ago

Hi @davidmorgan , thank you for your effort. Did you have time to look at this issue?

davidmorgan commented 5 years ago

I don't have any ETA for this, unfortunately; it's a complex fix. I do hope to get to it at some point.

davidmorgan commented 3 years ago

I took a look at this; it looks possible by:

But, it's still breaking to change the wire format; so it'll have to be an option on StandardJsonSerializer.

I think it's too big a feature for me to get to this time around--going to look at more smaller fixes first.