2

I am trying to understand how to work with Json in Flutter/Dart.

I want to read a local Json from the assets folder and display it on the screen.

I have in the assets folder the following Json

{
  "title": [
    {
      "number": 8,
      "firstarray": [
        26.6, 27, 26.6, 22.9, 27.1, 26.8, 

      ],
      "secondarray": [
        26.6, 27, 26.6, 22.9, 27.1, 26.8, 
      ]
    },
    {
      "number": 9,
      "firstarray": [
        26.6, 27, 26.6, 22.9, 27.1, 26.8, 
      ],
      "secondarray": [
        26.6, 27, 26.6, 22.9, 27.1, 26.8, 
      ]
    }
  ]
}


I tried to created a Class "DataModel" to be able to read from JSON file.

class DataModel {
  DataModel({this.measure});

  List<DataTitle>? measure;

  factory DataModel.fromJson(Map<String, dynamic> json) {
    return DataModel(
      measure: List<DataTitle>.from(
          json['title'].map((c) => DataTitle.fromJson(c)).toList()),
    );
  }
}

class DataTitle {
  DataTitle(
      {required this.number,
      required this.firstarray,
      required this.secondarray});

  int? number;
  List<double>? firstarray;
  List<double>? secondarray;

  DataTitle.fromJson(Map<String, dynamic> json) {
    number = json['number'];
    firstarray = json['firstarray'] == null
        ? []
        : List<double>.from(json['firstarray'].map((x) => x.toDouble()));
    secondarray = json['secondarray'] == null
        ? []
        : List<double>.from(json['secondarray'].map((x) => x.toDouble()));
  }
}


and I am trying to read and print to the console as follows.

  Future loadData() async {
    String jsonString = await _loadData();
    final jsonResponse = json.decode(jsonString);
    DataTitle measure = DataTitle.fromJson(jsonResponse);
    print('${measure.number} - ${measure.firstarray} - ${measure.secondarray}');
  }

I get at the console printed out,

flutter: null - [] - []

but i was expecting

flutter:  8 -  26.6, 27, 26.6, 22.9, 27.1, 26.8 - 26.6, 27, 26.6, 22.9, 27.1, 26.8, 
0

2 Answers 2

2

Updated part:

You tried to convert the whole json structure to an object of your DataModel class. However, in your json, only the list which is under the title node contains DataModelobjects.

Please check the loadData function below, where I first extract the list out of the title node and then make one DataModel object for each element in the list.

class DataModel {
  DataModel({this.measure});

  List<DataTitle>? measure;

  factory DataModel.fromJson(Map<String, dynamic> json) {
    return DataModel(
      measure: List<DataTitle>.from(
          json['title'].map((c) => DataTitle.fromJson(c)).toList()),
    );
  }
}

class DataTitle {
  DataTitle(
      {required this.number,
      required this.firstarray,
      required this.secondarray});

  int? number;
  List<double>? firstarray;
  List<double>? secondarray;

  DataTitle.fromJson(Map<String, dynamic> json) {
    number = json['number'];
    firstarray = json['firstarray'] == null
        ? []
        : List<double>.from(json['firstarray'].map((x) => x.toDouble()));
    secondarray = json['secondarray'] == null
        ? []
        : List<double>.from(json['secondarray'].map((x) => x.toDouble()));
  }

  @override
  String toString() {
    return 'DataTitle{number: $number, firstarray: $firstarray, secondarray: $secondarray}';
  }
}

Future loadData() async {
  final jsonString = await rootBundle.loadString('assets/data.json');
  final decodedJson = json.decode(jsonString);
  List<DataTitle> dataTileList= (decodedJson ['title'] as List)
      .map((jsonElement) => DataTitle.fromJson(jsonElement))
      .toList();
  print(dataTileList.first);
  print(dataTileList.last);
}

Original Answer

In your code your json data example does not match the way you try to read it. You provide a single element but you try to read a list of elements.

If the json data contains a single element

Your json structure is of type Map<String, dynamic> so this is the data type which json.decode returns (or you also use the method jsonDecode from import 'dart:convert';).

You then try to cast this Map to List<dynamic>which obviously fails.

Instead, the first part should look like this:

    final jsonString =
        await rootBundle.rootBundle.loadString('assets/measurelist.json');
    final json = json.decode(jsonString) as Map<String, dynamic>;

Afterwards you need to call the fromJsonmethod of your data model class.

In your example you try to use map to create one object for each element in the json array. However, in your example json you only have one object which is not part of an array. So that map doesn't make sense.

