How to parse a complex(nested) object to JSON and send it to server using HTTP in flutter?
Asked Answered
F

3

15

Hi I have a class that have other classes nested in it. I want to covert an object of this into a JSON string and send it to the server.

I have tried many answers both from stack overflow and google searches. Non sufficiently answer my question.

Any help is appreciated guys.

here are my models

class Place {
   String name;
   String description;
   List<PhoneNumber> phoneNumbers;
   List<String> tags;
   GPSCoordinante gpsCoordinates;
   List<Service> services;
   List<Album> albums;
   SocialMedia socialMedia;
   List<Comment> comments;
   List<String> imageURLArray;
   int rating;
   int shares;
   int favorites;
   int views;
   String category;
   String subcategory;
   WorkingHour workingHours;
   bool deleted;
   double distanceToUser;
   bool isApproved;
   String address;
   List<String> coverImages;

  Place({
    this.name,
    this.description,
    this.phoneNumbers,
    this.tags,
    this.gpsCoordinates,
    this.services,
    this.albums,
    this.socialMedia,
    this.comments,
    this.imageURLArray,
    this.rating,
    this.shares,
    this.favorites,
    this.views,
    this.category,
    this.subcategory,
    this.workingHours,
    this.deleted,
    this.distanceToUser,
    this.isApproved ,
    this.address,
    this.coverImages,
});

  factory Place.fromJson(Map<String, dynamic> json) {


    List phoneNumbersJsonList = json['phoneNumbers'] as List;
    List<PhoneNumber> parsedPhoneNumbers = phoneNumbersJsonList.map((value) => PhoneNumber.fromJson(value)).toList();

    List servicesJsonList = json['services'] as List;
    List<Service> parsedServices = servicesJsonList.map((value) => Service.fromJson(value)).toList();

    List walbumsJsonList = json['albums'] as List;
    List<Album> parsedAlbums = walbumsJsonList.map((value) => Album.fromJson(value)).toList();

    List commentsJsonList = json['comments'] as List;
    List<Comment> parsedComments = commentsJsonList.map((value) => Comment.fromJson(value)).toList();


    return Place(
        name: json['name'],
        description: json['description'],
        phoneNumbers:  parsedPhoneNumbers,
        gpsCoordinates: json['gpsCoordinates'],
        services: parsedServices,
        albums: parsedAlbums,
        socialMedia: json['socialMedia'],
        comments: parsedComments,
        imageURLArray: json['imageURLArray'],
        rating: json['rating'],
        shares : json['shares'],
        favorites: json['favorites'],
        category: json['category'],
        subcategory: json['subcategory'],
        workingHours: json['workingHours'],
        deleted: json['deleted'],
        isApproved: json['isApproved'],
        address: json['address'],
        coverImages: json['coverImages'],
    );

  }

   Map toMap() {
     var map = new Map<String, dynamic>();
     map["name"] = name;
     map["description"] = description;
     map["services"] = services;
     map["albums"] = albums;
     map["comments"] = comments;
     map["imageURLArray"] = imageURLArray;
     map["rating"] = rating;
     map["shares"] = shares;
     map["favorites"] = favorites;
     map["category"] = category;
     map["subcategory"] = subcategory;
     map["workingHours"] = workingHours;
     map["deleted"] = deleted;
     map["isApproved"] = isApproved;
     map["address"] = address;
     map["coverImages"] = coverImages;


     return map;
   }


}

class PhoneNumber {
   int phoneNumber;
   String owner;

  PhoneNumber({
    this.phoneNumber,
    this.owner
});

  factory PhoneNumber.fromJson(Map<String, dynamic> json) {
    return PhoneNumber(
      phoneNumber: json['phoneNumber'],
      owner: json['owner'],
    );
  }

   Map toMap() {
     var map = new Map<String, dynamic>();
     map["phoneNumber"] = phoneNumber;
     map["owner"] = owner;

     return map;
   }
}

class GPSCoordinante {
   double longitude;
   double latitude;

  GPSCoordinante({
    this.longitude,
    this.latitude
});

