Coder Social home page Coder Social logo

Comments (10)

yobud avatar yobud commented on May 22, 2024 1

I got it working with the following Converter :

class CustomDynamicConverter implements ICustomConverter<dynamic> {
  const CustomDynamicConverter() : super();

  @override
  dynamic fromJSON(dynamic jsonValue, [JsonProperty jsonProperty]) {
    final logger = Logger();
    if (jsonValue is String) {
      return {"iri": jsonValue};
    } else {
      return jsonValue;
    }
  }

  @override
  dynamic toJSON(dynamic object, [JsonProperty jsonProperty]) {
    return object;
  }
}
...
converters: {
  dynamic: CustomDynamicConverter()
}

I didn't implement toJson yet, but I would be glad if it could help anyone!

Thank you for your time ! And for your awesome lib :)

from dart-json-mapper.

yobud avatar yobud commented on May 22, 2024 1

Thank you for your answer,

I was close to a solution so I finaly got it working with converters!

class CustomDynamicConverter implements ICustomConverter<dynamic> {
  const CustomDynamicConverter() : super();

  @override
  dynamic fromJSON(dynamic jsonValue, [JsonProperty jsonProperty]) {

   // If this is a container, return as-is
    if (jsonValue is Iterable) {
      return jsonValue;
    }

    // If this is a string like "/packagings/1"
    if (jsonValue is String) {
      dynamic model;
      RegExp type = RegExp(r'^/([a-zA-Z\_\-]*)/');

      // I made a switch because in my project I have... 74 models.
      // I'll try to use convertParams to avoid having too much code.
      // This would also be much more clean
      switch (type.stringMatch(jsonValue as String)) {
        case '/packagings/':
          model = Packaging();
          break;
      }

      model.iri = jsonValue;
      return model;
    }

    // If jsonValue is an object {"iri": "/packagings/1"}
    if (jsonValue is Map<String, dynamic>) {

      // But avoiding Maps which I have for translations {fr: {}, en:{}} that would result in an error
      if (!jsonValue.containsKey('iri')) {
        return jsonValue;
      }

      // Here I'll use convertParams for type value
      Type type;
      RegExp modelClass = RegExp(r'^/([a-zA-Z\_\-]*)/');
      switch (modelClass.stringMatch(jsonValue["iri"] as String)) {
        case '/packagings/':
          type = Packaging;
          break;
      }

      final context = DeserializationContext(DeserializationOptions(), type);
      return JsonMapper().deserializeObject(jsonValue, context);
    }

    // All my models extends Stores mixin (mobX), this way I know jsonValue is already a model instance
    if (jsonValue is Store) {
      return jsonValue;
    }
  }


  // To be done
  @override
  dynamic toJSON(dynamic object, [JsonProperty jsonProperty]) {
    return object;
  }
}

In my models, everywhere I have relations with other model :

@JsonProperty(converter: CustomDynamicConverter())
  Packaging packaging;

It also works with ObservableList !

@JsonProperty(converter: CustomDynamicConverter())
  ObservableList<Activity> activities;

Now I think I'll not have any other trouble!

I'll post here my definitive solution with convertParams if I get it working!
I hope this could help anyone trying to do something similar.

Thank you for your help & for you great lib!

from dart-json-mapper.

k-paxian avatar k-paxian commented on May 22, 2024

Can you please add your models code as well. It's not very obvious for me what are you trying to achieve.

from dart-json-mapper.

yobud avatar yobud commented on May 22, 2024

Sure,

Here are the models :

@jsonSerializable
class Packaging extends PackagingBase with _$Packaging {
  Packaging({String iri, num weight}): super(iri: iri, weight: weight);
}

@jsonSerializable
abstract class PackagingBase with Store {
  PackagingBase({
    this.iri,
    this.weight
  });

  @JsonProperty()
  @observable
  String iri;

  @JsonProperty()
  @observable
  num weight;
}
@jsonSerializable
class PackagingDetail extends PackagingDetailBase with _$PackagingDetail {
  PackagingDetail({String iri, Packaging packaging, String name}): super(iri: iri, packaging: packaging, name: name);
}

@jsonSerializable
abstract class PackagingDetailBase with Store {
  PackagingDetailBase({
    this.iri,
    this.packaging,
    this.name
  });

  @JsonProperty()
  @observable
  String iri;

  @JsonProperty()
  @observable
  Packaging packaging;

  @JsonProperty()
  @observable
  String name;
}

from dart-json-mapper.

yobud avatar yobud commented on May 22, 2024

Oh I read my initial message again and yes, what I want to do might not be clear.

I want to create a PackagingDetail with property packaging filled with a Packaging, no matters if json contains only a string or complete package description.

If it contains only IRI, I want to create a Packaging with the only field iri filled with the string.
If in contains a complete Packaging, I want to fill it normally.

Hope it's more clear !

from dart-json-mapper.

yobud avatar yobud commented on May 22, 2024

