Sunday, October 6, 2013

Reflective JsonMapper (Part 3)

Originally I planed to use generics, but now it is better not to call it generic, but we call it reflective.
  
I completed the support of List(and Map) property in Entity class, and this works for both dynamic(with dart:mirrors lib for Dart VM) and static mode(for javascript).
But I haven't tested on javascript mode yet, but it should work. Also I haven't tested the map case. I will do it later.
This version was committed to Github.

Following is the sample code to show the support of List field in a class.

class A {
  int _i = 0;
  List<B> _bs = [];
  
  A(this._i, this._bs);
  factory A.create() => new A(111, []);
  
  int get i => _i;
  void set i(int ii) { _i == ii; }
  
  List<B> get bs => _bs;
  void set bs(List<B> bs) {_bs = bs; }
}

class B {
  String _s = 'zz';
  B(this._s);
  factory B.create() => new B('ss');
  String get s => _s;
  void set s(String ss) { _s == ss; }
}

test_simple_model() {
      A a = new A.create()..i = 10
        ..bs.add(new B.create()..s = "vvv")
        ..bs.add(new B.create()..s = "eee");
      
      String json = sampleJsonMapper.toJson(a);
      print(">>A@1 test_toJson json: ${json}");
      
      A a1 = sampleJsonMapper.fromJson(A, json);
      print(">>A@2 test_Json a1: ${a1}");
}

And the output of execution:
>>A@1 test_toJson json: {"i": 111,"bs": [{"s": "ss"},{"s": "ss"}]}
>> obj: Instance of 'A'
>>A@2 test_Json a1: Instance of 'A'
>> json: {"i": 10,"bs": [{"s": "ss"},{"s": "vv"}]}

Actually the support of generic type property was a bit difficult due to some confusing behavior of reflectedType property of ClassMirror. And some irreversible operation of types and  typeArguments behavior.

But by keeping type instead of extracting from ClassMirror, I could avoid the current restriction of Miror library. Probably Dart is still early stage, such mirror behavior should be refined.

Following are the problems I found:

1) why we cannot do followings:

var t1 = List<A>;
var cmirr = reflectClass(List<A>);

while we can do for none generic class:
car t = A;
var cmirr = reflectClass<A);

2) how can I create ClassMirror object which has reflectedType.
if we create CirrorClass through reflectClass(new List<B>().runtimeType),
it does not have reflectedType. but if we get ClassMirror from  bs of  A, it has a reflectedType. So there should be some way to create Mirror class which has reflectedType.

3) the reflectClass<List<A>) has all information to make cmirror to have reflectedType(since argumentType A is provided).
It should return ClassMirror with reflectedType.
I think this is very inconvenient behavior, and should be called a bug.

library dart_generics_test;

import 'dart:mirrors';

class A {
  int _i = 0;
  List<B> _bs = [];
  
  int get i => _i;
  void set i(int ii) { _i == ii; }
  
  List<B> get bs => _bs;
  void set bs(List<B> bs) {_bs = bs; }
}

class B {
  String _s = 'zz';
  
  String get s => _s;
  void set s(String ss) { _s == ss; }
}

test_generics() {
  {
    /*
    ClassMirror immir0 = reflectClass(List<B>); // <== this cannot be compiled!
    Type t0 = immir0.reflectedType;
    print(">> ${t0}");
    */
    
    // following code is to get a Type of List<B>, a work around to avoid above problem.  
    InstanceMirror immir = reflect(new List<B>());
    Type t = immir.type.reflectedType;
    print(">>1.1 ${t}");
    
    /*
    ClassMirror cmirr = reflectClass(t);
    Type t2 = cmirr.reflectedType; // here t2 == t, but it throws exception
    print(">>1.2 ${t2}");
    */
  /*
   * Unhandled exception:
Unsupported operation: Declarations of generics have no reflected type
#0      _LocalClassMirrorImpl.reflectedType (dart:mirrors-patch/mirrors_impl.dart:304:7)
#1      test_generics (file:///opt/dart-workspace/reflective_web_dev_kit/sample_app/bin/sample_json_mapper_test.dart:76:21)
#2      main (file:///opt/dart-workspace/reflective_web_dev_kit/sample_app/bin/sample_json_mapper_test.dart:81:16)
   * 
   */
  }

  {
    Type t1 = new B("nn").runtimeType;
    print(">>2.1 ${t1}");
  
    ClassMirror cmirr1 = reflectClass(t1);
    Type t2 = cmirr1.reflectedType; // this is ok
    print(">>2.1 ${t2}");
  }

  {
    Type t1 = new List<B>().runtimeType; // return List<B>
    print(">>3.1 t1: ${t1}");
  
    ClassMirror cmirr1 = reflectClass(t1);
    Type t2 = cmirr1.runtimeType; // return _LocalClassMirrorImpl
    print(">>3.2 t2: ${t2}"); 
    print(">>3.3 cmirr1.hasReflectedType: ${cmirr1.hasReflectedType}"); 
    
    Type t4 = cmirr1.reflectedType; // here it should be t4 == t0, but it throws exception
    print(">>3.3 t4: ${t4}");
  }

  {
    ClassMirror cmirr = reflectClass(A);
    cmirr.getters.forEach((k, MethodMirror md){
      Type tm = (md.returnType as ClassMirror).reflectedType; // this is OK
      print(">>4.1 k: ${k}, tm: ${tm}");
    });
  }
}

main() {
  test_generics();
}

No comments:

Post a Comment