k-paxian / dart-json-mapper

Serialize / Deserialize Dart Objects to / from JSON
https://pub.dev/packages/dart_json_mapper
Other
400 stars 33 forks source link

Deserialization fails for constructor having named argument on web. #180

Closed mithunadhikari40 closed 2 years ago

mithunadhikari40 commented 2 years ago

I have been using this package for quite a while and it works all great. However, I stumbled upon an issue running on the web and it is very specific. I am not able to create a model class instance from JSON when that model class has a named argument, however, it works when we have a positional argument in the model class's constructor. This is a minimal piece of code to reproduce the issue on the web.

import 'package:dart_json_mapper/dart_json_mapper.dart'
    show JsonMapper, jsonSerializable, JsonProperty;
import 'package:flutter/material.dart';

import 'main.mapper.g.dart' show initializeJsonMapper;

void main() {
  initializeJsonMapper();

  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
    final json = {"a": 12, "b": false, "c": "some value"};

    final instance = JsonMapper.fromMap<MyData>(json);
    print(instance?.a); // null
    print(instance?.b); //null
    print(instance?.c); //null
    print("Instance ${JsonMapper.toMap(instance)}"); // Instance null
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: Scaffold(
        appBar: AppBar(
          title: const Text("Flutter App"),
        ),
        body: Center(
          child: Column(
            children: <Widget>[
              const Text(
                'You have pushed the button this many times:',
              ),
              Text(
                '$_counter',
                style: Theme.of(context).textTheme.headline4,
              ),
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: _incrementCounter,
          tooltip: 'Increment',
          child: const Icon(Icons.add),
        ),
      ),
    );
  }
}

@jsonSerializable
class MyData {
  final int? a;
  final bool? b;
  final String? c;

  MyData({required this.a, required this.b, required this.c});
}

However, if I were to change the model class to :

@jsonSerializable
class MyData {
  final int? a;
  final bool? b;
  final String? c;

  MyData( this.a,  this.b,  this.c);
}

It works all well.

My environment are:
Flutter: 2.8.0
Dart: 2.15.0
Dart_json_mapper: 2.2.1+3
Google Chrome: 98.0.4758.102( Latest)

Just to repeat, it works quite well with a constructor with either named argument or positional argument on other platforms, issue is only on web.
k-paxian commented 2 years ago

Yeah, that's because the js doesn't have named arguments feature. And since dart code is transpiled to js there is no way to do it gracefully. So if you are targeting web platform you shouldn't use named arguments to keep your dart code multiplatform safe.

Here is the place where it's handled.

k-paxian commented 2 years ago

More context can be found in this issue