JSON to Dart

Generate Dart classes from JSON with fromJson and toJson

Try an example
Root class name:

JSON Input

Dart Output

Runs locally Β· Safe to paste secrets
Dart classes will appear here…

What is JSON to Dart Conversion?

JSON to Dart conversion takes a raw JSON object and produces Dart class definitions with typed fields, a named constructor, a fromJson factory, and a toJson method. Dart has no runtime reflection in Flutter (dart:mirrors is disabled), so you cannot deserialize JSON into typed objects without writing explicit mapping code. Every REST API response, Firebase document, or config payload needs a corresponding Dart model class before you can access its fields with type safety.

A typical Dart model class for JSON declares final fields for each key, a constructor with named parameters (using the required keyword for non-nullable fields), a factory constructor named fromJson that reads from a Map of String to dynamic, and a toJson method that returns a Map of String to dynamic. Nested JSON objects become separate classes. Arrays become typed List fields. Nullable JSON values use Dart's null-safety syntax with the ? suffix on the type.

Writing these model classes by hand means reading each JSON key, deciding the Dart type, creating the fromJson cast for each field (including list mapping with .map().toList()), building a toJson map literal, and repeating for every nested object. For a JSON object with 12 fields and 2 nested objects, that means 3 classes, 6 factory lines, and dozens of cast expressions. A converter produces all of this in milliseconds from a single paste.

Why Use a JSON to Dart Converter?

Manually writing Dart model classes from JSON involves reading field names, guessing types from sample values, writing fromJson casts with correct null handling, and repeating the process for nested objects. When the API shape changes, every field update touches the constructor, fromJson, and toJson. A converter removes that repetitive work.

⚑
Instant class generation
Paste JSON and get complete Dart classes with constructors, fromJson factories, and toJson methods in under a second. Nested objects and lists are detected and mapped automatically.
πŸ”’
Privacy-first processing
Conversion runs entirely in your browser using JavaScript. Your JSON data never leaves your machine. API keys, tokens, and production payloads stay private.
πŸ“
Null-safe output
Generated classes use Dart's sound null safety. Nullable JSON fields get the ? type suffix and skip the required keyword in constructors. Non-null fields are marked required.
πŸ“¦
No install or signup
Open the page and paste your JSON. No Dart SDK, no pub dependencies, no account. Works on any device with a browser.

JSON to Dart Use Cases

Flutter App Development
Generate model classes for REST API responses consumed by http or dio packages. Paste the JSON your backend returns and get Dart classes ready for jsonDecode and widget rendering.
Firebase / Firestore Integration
Create typed model classes for Firestore document snapshots. Paste a sample document as JSON, generate the Dart class, and use fromJson with snapshot.data() in your repository layer.
State Management Models
Define typed state objects for Riverpod, Bloc, or Provider. Paste the expected state shape as JSON and get immutable Dart classes with toJson for state persistence and hydration.
API Client Code Generation
Produce request and response models for Retrofit or Chopper API clients. Paste sample JSON payloads and get Dart classes that match your OpenAPI response schemas.
Automated Testing
Build typed test fixtures from real API responses. Paste a JSON sample, generate the model class, and use it directly in unit and widget tests without hand-writing fixture structs.
Learning Dart Patterns
Students learning Flutter can paste any JSON structure and see how Dart represents it: final fields, named constructors, factory fromJson patterns, and null-safety annotations in practice.

JSON to Dart Type Mapping

Every JSON value maps to a specific Dart type. The table below shows how the converter translates each JSON type. The Alternative column shows types used in less common or manual mapping scenarios.

JSON TypeExampleDart TypeAlternative
string"hello"StringString
number (integer)42intint
number (float)3.14doubledouble
booleantrueboolbool
nullnulldynamicNull / dynamic
object{"k": "v"}NestedClassMap<String, dynamic>
array of strings["a", "b"]List<String>List<String>
array of objects[{"id": 1}]List<Item>List<Item>
mixed array[1, "a"]List<dynamic>List<dynamic>

Dart JSON Serialization Approaches

Dart and Flutter offer multiple ways to handle JSON serialization. Manual fromJson/toJson is the simplest approach and requires no code generation. For larger projects, build_runner-based solutions like json_serializable and freezed generate the boilerplate at compile time, reducing errors and keeping models consistent with the JSON contract.

