Monster Drummer
Monster Drummer

Reputation: 17

How to convert a nested JSON into an Object

I need to send an Object as body parameter to a Post request. My Object is being created from a nested Json and I can't get it working so far.

I've done this quite easy in JavaScript but I haven't found the way to do this in Dart/Flutter.

The "conventional" way I've done this before is as simple as:

var data = {
      "email": "[email protected]",
      "password": "123",
      "first_name": "qweqwe",
      "billing": {
        "email": "[email protected]",
        "phone": "321654978",
      },
      "shipping": {
        "first_name": "qweqwe",
      }
    }

By using the same structure in Dart/Flutter I get below error

[ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: type '_InternalLinkedHashMap' is not a subtype of type 'String' in type cast

I've tried with Map<String, dynamic>, Map<String, String>, using a model for my data....

If I send the same Json but without "billing" and "shipping" (no nested Json) it works.

Upvotes: 0

Views: 2558

Answers (3)

Tobe Osakwe
Tobe Osakwe

Reputation: 721

I think the other answers might be (greatly) over-complicating this. Regardless of which language you're using, to send JSON to a server, you'll need to send the data as a string, with an application/json content type. json.encode handles nested Maps already, so you don't need to do anything special... At all:

// Replace the "..." with the rest of your data.
var data = {"email": "[email protected]", "password": "123", ...}

// `asJson` is a String.
var asJson = json.encode(data);

// Send it
var response = await client.post('example.com/...', body: asJson, headers: {'content-type', 'application/json'});

That's all you need to do. There's not really a need to make an entire User class with fromJson, toJson, etc.

As for this error - Unhandled Exception: type '_InternalLinkedHashMap' is not a subtype of type 'String' in type cast, I have no idea what the exact cause, as you didn't provide the relevant code, but it's clear that at some point, you gave Dart a Map where it was expecting a String. Like you said, you've tried Map<String, dynamic> and Map<String, String, but what was really necessary was a String.

If you're not already using the Dart analyzer (usually in an IDE plugin), this is the kind of error it can easily catch.

Hope this helped!

TLDR: Call json.encode(data) to get a String, and then send that String, along with an application/json content type, to the server.

Upvotes: 2

Robin Reiter
Robin Reiter

Reputation: 2517

The sort answer if you want to upload your data object is to convert it to a string like so:

var jsonString = json.encode(data);
// upload jsonString as body to your server.
print("Json String is " + jsonString);

Map headers = {
  'Content-type' : 'application/json',
};

http.post("www.server.com/myPostEndpoint", body: jsonString, headers: headers);

Like discussed in the comments you may need to wrap the entire content in a data property. You may try this:

var payload = {   
    "data": {
       "email": "[email protected]",
       "password": "123",
       "first_name": "qweqwe",
       "billing": {
         "email": "[email protected]",
         "phone": "321654978",
       },
       "shipping": {
         "first_name": "qweqwe",
       }   
   } 
};

var jsonString = json.encode(payload); // upload jsonString as body to your server. print("Json String is " + jsonString);

Map headers = {   'Content-type': 'application/json', };

http.post("www.server.com/myPostEndpoint", body: jsonString, headers: headers);

tl;dr

Its always a good idea to create a Model. So this could be your User model:

class User {
  final String email;
  final String password;
  final String firstName;
  final Map<String, String> billing;
  final Map<String, String> shipping;

  User({this.email, this.password, this.firstName, this.billing, this.shipping});

  static User fromJson(dynamic json) {
    return User(
      email: json["email"],
      password: json["password"],
      firstName: json["first_name"],
      billing: json["billing"],
      shipping: json["shipping"],
    );
  }

}

It has a static constructor which is handy for creating an instance of this User from a piece of decoded json like you have.

Now you can go ahead and use this model in your code like so:

var data = {
  "email": "[email protected]",
  "password": "123",
  "first_name": "qweqwe",
  "billing": {
    "email": "[email protected]",
    "phone": "321654978",
  },
  "shipping": {
    "first_name": "qweqwe",
  }
};


var user = User.fromJson(data);

print("Users Email is ${user.billing["email"]}");

It would be even cleaner to also create models for both Billing and maybe also Shipping.

Here is a complete example which also includes the toJson() methods to generate Json from a parsed model:

class User {
  final String email;
  final String password;
  final String firstName;
  final Billing billing;
  final Shipping shipping;

  User({this.email, this.password, this.firstName, this.billing, this.shipping});

  static User fromJson(dynamic json) {
    return User(
      email: json["email"],
      password: json["password"],
      firstName: json["first_name"],
      billing: Billing.fromJson(json["billing"]),
      shipping: Shipping.fromJson(json["shipping"]),
    );
  }

  Map<String, dynamic> toJson() => {'email': email, 'password': password, 'first_name': firstName, 'billing': billing.toJson(), 'shipping': shipping.toJson()};
}

class Billing {
  final String email;
  final String phone;

  Billing({this.email, this.phone});

  static Billing fromJson(dynamic json) {
    return Billing(email: json["email"], phone: json["phone"]);
  }

  Map<String, dynamic> toJson() => {'email': email, 'phone': phone};
}

class Shipping {
  final String name;

  Shipping({
    this.name,
  });

  static Shipping fromJson(dynamic json) {
    return Shipping(
      name: json["first_name"],
    );
  }


  Map<String, dynamic> toJson() => {'name': name};
}

And this is how you could use it:

var data = {
  "email": "[email protected]",
  "password": "123",
  "first_name": "qweqwe",
  "billing": {
    "email": "[email protected]",
    "phone": "321654978",
  },
  "shipping": {
    "first_name": "qweqwe",
  }
};


var user = User.fromJson(data);

print("Users Email is ${user.billing.email}");
print("Shipping name is ${user.shipping.name}");

var jsonString = json.encode(user.toJson()); // upload this as body to your server.
print("Json String is " + jsonString);

Upvotes: 4

adami
adami

Reputation: 11

In JSON, there should be commas only between values.

var data = {
  "email": "[email protected]",
  "password": "123",
  "first_name": "qweqwe",
  "billing": {
    "email": "[email protected]",
    "phone": "321654978"
  },
  "shipping": {
    "first_name": "qweqwe"
  }
}

Here i removed commas at the end of the fields "phone" and "first_name".

Upvotes: 1

Related Questions