  factory GPSCoordinante.fromJson(Map<String, dynamic> json) {
    return GPSCoordinante(
      longitude: json['longitude'],
      latitude: json['latitude'],
    );
  }

   Map toMap() {
     var map = new Map<String, dynamic>();
     map["longitude"] = longitude;
     map["latitude"] = latitude;

     return map;
   }
}


class Service {

  String name;
  String description;

  Service({
    this.name,
    this.description
  });

  factory Service.fromJson(Map<String, dynamic> json) {
    return Service(
      name: json['name'],
      description: json['description'],
    );
  }

  Map toMap() {
    var map = new Map<String, dynamic>();
    map["name"] = name;
    map["description"] = description;

    return map;
  }
}

class Album {
   String name;
   String nameEn;
   List<Item> items;
   bool isAutoConvertToUSDEnabled;
   bool isAllItemsDeliveryEnabled;

  Album({
    this.name,
    this.nameEn,
    this.items,
    this.isAutoConvertToUSDEnabled,
    this.isAllItemsDeliveryEnabled
});

  factory Album.fromJson(Map<String, dynamic> json) {

    List itemsJsonList = json['items'] as List;
    List<Item> parsedItems = itemsJsonList.map((value) => Item.fromJson(value)).toList();

    return Album(
      name: json['name'],
      nameEn: json['nameEn'],
      items: parsedItems,
      isAutoConvertToUSDEnabled: json['isAutoConvertToUSDEnabled'],
      isAllItemsDeliveryEnabled: json['isAllItemsDeliveryEnabled']

    );
  }

   Map toMap() {
     var map = new Map<String, dynamic>();
     map["name"] = name;
     map["nameEn"] = nameEn;
     map["items"] = items;
     map["isAutoConvertToUSDEnabled"] = isAutoConvertToUSDEnabled;
     map["isAllItemsDeliveryEnabled"] = isAllItemsDeliveryEnabled;

     return map;
   }
}

class Item{
   List<String> imageVariants;
   String name;
   String nameEn;
   bool isAutoConvertNameToEnglishEnabled;
   List<String> tags;
   int priceIQD;
   double priceUSD;
   String description;
   bool isDeliveryAvailable;
   bool isDinarAutomaticallyConvertedToDollar;
   int itemIndex;
   int albumIndex;
   bool isDeleted;

  Item({
    this.imageVariants,
    this.name,
    this.nameEn,
    this.isAutoConvertNameToEnglishEnabled,
    this.tags,
    this.priceIQD,
    this.priceUSD,
    this.description,
    this.isDeliveryAvailable,
    this.isDinarAutomaticallyConvertedToDollar,
    this.itemIndex,
    this.albumIndex,
    this.isDeleted
});

  factory Item.fromJson(Map<String, dynamic> json) {
    return Item(
      imageVariants: json['imageVariants'],
      name: json['name'],
      nameEn: json['nameEn'],
      isAutoConvertNameToEnglishEnabled: json['isAutoConvertNameToEnglishEnabled'],
      tags: json['tags'],
      priceIQD: json['priceIQD'],
      priceUSD: json['priceUSD'],
      description: json['description'],
      isDeliveryAvailable: json['isDeliveryAvailable'],
      isDinarAutomaticallyConvertedToDollar: json['isDinarAutomaticallyConvertedToDollar'],
      albumIndex: json['albumIndex'],
      itemIndex: json['itemIndex'],
      isDeleted: json['isDeleted'],

    );
  }

   Map toMap() {
     var map = new Map<String, dynamic>();
     map["imageVariants"] = imageVariants;
     map["name"] = name;
     map["nameEn"] = nameEn;
     map["isAutoConvertNameToEnglishEnabled"] = isAutoConvertNameToEnglishEnabled;
     map["tags"] = tags;
     map["priceIQD"] = priceIQD;
     map["priceUSD"] = priceUSD;
     map["description"] = description;
     map["isDeliveryAvailable"] = isDeliveryAvailable;
     map["isDinarAutomaticallyConvertedToDollar"] = isDinarAutomaticallyConvertedToDollar;
     map["albumIndex"] = albumIndex;
     map["itemIndex"] = itemIndex;
     map["isDeleted"] = isDeleted;

     return map;
   }

}