ApproachDescriptionSource
json_serializableCode-generation-based JSON serialization. Generates .g.dart files at build time via build_runner. Type-safe and compile-time verified.Official (Google)
freezedGenerates immutable data classes with copyWith, fromJson/toJson, equality, and pattern matching support. Built on top of json_serializable.Community (rrousselGit)
built_valueGenerates immutable value types with serialization. Enforces immutability patterns. Used in larger codebases with strict data modeling.Google
dart_mappableAnnotation-based mapper that generates fromJson, toJson, copyWith, and equality. Simpler setup than freezed with similar features.Community
Manual fromJson/toJsonHand-written factory constructors and toJson methods. No code generation needed. Full control over the mapping logic.Built-in Dart

Manual vs json_serializable vs freezed

Dart has three common approaches for JSON model classes. Manual fromJson/toJson has zero dependencies. json_serializable automates the mapping code. freezed adds immutability, copyWith, and pattern matching on top of json_serializable.

Manual fromJson/toJson
Write fromJson factories and toJson methods by hand. Zero dependencies, no build_runner step. You have full control over the casting logic and error handling. Best for small projects with a few models, or when you need custom deserialization that code generators cannot express. This is the output format this tool generates.
json_serializable
Annotate your class with @JsonSerializable() and run dart run build_runner build. The generator creates a .g.dart file with _$ClassFromJson and _$ClassToJson functions. Handles @JsonKey for field renaming, default values, and custom converters. The standard choice for medium-to-large Flutter projects.
freezed
Annotate with @freezed and get immutable classes with fromJson, toJson, copyWith, equality (== and hashCode), and toString generated automatically. Supports union types for sealed class patterns. Requires both freezed and json_serializable as dev dependencies. Best for state management models and complex domain objects.

Code Examples

These examples show how to use generated Dart classes for JSON deserialization, how to set up json_serializable with build_runner, and how to generate Dart classes programmatically from JavaScript and Python.

Dart (manual fromJson / toJson)
import 'dart:convert';

class User {
  final int id;
  final String name;
  final String email;
  final bool active;
  final Address address;
  final List<String> tags;

  User({
    required this.id,
    required this.name,
    required this.email,
    required this.active,
    required this.address,
    required this.tags,
  });

  factory User.fromJson(Map<String, dynamic> json) => User(
    id: json['id'] as int,
    name: json['name'] as String,
    email: json['email'] as String,
    active: json['active'] as bool,
    address: Address.fromJson(json['address'] as Map<String, dynamic>),
    tags: (json['tags'] as List<dynamic>).map((e) => e as String).toList(),
  );

  Map<String, dynamic> toJson() => {
    'id': id,
    'name': name,
    'email': email,
    'active': active,
    'address': address.toJson(),
    'tags': tags,
  };
}

class Address {
  final String street;
  final String city;
  final String zip;

  Address({required this.street, required this.city, required this.zip});

  factory Address.fromJson(Map<String, dynamic> json) => Address(
    street: json['street'] as String,
    city: json['city'] as String,
    zip: json['zip'] as String,
  );

  Map<String, dynamic> toJson() => {'street': street, 'city': city, 'zip': zip};
}

void main() {
  final jsonStr = '{"id":1,"name":"Alice","email":"alice@example.com","active":true,"address":{"street":"123 Main","city":"Springfield","zip":"12345"},"tags":["admin","user"]}';
  final user = User.fromJson(jsonDecode(jsonStr));
  print(user.name); // -> Alice
  print(jsonEncode(user.toJson())); // -> round-trip back to JSON
}
Dart (json_serializable + build_runner)
// pubspec.yaml dependencies:
//   json_annotation: ^4.8.0
//   dev_dependencies:
//     build_runner: ^2.4.0
//     json_serializable: ^6.7.0

import 'package:json_annotation/json_annotation.dart';
part 'user.g.dart'; // generated by: dart run build_runner build

@JsonSerializable()
class User {
  final int id;
  final String name;
  final String email;
  @JsonKey(name: 'is_active')
  final bool isActive;
  final List<String> tags;

  User({
    required this.id,
    required this.name,
    required this.email,
    required this.isActive,
    required this.tags,
  });

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  Map<String, dynamic> toJson() => _$UserToJson(this);
}

