JSON tools

JSON to Dart Model Generator for Flutter

Paste JSON and get null-safe Dart model classes with fromJson, toJson and copyWith, written by hand or annotated for json_serializable.

  • Free foreverNo trial, no paywall, no usage limits.
  • No sign-upOpen the page and start. We never ask for an email to use a tool.
  • PrivateEverything runs in your browser. Your input is never uploaded or stored on our servers.
  • Works offlineOnce the page has loaded, the tool keeps working without a connection.

Dart options

Every option has a tooltip. Your choices are remembered on this device; your JSON never is.
Reset options to defaults
Name of the top-level type. If your JSON is an array, this names each item. Nested types are named after their keys.
Annotate for json_serializable + build_runner instead of hand-written fromJson/toJson. Run dart run build_runner build to generate the .g.dart part.
Add copyWith() for immutable updates, the pattern Flutter state management relies on.
Parse ISO 8601 date-times into DateTime (and write them back with toIso8601String).

JSON sample

Paste a real response. Arrays are treated as samples: keys missing from some items become optional, null values nullable.
Clear

Dart models

CopyDownload models.dart
Paste JSON or load the example. Code is generated in your browser as you type.

Need this built into your own API or pipeline? Talk to our team

Quick answer

Dart's jsonDecode returns untyped maps, so Flutter apps convert JSON into model classes with a fromJson factory and a toJson method. Paste a sample into Playxoft's free JSON to Dart generator to get them: final, null-safe fields, a safe cast for every value, DateTime parsing for ISO dates, nested models and lists, and an optional copyWith. Switch on json_serializable to get annotated classes for build_runner instead.

  • Null-safe classes
  • fromJson and toJson
  • copyWith
  • json_serializable mode
  • DateTime parsing
  • Nested models and lists
  • Download models.dart

How to use the JSON to Dart

  1. 1Paste a JSON response into JSON sample and set the Root type name.
  2. 2Keep the hand-written output, or switch on json_serializable and set the File name used by the part directive.
  3. 3Toggle copyWith and DateTime to match your app.
  4. 4Copy the code or download models.dart into your lib folder.
  5. 5Decode with Root.fromJson(jsonDecode(source) as Map<String, dynamic>).

How does JSON parsing work in Dart and Flutter?

jsonDecode from dart:convert returns plain, untyped data: a Map<String, dynamic> for objects and a List<dynamic> for arrays. Reading data['user']['name'] compiles, but every typo and wrong type becomes a runtime crash. Model classes fix that by converting the map once, at the edge of your app, into typed objects:

import 'dart:convert';
import 'package:http/http.dart' as http;

final response = await http.get(Uri.parse('https://api.example.com/me'));
final user = User.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
print(user.displayName); // typed, autocompleted and null-safe

The generator writes a fromJson factory and a toJson method for every class, converting nested objects and lists of objects recursively, so the whole response becomes typed in one call. The output is already formatted the way dart format would format it.

Hand-written fromJson or json_serializable?

Hand-written (default)json_serializable
DependenciesNonejson_annotation, plus build_runner and json_serializable for development
Build stepNonedart run build_runner build
Extra fileNoneA generated .g.dart part
Best forSmall apps and prototypesLarge apps with many models

Switch on json_serializable and the generator writes annotated classes instead: @JsonSerializable(), @JsonKey(name: ...) for renamed keys, and a part directive that must match your file name. Then add the packages and run the builder:

dart pub add json_annotation dev:build_runner dev:json_serializable
dart run build_runner build --delete-conflicting-outputs

If you also want value equality and union types, freezed builds on the same annotations.

How are JSON types mapped to Dart?

JSON in your sampleDart typeRead with
StringStringjson['k'] as String
ISO 8601 date-timeDateTimeDateTime.parse(json['k'] as String)
Whole numberintjson['k'] as int
Decimal numberdouble(json['k'] as num).toDouble()
Booleanbooljson['k'] as bool
ArrayList<T>.map(...).toList()
ObjectA model classModel.fromJson(...)
Mixed values, or only nulldynamicjson['k']

The num cast for decimals is deliberate. jsonDecode turns 10 into an int and 10.5 into a double, so a price that happens to be a whole number would crash a plain as double cast with "type 'int' is not a subtype of type 'double'".

How does null safety affect JSON models?

Every field is final and non-nullable unless your samples prove otherwise. A key that was null or missing in any sample becomes nullable (String?), drops required from the constructor and is read with a null-aware cast. In toJson, keys that were missing from some samples are written only when they have a value:

Map<String, dynamic> toJson() {
  return {
    'id': id,
    if (couponCode != null) 'couponCode': couponCode,
  };
}

On the web, Dart's int is a JavaScript number, so integers above 2^53 lose precision there just as they do in JavaScript. If your Flutter app also runs in the browser, ask the API to send large IDs as strings.

How do I parse large JSON without dropping frames?

jsonDecode runs on the isolate that calls it, and a multi-megabyte response can take long enough to drop frames. Move the work to a background isolate:

// Dart 2.19 and later
final user = await Isolate.run(
  () => User.fromJson(jsonDecode(body) as Map<String, dynamic>),
);

// Flutter's compute() also works on the web (where it runs on the main thread)
final user = await compute(parseUser, body);

For small responses, decoding inline is fine: starting an isolate has a cost of its own. To check a large response before you model it, the JSON Formatter shows its structure as a collapsible tree.

Frequently asked questions

jsonDecode turns whole numbers like 10 into int, even in a field that is usually a decimal. Casting that value with as double fails. Read numbers with (json['price'] as num).toDouble(), which the generated code does for every decimal field.
json_serializable generates fromJson and toJson. freezed builds on it and also generates copyWith, value equality, toString and union types. Start with json_serializable and move to freezed when you want immutable data classes with less boilerplate.
A field is nullable when at least one sample had null for it or left the key out. Paste several real responses as an array to get the most accurate result; fields present in every sample stay non-nullable and required.
Cast the decoded value to a list and map each item: (jsonDecode(source) as List<dynamic>).map((e) => Root.fromJson(e as Map<String, dynamic>)).toList(). The generated usage comment shows this when your sample is an array.
Not with the common pattern of field ?? this.field, because passing null keeps the old value. If you need that, use freezed, which generates a copyWith that can clear nullable fields.
The generated fromJson already does it: json['created_at'] is read into a createdAt field and written back under the original key in toJson. With json_serializable, each renamed field gets a JsonKey name, or you can set fieldRename: FieldRename.snake on the JsonSerializable annotation for the whole class.

Last updated by the Playxoft engineering team.

From the engineers who built this tool

Fighting messy data every day?

When payloads, schemas and integrations keep breaking, the fix is usually upstream. We design typed APIs and backends that stay clean as you grow. Tell us what you are building and we will suggest the simplest architecture that fits.