class Comment{
   String user;
   String userId;
   String text;
   DateTime dateTime;

  Comment({
   this.user,
   this.userId,
   this.text,
   this.dateTime
});

  factory Comment.fromJson(Map<String, dynamic> json) {
    return Comment(
      user: json['user'],
      userId: json['userId'],
      text: json['text'],
      dateTime: json['dateTime'],

    );
  }

   Map toMap() {
     var map = new Map<String, dynamic>();
     map["user"] = user;
     map["userId"] = userId;
     map["text"] = text;
     map["dateTime"] = dateTime;
     return map;
   }
}

class WorkingHour{
   String openingHour;
   String openingHourAmOrPm;
   String closingHour;
   String closingHourAmOrPm;

  WorkingHour({
    this.openingHour,
    this.openingHourAmOrPm,
    this.closingHour,
    this.closingHourAmOrPm
});

  factory WorkingHour.fromJson(Map<String, dynamic> json) {
    return WorkingHour(
      openingHour: json['openingHour'],
      openingHourAmOrPm: json['openingHourAmOrPm'],
      closingHour: json['closingHour'],
      closingHourAmOrPm: json['closingHourAmOrPm'],

    );
  }

   Map toMap() {
     var map = new Map<String, dynamic>();
     map["openingHour"] = openingHour;
     map["openingHourAmOrPm"] = openingHourAmOrPm;
     map["closingHour"] = closingHour;
     map["closingHourAmOrPm"] = closingHourAmOrPm;
     return map;
   }
}

class SocialMedia {
  String facebook ;
  String instagram;
  String youTube;
  String snapChat;
  String twitter;
  String googlePlus;
  String pinterest;

  SocialMedia({
    this.facebook,
    this.instagram,
    this.youTube,
    this.snapChat,
    this.twitter,
    this.googlePlus,
    this.pinterest
});

  factory SocialMedia.fromJson(Map<String, dynamic> json) {
    return SocialMedia(
      facebook: json['facebook'],
      instagram: json['instagram'],
      youTube: json['youTube'],
      snapChat: json['snapChat'],
      twitter: json['twitter'],
      googlePlus: json['googlePlus'],
      pinterest: json['pinterest'],


    );
  }

  Map toMap() {
    var map = new Map<String, dynamic>();
    map["facebook"] = facebook;
    map["instagram"] = instagram;
    map["youTube"] = youTube;
    map["twitter"] = twitter;
    map["snapChat"] = snapChat;
    map["googlePlus"] = googlePlus;
    map["pinterest"] = pinterest;

    return map;
  }
}

I am trying to send this Place object to the server:

 Place place = Place(
      name: 'some name',
      description: 'some description',
      phoneNumbers: [PhoneNumber(phoneNumber: 125252525, owner: 'ali')],
      tags: ['some tag 2', 'some tag 2'],
      gpsCoordinates: GPSCoordinante(latitude: 11332, longitude: 13415),
      services: [Service(name: 'some service', description: 'some description')],
      albums: [
        Album(
            name: 'some album name',
            nameEn: 'someEN name',
            items: [
              Item(
                  name: 'some item name',
                  imageVariants: ['a;lgjlagj', 'ag;ja;gj;ag'],
                  nameEn: 'some en name',
                  isAutoConvertNameToEnglishEnabled: true,
                  tags: ['aggagag'],
                  priceIQD: 32425,
                  priceUSD: 252525,
                  description: 'agkgl;aj g;g ja;g ',
                  isDeliveryAvailable: true,
                  isDinarAutomaticallyConvertedToDollar: true,
                  itemIndex: 1,
                  albumIndex: 2)
            ],
            isAutoConvertToUSDEnabled: true,
            isAllItemsDeliveryEnabled: false)
      ],
      socialMedia: SocialMedia(facebook: 'facebook url'),
      comments: [
        Comment(
            user: 'some user',
            userId: '324-2-5-25',
            text: 'some comment',
            dateTime: DateTime.now())
      ],
      imageURLArray: ['some url'],
      rating: 0,
      shares: 0,
      favorites: 0,
      views: 0,
      category: 'sdagagasga;lgjaa;lgj',
      subcategory: 'as;glgjasl;gjas;lgkj',
      workingHours: WorkingHour(
          openingHour: '11', openingHourAmOrPm: 'am', closingHour: '11:00'),
      deleted: false,
      distanceToUser: 225252,
      isApproved: false,
      address: "some adress",
      coverImages: ['image one', 'image 2']);