What should work is directly calling the fromJson method on the single data element you provide, like:

   return DataModel.fromJson(json);

Putting all together, your method should look like this:

  Future<DataModel> readJsonData() async {
    final jsonString =
        await rootBundle.rootBundle.loadString('assets/measurelist.json');
    final json = json.decode(jsonString) as Map<String, dynamic>;
    return DataModel.fromJson(json);
  }

If the json data contains a list of elements

If your datamodel actually contains a list of elements, the json data would look something like this:

[
{
  "title": [
    {
      "number": 8,
      "firstarray": [
        26.6, 27, 26.6, 2, 27.1, 26.8, 26.6, 26.8, 26.8, 27.2, 26.9, 0, 26.8,
        26.8, 26.9, 0, 27.1, 26.8, 27.2, 26.7
      ],
      "secondarray": [
        0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 6.4, 6.4, 2.1, 2, 0, 0, 2, 0, 0, 6.3
      ]
    }
  ]
},
{
  "title": [
    {
      "number": 9,
      "firstarray": [
        26.6, 27, 26.6, 2, 27.1, 26.8, 26.6, 26.8, 26.8, 27.2, 26.9, 0, 26.8,
        26.8, 26.9, 0, 27.1, 26.8, 27.2, 26.7
      ],
      "secondarray": [
        0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 6.4, 6.4, 2.1, 2, 0, 0, 2, 0, 0, 6.3
      ]
    }
  ]
},

...

]

Then your method for parsing it should look something like this:

  Future<List<DataModel>> readJsonData() async {
    final jsonString =
        await rootBundle.rootBundle.loadString('assets/measurelist.json');
    final listOfJsonElements = json.decode(jsonString) as List;
    return listOfJsonElements.map((jsonElement) => DataModel.fromJson(jsonElement)).toList();
  }
Sign up to request clarification or add additional context in comments.

6 Comments

Thanks for the reply and help. I was following your sugestions, but now i have another problem. When i try to display the values on the screen, i get a error saying that "The getter 'number' isn't defined for the type 'DataModel'". And this error is for the 3 values. I changed my post to the actual state that i have right now.
still seems to be an inconsistency with your data models and the way you define your data and try to read it. I don't have much time right now, but the error says that DataModel class doesn't have a getter number. That is correct, your second class Title only has a field named number. Apparently you try to read the field number from a DataModel object but you should read it from a Title object. A suggestion: Try to set breakpoints at important lines, or print some values to the console to check if they are in the format that you expect
Sorry to come back to ask for help. I follow your suggestion trying to print out some values. I refactor my code without any error message. At this moment i try only to print out to the console instead to the screen. Unfortunately it dos not give me what i was expecting .
no worries. You are actually pretty close. You just missed one step. I tried your recent code, made a small adjustment and it's working now. For debugging purposes I also added a toString method to your class. Please check my updated answer
Thanks a lot for your help. I checked your updated answer and tried it out. IT IS WORKING... :). It was very very helpful your suggestions. I need to put my head more around this Json & Map topic in Dart and Flutter. Again, Thanks for your help.
|
1

Fix your model and future void:

DataModel

class DataModel {
  List<Title>? title;

  DataModel({this.title});

  DataModel.fromJson(Map<String, dynamic> json) {
    if (json['title'] != null) {
      title = <Title>[];
      json['title'].forEach((v) {
        title!.add(new Title.fromJson(v));
      });
    }
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    if (this.title != null) {
      data['title'] = this.title!.map((v) => v.toJson()).toList();
    }
    return data;
  }
}

class Title {
  int? number;
  List<double>? firstarray;
  List<int>? secondarray;

  Title({this.number, this.firstarray, this.secondarray});

  Title.fromJson(Map<String, dynamic> json) {
    number = json['number'];
    firstarray = json['firstarray'].cast<double>();
    secondarray = json['secondarray'].cast<int>();
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['number'] = this.number;
    data['firstarray'] = this.firstarray;
    data['secondarray'] = this.secondarray;
    return data;
  }
}

Function

Future<DataModel> readJsonData() async {
    final jsondata = await rootBundle.loadString('assets/measurelist.json');
    final list = json.decode(jsondata) as Map<String, dynamic> ;
    DataModel res = DataModel.fromJson(list);
    return res;
  }

1 Comment

Thanks for the reply and help. I was following your sugestions, but now i have another problem. When i try to display the values on the screen, i get a error saying that "The getter 'number' isn't defined for the type 'DataModel'". And this error is for the 3 values. I changed my post to the actual state that i have right now.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.