Try it yourself with our free Json Formatter tool — runs entirely in your browser, no signup needed.

How to Flatten nested JSON in Dart

How to Flatten Nested JSON in Dart

Flattening nested JSON is a common task in Dart development, especially when working with APIs that return complex, hierarchical data. By flattening the JSON, you can simplify the data structure and make it easier to work with in your Dart applications. In this guide, we'll show you how to flatten nested JSON in Dart, covering the basics, edge cases, and performance tips.

Quick Example

Here's a minimal example that flattens a nested JSON object:

import 'dart:convert';

void main() {
  // Sample nested JSON
  final json = '''
  {
    "name": "John",
    "address": {
      "street": "123 Main St",
      "city": "Anytown",
      "state": "CA",
      "zip": "12345"
    }
  }
  ''';

  // Parse the JSON
  final jsonData = jsonDecode(json);

  // Flatten the JSON
  final flattenedJson = _flattenJson(jsonData);

  print(flattenedJson);
}

Map<String, dynamic> _flattenJson(Map<String, dynamic> json) {
  final flattened = {};
  _flattenHelper(json, flattened, '');
  return flattened;
}

void _flattenHelper(Map<String, dynamic> json, Map<String, dynamic> flattened, String prefix) {
  json.forEach((key, value) {
    final newKey = prefix.isEmpty ? key : '${prefix}_${key}';
    if (value is Map<String, dynamic>) {
      _flattenHelper(value, flattened, newKey);
    } else {
      flattened[newKey] = value;
    }
  });
}

This code defines a _flattenJson function that takes a JSON object and returns a flattened map. The _flattenHelper function is a recursive helper function that does the actual flattening.

Step-by-Step Breakdown

Let's walk through the code:

  1. We start by importing the dart:convert library, which provides the jsonDecode function for parsing JSON strings.
  2. We define a sample nested JSON string and parse it using jsonDecode.
  3. We call the _flattenJson function, passing the parsed JSON object as an argument.
  4. The _flattenJson function creates an empty map to store the flattened data and calls the _flattenHelper function.
  5. The _flattenHelper function takes three arguments: the JSON object to flatten, the flattened map, and a prefix string.
  6. The function iterates over the JSON object's key-value pairs using forEach.
  7. For each pair, it constructs a new key by concatenating the prefix and the original key.
  8. If the value is another JSON object, the function calls itself recursively with the new key as the prefix.
  9. If the value is not a JSON object, the function adds the key-value pair to the flattened map.
  10. Finally, the _flattenJson function returns the flattened map.

Handling Edge Cases

Here are some common edge cases to consider:

Empty/Null Input

If the input JSON is empty or null, the _flattenJson function will return an empty map. You can add a simple null check to handle this case:

Map<String, dynamic> _flattenJson(Map<String, dynamic> json) {
  if (json == null) return {};
  // ...
}

Invalid Input

If the input JSON is invalid (e.g., not a valid JSON string), the jsonDecode function will throw a FormatException. You can catch this exception and return an error message:

try {
  final jsonData = jsonDecode(json);
  // ...
} catch (e) {
  return {'error': 'Invalid JSON'};
}

Large Input

If the input JSON is very large, the recursive _flattenHelper function may cause a stack overflow. To avoid this, you can use an iterative approach instead:

Map<String, dynamic> _flattenJson(Map<String, dynamic> json) {
  final flattened = {};
  final stack = [json];
  while (stack.isNotEmpty) {
    final current = stack.removeLast();
    // ...
  }
  return flattened;
}

Unicode/Special Characters

If the input JSON contains Unicode or special characters, the _flattenJson function will handle them correctly. However, you may need to adjust the prefix concatenation logic to handle non-ASCII characters:

final newKey = prefix.isEmpty ? key : '${prefix}_${key.replaceAll(RegExp(r'[^a-zA-Z0-9_]+'), '_')}';

Common Mistakes

Here are some common mistakes developers make when implementing JSON flattening in Dart:

Mistake 1: Not handling nested objects correctly

Wrong code:

void _flattenHelper(Map<String, dynamic> json, Map<String, dynamic> flattened, String prefix) {
  json.forEach((key, value) {
    if (value is Map<String, dynamic>) {
      flattened[prefix] = value;
    } else {
      flattened[prefix] = value;
    }
  });
}

Corrected code:

void _flattenHelper(Map<String, dynamic> json, Map<String, dynamic> flattened, String prefix) {
  json.forEach((key, value) {
    if (value is Map<String, dynamic>) {
      _flattenHelper(value, flattened, prefix);
    } else {
      final newKey = prefix.isEmpty ? key : '${prefix}_${key}';
      flattened[newKey] = value;
    }
  });
}

Mistake 2: Not handling edge cases correctly

Wrong code:

Map<String, dynamic> _flattenJson(Map<String, dynamic> json) {
  // ...
}

Corrected code:

Map<String, dynamic> _flattenJson(Map<String, dynamic> json) {
  if (json == null) return {};
  try {
    final jsonData = jsonDecode(json);
    // ...
  } catch (e) {
    return {'error': 'Invalid JSON'};
  }
}

Mistake 3: Not using an iterative approach for large inputs

Wrong code:

void _flattenHelper(Map<String, dynamic> json, Map<String, dynamic> flattened, String prefix) {
  // ...
}

Corrected code:

Map<String, dynamic> _flattenJson(Map<String, dynamic> json) {
  final flattened = {};
  final stack = [json];
  while (stack.isNotEmpty) {
    final current = stack.removeLast();
    // ...
  }
  return flattened;
}

Performance Tips

Here are some performance tips for JSON flattening in Dart:

  1. Use an iterative approach for large inputs: Recursive functions can cause stack overflows for very large input JSON. Use an iterative approach instead.
  2. Avoid unnecessary string concatenations: String concatenations can be expensive in Dart. Use a StringBuilder or a List to build the flattened key instead.
  3. Use a Map instead of a List: If you need to store the flattened data in a collection, use a Map instead of a List. Maps are generally faster and more efficient in Dart.

FAQ

Q: What is JSON flattening?

A: JSON flattening is the process of converting a nested JSON object into a flat, one-level JSON object.

Q: Why do I need to flatten JSON?

A: You may need to flatten JSON to simplify the data structure, make it easier to work with in your Dart application, or to store it in a database.

Q: How do I handle edge cases like empty or null input?

A: You can add null checks and error handling to handle edge cases like empty or null input.

Q: How do I handle large inputs?

A: You can use an iterative approach instead of a recursive function to handle large inputs.

Q: How do I handle Unicode or special characters?

A: The _flattenJson function will handle Unicode or special characters correctly. However, you may need to adjust the prefix concatenation logic to handle non-ASCII characters.

AI agent tools available. The CodeTidy MCP Server gives Claude, Cursor, and other AI agents access to 60+ developer tools. One command: npx @codetidy/mcp