I keep getting errors when using json.encode(place); or json.encode(place.toMap()) like this

converting object to an encodable object failed: Instance of 'Service'

How can make this work ?

Fascinating answered 26/8, 2019 at 17:15 Comment(0)
F
32

It seems you don't need to write so much code to encode and decode your classes to JSON strings. to avoid a lot of redundancy and make your life easier use the following solution:

here is how it goes :

first depend on these libraries in your pubspec.yaml:

dependencies:
  # Your other regular dependencies here
  json_annotation: ^2.0.0

dev_dependencies:
  # Your other dev_dependencies here
  build_runner: ^1.0.0
  json_serializable: ^2.0.0

then in the file where you have all your model classes add this at the top:

import 'package:json_annotation/json_annotation.dart';
part 'user.g.dart';

instead of user in part 'user.g.dart'; use the name of the file your model classes located in. in my case it was model.dart so it becomes :

part 'model.g.dart';

this gives a red squiggly line saying :

enter image description here

This is perfectly fine what you need to do is run the following in the terminal in flutter in the root of the project:

flutter pub run build_runner watch

what this does is generate the part 'model.g.dart'; file so you won't see red underline anymore. And it will watch your models (the file containing your models , in my case models.dart file) for any changes to the variables or fields in your classes.

If you make any changes it will automatically regenerate the code required for conversion of models from and to JSON, meaning need for (json decode and encode).

Then you have to add this line to line above every class you have in your models file( in my case models.dart).

so add this to the line above every class :

@JsonSerializable(explicitToJson: true)

like this for exmaple :

@JsonSerializable(explicitToJson: true)
class Place{ some fields};

and in your class definition you have to add this to every class:

factory Place.fromJson(Map<String, dynamic> json) => _$PlaceFromJson(json);
  Map<String, dynamic> toJson() => _$PlaceToJson(this);

then of course you have to change the variables _$PlaceFromJson(json) and _$PlaceToJson(this) to something that will reflect your class name. for example if I have a PhoneNumber class I have to change those tow variables to _$PhoneNumberFromJson(json) and _$PhoneNumberToJson(this) respectively.

now all you need is to create an object of your model, for exmaple Place class, then pass it to jsonEndcode, or jsonDecode like this:

Place place = Place( name : "some name", description :"some description" );

var placeEncoded = jsonEncode(place);
print(placeEncoded);

var placeDecoded = jsonDecode(place);
print(placeDecoded );

That is all you need to do.

the official documentation can be found on this page : https://flutter.dev/docs/development/data-and-backend/json#code-generation

here is what my model.dart look like now :

import 'package:json_annotation/json_annotation.dart';
part 'models.g.dart';


@JsonSerializable(explicitToJson: true)
class Place {
   String name;
   String description;
   List<PhoneNumber> phoneNumbers;
   List<String> tags;
   GPSCoordinante gpsCoordinates;
   List<Service> services;
   List<Album> albums;
   SocialMedia socialMedia;
   List<Comment> comments;
   List<String> imageURLArray;
   int rating;
   int shares;
   int favorites;
   int views;
   String category;
   String subcategory;
   WorkingHour workingHours;
   bool deleted;
   double distanceToUser;
   bool isApproved;
   String address;
   List<String> coverImages;