// Run code generation:
// dart run build_runner build --delete-conflicting-outputs
JavaScript (generate Dart classes from JSON)
function jsonToDart(obj, name = "Root") {
  const classes = [];
  function infer(val, fieldName) {
    if (val === null) return "dynamic";
    if (typeof val === "string") return "String";
    if (typeof val === "number") return Number.isInteger(val) ? "int" : "double";
    if (typeof val === "boolean") return "bool";
    if (Array.isArray(val)) {
      const first = val.find(v => v !== null);
      if (!first) return "List<dynamic>";
      return `List<${infer(first, fieldName)}>`;
    }
    if (typeof val === "object") {
      const cls = fieldName.charAt(0).toUpperCase() + fieldName.slice(1);
      build(val, cls);
      return cls;
    }
    return "dynamic";
  }
  function build(obj, cls) {
    const fields = Object.entries(obj).map(([k, v]) =>
      `  final ${infer(v, k)} ${k};`
    );
    classes.push(`class ${cls} {\n${fields.join("\n")}\n}`);
  }
  build(obj, name);
  return classes.join("\n\n");
}

console.log(jsonToDart({ id: 1, name: "Alice", scores: [98, 85] }, "User"));
// class User {
//   final int id;
//   final String name;
//   final List<int> scores;
// }
Python (generate Dart model from JSON)
import json

def json_to_dart(obj: dict, class_name: str = "Root") -> str:
    classes = []

    def infer(val, name):
        if val is None:
            return "dynamic"
        if isinstance(val, bool):
            return "bool"
        if isinstance(val, int):
            return "int"
        if isinstance(val, float):
            return "double"
        if isinstance(val, str):
            return "String"
        if isinstance(val, list):
            if not val:
                return "List<dynamic>"
            return f"List<{infer(val[0], name)}>"
        if isinstance(val, dict):
            cls = name[0].upper() + name[1:]
            build(val, cls)
            return cls
        return "dynamic"

    def build(obj, cls):
        fields = [f"  final {infer(v, k)} {k};" for k, v in obj.items()]
        classes.append(f"class {cls} {{\n" + "\n".join(fields) + "\n}")

    build(obj, class_name)
    return "\n\n".join(classes)

data = json.loads('{"id": 1, "name": "Alice", "active": true}')
print(json_to_dart(data, "User"))
# class User {
#   final int id;
#   final String name;
#   final bool active;
# }

Frequently Asked Questions

Why can't Flutter use dart:mirrors for JSON deserialization?
Flutter disables dart:mirrors (the reflection library) because tree-shaking requires knowing at compile time which code is used. Reflection would prevent the compiler from removing unused classes, increasing app size. This is why Flutter projects need explicit fromJson/toJson methods or code generation instead of runtime reflection.
Should I use json_serializable or write fromJson by hand?
For projects with fewer than 10 model classes, hand-written fromJson/toJson is straightforward and adds no build dependencies. Once you have more than 10 models, or models that change frequently, json_serializable saves time and reduces copy-paste errors. It generates the mapping code from annotations, so field additions only require re-running build_runner.
How does the converter handle nested JSON objects?
Each nested JSON object becomes a separate Dart class. If a JSON field named "address" contains an object with "street" and "city" keys, the converter creates an Address class with those fields and types the parent field as Address. The fromJson factory casts the nested map and passes it to Address.fromJson.
What happens when a JSON field is null?
Null fields are typed as dynamic with a ? suffix because the converter cannot determine the intended type from a null value alone. Replace dynamic? with the correct type (String?, int?, etc.) once you know it from your API contract. Dart's null safety will then enforce correct null checks at compile time.
How do I handle JSON keys that are not valid Dart identifiers?
JSON keys like "first-name" or "2nd_place" are not valid Dart field names. With manual fromJson, you can map any JSON key to any Dart field name in the factory constructor. With json_serializable, use @JsonKey(name: 'first-name') to annotate the field. The converter automatically converts key names to camelCase Dart fields.
Can I use freezed with the output from this tool?
The tool generates standard Dart classes with manual fromJson/toJson. To convert to freezed, change the class to use the @freezed annotation, remove the handwritten constructor and factory, and add the freezed mixin. Then run build_runner to generate the .freezed.dart and .g.dart files. The field names and types from the generated output transfer directly.
What is the difference between a raw map and a typed Dart class for JSON?
A raw map (Map of String to dynamic) gives you raw access to JSON data with no type checking. You must cast every value at the call site, and typos in key names fail silently at runtime. A typed Dart class catches type mismatches at the fromJson boundary, provides IDE autocompletion for field names, and makes refactoring safe. The performance difference is negligible; the benefit is entirely about correctness and maintainability.