I've looked at many examples but I'm still struggling to decode the following json in flutter. I'm new to flutter and I found that json.decode
will only work if I map it as List<dynamic>
. But, I believe I need to eventually map it to List<Map<String, Map<String, dynamic>>
. I could be wrong about this so I'm looking for input. I've created two models, ProductLine
and PartNumber
. The challenge is that the keys shown as Product line #
need to be added to the PartNumber
object as a value as well as to the name
value of ProductLine
. These keys can be any string value and are not predetermined.
[
{
"Product line 1": [
{
"partNumber": "160-9013-900",
"orderable": true,
"description": "Part Number Description"
},
{
"partNumber": "160-9104-900",
"orderable": true,
"description": "Part Number Description"
},
{
"partNumber": "160-9105-900",
"orderable": false,
"description": "Part Number Description"
}
]
},
{
"Product line 2": [
{
"partNumber": "160-9113-900",
"orderable": true,
"description": "Part Number Description"
},
{
"partNumber": "160-9114-900",
"orderable": true,
"description": "Part Number Description"
},
{
"partNumber": "160-9115-900",
"orderable": false,
"description": "Part Number Description"
}
]
},
{
"Product line 3": [
{
"partNumber": "160-9205-900",
"orderable": true,
"description": "Part Number Description"
},
{
"partNumber": "160-9211-900",
"orderable": true,
"description": "Part Number Description"
},
{
"partNumber": "160-9212-900",
"orderable": false,
"description": "Part Number Description"
}
]
}
]
Here are my models for ProductLine
and PartNumber
. The @JsonSerializable()
code is autogenerated by build_runner.
const uuid = Uuid();
@JsonSerializable()
class PartNumber {
PartNumber({
String? id,
required this.partNumber,
required this.orderable,
required this.description,
required this.productLine,
}) : id = id ?? uuid.v4();
final String id;
final String partNumber;
final bool orderable;
final String description;
final String productLine;
factory PartNumber.fromJson(Map<String, dynamic> json) =>
_$PartNumberFromJson(json);
Map<String, dynamic> toJson() => _$PartNumberToJson(this);
PartNumber _$PartNumberFromJson(Map<String, dynamic> json) => PartNumber(
id: json['id'] as String?,
partNumber: json['partNumber'] as String,
orderable: json['orderable'] as bool,
description: json['description'] as String,
productLine: json['productLine'] as String,
);
Map<String, dynamic> _$PartNumberToJson(PartNumber instance) =>
<String, dynamic>{
'id': instance.id,
'partNumber': instance.partNumber,
'orderable': instance.orderable,
'description': instance.description,
'productLine': instance.productLine,
};
}
@JsonSerializable()
class ProductLine {
ProductLine({
String? id,
required this.name,
required this.partNumbers,
}) : id = id ?? uuid.v4();
final String id;
final String name;
final List<PartNumber> partNumbers;
factory ProductLine.fromJson(Map<String, dynamic> json) =>
_$ProductLineFromJson(json);
Map<String, dynamic> toJson() => _$ProductLineToJson(this);
ProductLine _$ProductLineFromJson(Map<String, dynamic> json) => ProductLine(
id: json['id'] as String?,
name: json['name'] as String,
partNumbers: (json['partNumbers'] as List<dynamic>)
.map((e) => PartNumber.fromJson(e as Map<String, dynamic>))
.toList(),
);
Map<String, dynamic> _$ProductLineToJson(ProductLine instance) =>
<String, dynamic>{
'id': instance.id,
'name': instance.name,
'partNumbers': instance.partNumbers,
};
}
The error I'm getting is:
E/flutter (18120): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: type 'Null' is not a subtype of type 'String' in type cast E/flutter (18120): #0 _$ProductLineFromJson (package:test_app/models/product_line.g.dart:11:26) E/flutter (18120): #1 new ProductLine.fromJson (package:test_app/models/product_line.dart:24:7) E/flutter (18120): #2 PartNumberManager.formatPartNumbers (package:test_app/managers/part_number_manager.dart:62:39) E/flutter (18120): E/flutter (18120): #3 PartNumberManager.loadPartNumbers (package:test_app/managers/part_number_manager.dart:36:5) E/flutter (18120):
Here is my code to read the json and build my models. What am I doing wrong?
class PartNumberManager {
List<PartNumber>? partNumbers;
Future<bool> loadPartNumbers() async {
await formatPartNumbers();
final partNumbersLoaded = await batchInsertPartNumbers();
return partNumbersLoaded;
}
// Read part_numbers.json file before adding data to the sql database
Future<List<dynamic>> readJson() async {
try {
final String response =
await rootBundle.loadString('assets/part_numbers.json');
final data = (json.decode(response) as List<dynamic>);
return data;
} catch (error) {
throw Exception(
'PartNumberManager, Unexpected error reading JSON: $error');
}
}
Future<void> formatPartNumbers() async {
List<ProductLine> tmpProductLines = [];
List<dynamic> data = await readJson();
for (var i = 0; i < data.length; i++) {
final productLine = ProductLine.fromJson(data[i]);
tmpProductLines.add(productLine);
}
for (var product in tmpProductLines) {
for (var pnum in product.partNumbers) {
final partNumber = PartNumber(
partNumber: pnum.partNumber,
orderable: pnum.orderable,
description: pnum.description,
productLine: product.name,
);
partNumbers!.add(partNumber);
}
}
}
}
class TestCallClass {
void testFunction() {
try {
//print(jsonData);
List<ProductLine> productLines = ProductLineFromJson(jsonData);
for (var productLine in productLines) {
print('Product Line: ${productLine.productLine}');
for (var product in productLine.products!) {
print(' Part Number: ${product.partNumber}');
print(' Orderable: ${product.orderable}');
print(' Description: ${product.description}');
}
print('-------------------');
}
} catch (e) {
print('Error decoding JSON: $e');
}
}
}
class Product {
String? partNumber;
bool? orderable;
String? description;
Product({
this.partNumber,
this.orderable,
this.description,
});
factory Product.fromJson(Map<String, dynamic> json) {
return Product(
partNumber: json['partNumber'],
orderable: json['orderable'],
description: json['description'],
);
}
}
List<ProductLine> ProductLineFromJson(dynamic str) => List<ProductLine>.from((str as List<dynamic>).map((x) => ProductLine.fromJson(x)));
class ProductLine {
String? productLine;
List<Product>? products;
ProductLine({
this.productLine,
this.products,
});
factory ProductLine.fromJson(Map<String, dynamic> json) {
final List<Product> products = (json[json.keys.first] as List).map((productJson) => Product.fromJson(productJson)).toList();
return ProductLine(
productLine: json.keys.first,
products: products,
);
}
}
var jsonData = [
{
"Product line 1": [
{"partNumber": "160-9013-900", "orderable": true, "description": "Part Number Description"},
{"partNumber": "160-9104-900", "orderable": true, "description": "Part Number Description"},
{"partNumber": "160-9105-900", "orderable": false, "description": "Part Number Description"}
]
},
{
"Product line 2": [
{"partNumber": "160-9113-900", "orderable": true, "description": "Part Number Description"},
{"partNumber": "160-9114-900", "orderable": true, "description": "Part Number Description"},
{"partNumber": "160-9115-900", "orderable": false, "description": "Part Number Description"}
]
},
{
"Product line 3": [
{"partNumber": "160-9205-900", "orderable": true, "description": "Part Number Description"},
{"partNumber": "160-9211-900", "orderable": true, "description": "Part Number Description"},
{"partNumber": "160-9212-900", "orderable": false, "description": "Part Number Description"}
]
}
];