JSON to Dart model in three steps
- Drop a
.jsonfile on the editor, paste the text, or fetch it with From URL. - Set Root name to your model’s name, for example
Product. - Copy the output: plain Dart, with no imports, annotations or build step.
A shop product
The sample’s main class is below; Rating and Image follow it in the output, built the same way.
{
"id": 118,
"title": "Linen shirt",
"price": 59.9,
"currency": "EUR",
"sizes": ["S", "M", "L"],
"images": [
{
"url": "https://cdn.example.com/s/118-a.jpg",
"primary": true
},
{
"url": "https://cdn.example.com/s/118-b.jpg",
"primary": false
}
],
"rating": { "average": 4.6, "count": 214 },
"onSale": false
}
class Product {
final int id;
final String title;
final double price;
final String currency;
final List<String> sizes;
final List<Image> images;
final Rating rating;
final bool onSale;
Product({required this.id, required this.title, required this.price, required this.currency, required this.sizes, required this.images, required this.rating, required this.onSale});
factory Product.fromJson(Map<String, dynamic> json) => Product(
id: json['id'] as int,
title: json['title'] as String,
price: (json['price'] as num).toDouble(),
currency: json['currency'] as String,
sizes: (json['sizes'] as List<dynamic>).map((e) => e as String).toList(),
images: (json['images'] as List<dynamic>).map((e) => Image.fromJson(e as Map<String, dynamic>)).toList(),
rating: Rating.fromJson(json['rating'] as Map<String, dynamic>),
onSale: json['onSale'] as bool,
);
Map<String, dynamic> toJson() => {
'id': id,
'title': title,
'price': price,
'currency': currency,
'sizes': sizes,
'images': images.map((e) => e.toJson()).toList(),
'rating': rating.toJson(),
'onSale': onSale,
};
}
Why the casts look like that
jsonDecode returns dynamic (objects as Map<String, dynamic>, arrays as List<dynamic>), so every value needs a cast:
- Decimals: on the Dart VM a whole number such as
60decodes as anint, andas doublewould throw.(json['price'] as num).toDouble()accepts both. - Lists: a
List<dynamic>can’t be cast toList<String>withas, so each element is cast inside amap. Lists of objects callImage.fromJsonper element, andtoJsonmaps them back. - Nested objects go through their own
fromJsonandtoJson.
Field names are camelCase, but fromJson and toJson use the exact JSON key: on_sale would become the field onSale, read from json['on_sale'].
Nullable fields
Values that were null or missing get a nullable type and drop required from the constructor; array items are merged first to find them (rules on JSON to TypeScript). With Coupons as the root name:
[
{ "code": "SPRING", "percent": 15, "expires": "2026-12-31" },
{ "code": "VIP", "percent": null }
]
class Coupon {
final String code;
final int? percent;
final String? expires;
Coupon({required this.code, this.percent, this.expires});
factory Coupon.fromJson(Map<String, dynamic> json) => Coupon(
code: json['code'] as String,
percent: json['percent'] as int?,
expires: json['expires'] as String?,
);
Map<String, dynamic> toJson() => {
'code': code,
'percent': percent,
'expires': expires,
};
}
Mixed types and values that were only ever null become dynamic.
Using the model
import 'dart:convert';
Product parseProduct(String body) =>
Product.fromJson(jsonDecode(body) as Map<String, dynamic>);
String encodeProduct(Product product) => jsonEncode(product.toJson());
List<Coupon> parseCoupons(String body) => (jsonDecode(body) as List<dynamic>)
.map((e) => Coupon.fromJson(e as Map<String, dynamic>))
.toList();
With the http package, pass response.body to parseProduct. jsonEncode(product) also works, because jsonEncode calls toJson() on objects it doesn’t know.
The generated Image class clashes with Flutter’s Image widget in any file that also imports package:flutter/material.dart, so rename it (say, ProductImage) or hide one import. The classes also don’t override == or hashCode, so two products with the same data aren’t equal; add them, or use a package such as equatable, if you compare models. For native apps, JSON to Kotlin and JSON to Swift build the same model.