Uh... It doesn't work.

I have to do the following to get a begining of a solution :

class CustomPackagingConverter implements ICustomConverter<dynamic> {
  const CustomPackagingConverter() : super();

  @override
  dynamic fromJSON(dynamic jsonValue, [JsonProperty jsonProperty]) {
    if (jsonValue is String) {
      Packaging packaging = Packaging():
      packaging.iri = jsonValue;
      return packaging;
    } else {
      return jsonValue;
    }
  }

  @override
  dynamic toJSON(dynamic object, [JsonProperty jsonProperty]) {
    return object;
  }
}
converters: {
  Packaging: CustomPackagingConverter()
}

And this will deserialize correctly with the following json :

{
  "iri": "/packaging_details/1",
  "packaging": "/packagings/1",
}

But now, if I want to deserialize directly this model (not as a relation but as the root model I want to deserialize), with the following json :

{
  "iri": "/packagings/1"
}

It ends up with the following error :

[ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'Packaging'
    JsonMapper.deserialize (package:dart_json_mapper/src/mapper.dart:44:5)

Because in this case I should have used the initial converter (don't know which one, something created with reflectable maybe?).

Might you please help me fix this issue? I don't know what I should do...

Thank you

from dart-json-mapper.

yobud avatar yobud commented on May 22, 2024

To avoid root model to pass through my converter, I tried setting the converter as JsonProperty:

  @observable
  @JsonProperty(converter: CustomPackagingConverter())
  Packaging packaging;

So when I ask to deserialize a Packaging json directly, I have no problem:

{
  "iri": "/packaging_details/1",
  "packaging": {"iri": "/packagings/1"}
}

=> 👍

But now I've got an error when I'm serializing a non-string value (a embedded json). e.g:

{
  "iri": "/packaging_details/1",
  "packaging": {"iri": "/packagings/1"}
}

=> 👎

Unhandled Exception: type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'Packaging' of 'value'

from dart-json-mapper.

yobud avatar yobud commented on May 22, 2024

@k-paxian any suggestion ? :x

from dart-json-mapper.

k-paxian avatar k-paxian commented on May 22, 2024

My suggestion would be to skip converter approach and take a look at this unmapped-properties

starter code could be

@jsonSerializable
class Packaging {
  String iri;
  num weight;

  Map<String, dynamic> _extraPropsMap = {};
  bool  _isPackaged;

  @jsonProperty
  void unmappedSet(String name, dynamic value) {   // Deserialization  
    _extraPropsMap[name] = value;
      if (name == 'packaging' && value is String) {
        iri = value
      } else {
        _isPackaged = true;
         // value should be as of type Map<String, dynamic>
         iri = value['iri'];
      }
  }

  @jsonProperty
  Map<String, dynamic> unmappedGet() { // Serialization  
    if (_isPackaged == true) {
      _extraPropsMap['packaging'] = ...
    }
    return _extraPropsMap;
  }
}

from dart-json-mapper.

yobud avatar yobud commented on May 22, 2024

I ended up with the following solution :

@observable
@JsonProperty(converter: CustomDynamicConverter(), converterParams: {'type': Packaging})
  Packaging packaging;
class CustomDynamicConverter extends BaseCustomConverter
    implements ICustomConverter<dynamic> {
  const CustomDynamicConverter() : super();

  @override
  dynamic fromJSON(dynamic jsonValue, [JsonProperty jsonProperty]) {
    if (jsonValue is Iterable) {
      return jsonValue;
    }

    final context = DeserializationContext(
        DeserializationOptions(), getModelType(jsonProperty));
    if (jsonValue is String) {
      final model = getInstance(context);
      model.iri = jsonValue;
      return model;
    }

    if (jsonValue is Map<String, dynamic>) {
      if (!jsonValue.containsKey('iri')) {
        return jsonValue;
      }
      print(jsonValue);
      print(getModelType(jsonProperty));

      return JsonMapper().deserializeObject(jsonValue, context);
    }

    if (jsonValue is Store) {
      return jsonValue;
    }
  }

  @override
  dynamic toJSON(dynamic object, [JsonProperty jsonProperty]) {
    return object;
  }

  Type getModelType([JsonProperty jsonProperty]) {
    return getConverterParameter('type', jsonProperty) as Type;
  }

  dynamic getInstance(DeserializationContext context) {
    final instanceType = context.instanceType.toString();
    final cm = JsonMapper().classes[instanceType];

    if (cm == null) {
      throw MissingAnnotationOnTypeError(context.instanceType);
    }
    final classInfo = ClassInfo(cm);

    return context.options.template ??
        (cm.isEnum
            ? null
            : cm.newInstance(
            classInfo
                .getJsonConstructor(context.options.scheme)
                .constructorName,
            null,
            null));
  }
}

I think I should have done more to fill all the newInstance parameters, but I don't need it for now.

from dart-json-mapper.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.