  Place({
    this.name,
    this.description,
    this.phoneNumbers,
    this.tags,
    this.gpsCoordinates,
    this.services,
    this.albums,
    this.socialMedia,
    this.comments,
    this.imageURLArray,
    this.rating,
    this.shares,
    this.favorites,
    this.views,
    this.category,
    this.subcategory,
    this.workingHours,
    this.deleted,
    this.distanceToUser,
    this.isApproved ,
    this.address,
    this.coverImages,
});

  factory Place.fromJson(Map<String, dynamic> json) => _$PlaceFromJson(json);
  Map<String, dynamic> toJson() => _$PlaceToJson(this);


}

@JsonSerializable(explicitToJson: true)
class PhoneNumber {
   int phoneNumber;
   String owner;

  PhoneNumber({
    this.phoneNumber,
    this.owner
});


   factory PhoneNumber.fromJson(Map<String, dynamic> json) => _$PhoneNumberFromJson(json);
   Map<String, dynamic> toJson() => _$PhoneNumberToJson(this);
}

@JsonSerializable(explicitToJson: true)
class GPSCoordinante {
   double longitude;
   double latitude;

  GPSCoordinante({
    this.longitude,
    this.latitude
});

   factory GPSCoordinante.fromJson(Map<String, dynamic> json) => _$GPSCoordinanteFromJson(json);
   Map<String, dynamic> toJson() => _$GPSCoordinanteToJson(this);

}



@JsonSerializable(explicitToJson: true)
class Service {

  String name;
  String description;

  Service({
    this.name,
    this.description
  });

  factory Service.fromJson(Map<String, dynamic> json) => _$ServiceFromJson(json);
  Map<String, dynamic> toJson() => _$ServiceToJson(this);
}

@JsonSerializable(explicitToJson: true)
class Album {
   String name;
   String nameEn;
   List<Item> items;
   bool isAutoConvertToUSDEnabled;
   bool isAllItemsDeliveryEnabled;

  Album({
    this.name,
    this.nameEn,
    this.items,
    this.isAutoConvertToUSDEnabled,
    this.isAllItemsDeliveryEnabled
});


   factory Album.fromJson(Map<String, dynamic> json) => _$AlbumFromJson(json);
   Map<String, dynamic> toJson() => _$AlbumToJson(this);

}

@JsonSerializable(explicitToJson: true)
class Item{
   List<String> imageVariants;
   String name;
   String nameEn;
   bool isAutoConvertNameToEnglishEnabled;
   List<String> tags;
   int priceIQD;
   double priceUSD;
   String description;
   bool isDeliveryAvailable;
   bool isDinarAutomaticallyConvertedToDollar;
   int itemIndex;
   int albumIndex;
   bool isDeleted;

  Item({
    this.imageVariants,
    this.name,
    this.nameEn,
    this.isAutoConvertNameToEnglishEnabled,
    this.tags,
    this.priceIQD,
    this.priceUSD,
    this.description,
    this.isDeliveryAvailable,
    this.isDinarAutomaticallyConvertedToDollar,
    this.itemIndex,
    this.albumIndex,
    this.isDeleted
});

   factory Item.fromJson(Map<String, dynamic> json) => _$ItemFromJson(json);
   Map<String, dynamic> toJson() => _$ItemToJson(this);

}


@JsonSerializable(explicitToJson: true)
class Comment{
   String user;
   String userId;
   String text;
   DateTime dateTime;

  Comment({
   this.user,
   this.userId,
   this.text,
   this.dateTime
});

   factory Comment.fromJson(Map<String, dynamic> json) => _$CommentFromJson(json);
   Map<String, dynamic> toJson() => _$CommentToJson(this);

}

@JsonSerializable(explicitToJson: true)
class WorkingHour{
   String openingHour;
   String openingHourAmOrPm;
   String closingHour;
   String closingHourAmOrPm;

  WorkingHour({
    this.openingHour,
    this.openingHourAmOrPm,
    this.closingHour,
    this.closingHourAmOrPm
});

   factory WorkingHour.fromJson(Map<String, dynamic> json) => _$WorkingHourFromJson(json);
   Map<String, dynamic> toJson() => _$WorkingHourToJson(this);

}

@JsonSerializable(explicitToJson: true)
class SocialMedia {
  String facebook ;
  String instagram;
  String youTube;
  String snapChat;
  String twitter;
  String googlePlus;
  String pinterest;

  SocialMedia({
    this.facebook,
    this.instagram,
    this.youTube,
    this.snapChat,
    this.twitter,
    this.googlePlus,
    this.pinterest
});

  factory SocialMedia.fromJson(Map<String, dynamic> json) => _$SocialMediaFromJson(json);
  Map<String, dynamic> toJson() => _$SocialMediaToJson(this);

}

I love it when unnecessary coding is trashed and not used anymore. That's why I actually love flutter.

Take care.

Fascinating answered 26/8, 2019 at 19:16 Comment(1)
explicitToJson: true, This is what I needed in my case. Thanks.Constitutionally
I
2

You should use BuiltValue library (https://github.com/google/built_value.dart).

You will need one day studying the technology before start using it.

But in resume your class will be like the class above, and you will have to create a class like that for each complex class, like PhoneNumber, ...:

import 'package:built_collection/built_collection.dart';
import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart';
import 'package:wallet/models/transaction.dart';

import 'json_serializer.dart';

part 'place.g.dart';

abstract class Place
    implements Built<Place, PlaceBuilder>, JsonSerializer {


  factory Place([PlaceBuilder updates(PlaceBuilder builder)]) =
      _$Place;

  Place._();

  factory Place.initState() {
    return Place((b) {
      b..name = "";

      return b;
    });
  }
  @nullable
  String get name;

  @nullable
  String get description;

  @nullable
  BuiltList<PhoneNumber> get phoneNumbers;


  static Serializer<Place> get serializer => _$place;


}

After you create the classes, you must define a serializer.dart class, that you specify all the objects that can be serialized. Observe that for each BuiltList used in the definition, you have to create a factory for it:

    part 'serializers.g.dart';

    @SerializersFor(<Type>[
      Place
    ])
    final Serializers serializers = (_$serializers.toBuilder()
          ..addPlugin(StandardJsonPlugin())
..addBuilderFactory(phoneNumberList.fullType, phoneNumberList.function)
        .build();

ListBuilderFactory<PhoneNumber> phoneNumberList = ListBuilderFactory<PhoneNumber>();

In the last moment, you have to generate the files .g.dart. with the following command:

flutter packages pub run build_runner build --delete-conflicting-outputs

In the end, to decode the json in your project, call:

var body = jsonDecode(jsonPlaces);
        Place places = serializers.deserialize(
          body,
          specifiedType: const FullType(Place),
        );
Interpret answered 26/8, 2019 at 18:35 Comment(1)
Thank you so much Roger for taking the time to writing such a detailed answer. Although I have decided to go with a simpler solution.Fascinating
P
0

You can use @JsonKey annotation with parametrs toJson fromJson, but this work only for getting one field from json

@freezed
class Data with _$Data {
  const Data._();

  @JsonSerializable(fieldRename: FieldRename.snake)
  const factory Data({
    @JsonKey(fromJson: Data._addressFromJson, toJson: Data._addressToJson, name: 'contract')
        required final String address,
    @JsonKey(fromJson: Data._idFromJson, toJson: Data._idToJson, name: 'id')
        required final String type,
    required final String balance,
    required final String title,
    required final String description,
  }) = _Data;

  static String _addressFromJson(Map<String, dynamic> json) {
    return json['address'];
  }

  static Map<String, dynamic> _addressToJson(String data) {
    return {'address': data};
  }

  static String _idFromJson(Map<String, dynamic> json) {
    return json['tokenMetadata']['tokenType'];
  }

  static Map<String, dynamic> _idToJson(String data) {
    return {
      'tokenMetadata': {'tokenType': data}
    };
  }

  factory Data.fromJson(Map<String, dynamic> json) => _$DataFromJson(json);
}
Perplexed answered 15/3, 2023 at 8:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.