在这里插入图片描述
在这里插入图片描述

概述

经过前面章节的学习,我们已经掌握了JSON解析的核心技术,包括数据结构、手动解析、序列化反序列化、复杂结构处理、错误处理、格式化和文件IO等。本章将通过综合练习,将这些知识融会贯通,解决实际开发中常见的JSON数据处理问题。

本章将涵盖以下实战场景:

  • JSON验证与格式检查
  • 数据统计与分析
  • 字段提取与路径遍历
  • 数据转换与重组
  • API响应处理综合案例

练习任务一:JSON验证

任务描述

实现一个JSON验证工具,能够检查JSON字符串的格式是否正确,并给出详细的验证结果。

实现方案

import 'dart:convert';

class JsonValidator {
  static ValidationResult validate(String input) {
    if (input.isEmpty) {
      return ValidationResult(
        isValid: false,
        errorMessage: '输入为空',
        jsonType: JsonType.none,
        elementCount: 0,
      );
    }

    try {
      final dynamic parsed = json.decode(input);

      if (parsed is Map) {
        return ValidationResult(
          isValid: true,
          errorMessage: '',
          jsonType: JsonType.object,
          elementCount: parsed.length,
        );
      } else if (parsed is List) {
        return ValidationResult(
          isValid: true,
          errorMessage: '',
          jsonType: JsonType.array,
          elementCount: parsed.length,
        );
      } else {
        return ValidationResult(
          isValid: true,
          errorMessage: '',
          jsonType: JsonType.primitive,
          elementCount: 1,
        );
      }
    } on FormatException catch (e) {
      return ValidationResult(
        isValid: false,
        errorMessage: 'JSON格式错误: $e',
        jsonType: JsonType.none,
        elementCount: 0,
      );
    } on TypeError catch (e) {
      return ValidationResult(
        isValid: false,
        errorMessage: '类型错误: $e',
        jsonType: JsonType.none,
        elementCount: 0,
      );
    } catch (e) {
      return ValidationResult(
        isValid: false,
        errorMessage: '未知错误: $e',
        jsonType: JsonType.none,
        elementCount: 0,
      );
    }
  }
}

enum JsonType {
  none,
  object,
  array,
  primitive,
}

class ValidationResult {
  final bool isValid;
  final String errorMessage;
  final JsonType jsonType;
  final int elementCount;

  ValidationResult({
    required this.isValid,
    required this.errorMessage,
    required this.jsonType,
    required this.elementCount,
  });

  
  String toString() {
    if (isValid) {
      final String typeName = jsonType == JsonType.object ? 'Object' :
                              jsonType == JsonType.array ? 'Array' : 'Primitive';
      return '✅ JSON验证通过!\n类型: $typeName\n元素数量: $elementCount';
    } else {
      return '❌ JSON格式错误: $errorMessage';
    }
  }
}

使用示例

void main() {
  // 测试有效JSON
  final String validJson = '{"name":"张三","age":25,"isStudent":true}';
  final ValidationResult result1 = JsonValidator.validate(validJson);
  print(result1);

  // 测试无效JSON
  final String invalidJson = '{"name":"张三",age:25}';
  final ValidationResult result2 = JsonValidator.validate(invalidJson);
  print(result2);

  // 测试数组
  final String arrayJson = '[1, 2, 3, 4, 5]';
  final ValidationResult result3 = JsonValidator.validate(arrayJson);
  print(result3);
}

练习任务二:数据统计

任务描述

实现一个JSON数据统计工具,能够统计JSON中的各种元素类型和数量。

实现方案

import 'dart:convert';

class JsonStatistics {
  static StatisticsResult analyze(String jsonString) {
    final dynamic parsed = json.decode(jsonString);
    
    if (parsed is Map<String, dynamic>) {
      return _analyzeObject(parsed);
    } else if (parsed is List) {
      return _analyzeArray(parsed);
    } else {
      return StatisticsResult(
        totalElements: 1,
        stringCount: parsed is String ? 1 : 0,
        numberCount: parsed is num ? 1 : 0,
        boolCount: parsed is bool ? 1 : 0,
        arrayCount: 0,
        objectCount: 0,
        nullCount: parsed == null ? 1 : 0,
        nestedDepth: 0,
      );
    }
  }

  static StatisticsResult _analyzeObject(Map<String, dynamic> obj) {
    int stringCount = 0;
    int numberCount = 0;
    int boolCount = 0;
    int arrayCount = 0;
    int objectCount = 0;
    int nullCount = 0;
    int maxDepth = 0;

    for (final dynamic value in obj.values) {
      final ValueAnalysis analysis = _analyzeValue(value, 1);
      
      stringCount += analysis.stringCount;
      numberCount += analysis.numberCount;
      boolCount += analysis.boolCount;
      arrayCount += analysis.arrayCount;
      objectCount += analysis.objectCount;
      nullCount += analysis.nullCount;
      
      if (analysis.maxDepth > maxDepth) {
        maxDepth = analysis.maxDepth;
      }
    }

    return StatisticsResult(
      totalElements: obj.length,
      stringCount: stringCount,
      numberCount: numberCount,
      boolCount: boolCount,
      arrayCount: arrayCount,
      objectCount: objectCount + 1,
      nullCount: nullCount,
      nestedDepth: maxDepth,
    );
  }

  static StatisticsResult _analyzeArray(List<dynamic> arr) {
    int stringCount = 0;
    int numberCount = 0;
    int boolCount = 0;
    int arrayCount = 0;
    int objectCount = 0;
    int nullCount = 0;
    int maxDepth = 0;

    for (final dynamic value in arr) {
      final ValueAnalysis analysis = _analyzeValue(value, 1);
      
      stringCount += analysis.stringCount;
      numberCount += analysis.numberCount;
      boolCount += analysis.boolCount;
      arrayCount += analysis.arrayCount;
      objectCount += analysis.objectCount;
      nullCount += analysis.nullCount;
      
      if (analysis.maxDepth > maxDepth) {
        maxDepth = analysis.maxDepth;
      }
    }

    return StatisticsResult(
      totalElements: arr.length,
      stringCount: stringCount,
      numberCount: numberCount,
      boolCount: boolCount,
      arrayCount: arrayCount + 1,
      objectCount: objectCount,
      nullCount: nullCount,
      nestedDepth: maxDepth,
    );
  }

  static ValueAnalysis _analyzeValue(dynamic value, int depth) {
    if (value is String) {
      return ValueAnalysis(1, 0, 0, 0, 0, 0, depth);
    } else if (value is num) {
      return ValueAnalysis(0, 1, 0, 0, 0, 0, depth);
    } else if (value is bool) {
      return ValueAnalysis(0, 0, 1, 0, 0, 0, depth);
    } else if (value == null) {
      return ValueAnalysis(0, 0, 0, 0, 0, 1, depth);
    } else if (value is List) {
      int stringCount = 0;
      int numberCount = 0;
      int boolCount = 0;
      int arrayCount = 1;
      int objectCount = 0;
      int nullCount = 0;
      int maxDepth = depth;

      for (final dynamic item in value) {
        final ValueAnalysis analysis = _analyzeValue(item, depth + 1);
        stringCount += analysis.stringCount;
        numberCount += analysis.numberCount;
        boolCount += analysis.boolCount;
        arrayCount += analysis.arrayCount;
        objectCount += analysis.objectCount;
        nullCount += analysis.nullCount;
        
        if (analysis.maxDepth > maxDepth) {
          maxDepth = analysis.maxDepth;
        }
      }

      return ValueAnalysis(stringCount, numberCount, boolCount, arrayCount, objectCount, nullCount, maxDepth);
    } else if (value is Map) {
      int stringCount = 0;
      int numberCount = 0;
      int boolCount = 0;
      int arrayCount = 0;
      int objectCount = 1;
      int nullCount = 0;
      int maxDepth = depth;

      for (final dynamic item in value.values) {
        final ValueAnalysis analysis = _analyzeValue(item, depth + 1);
        stringCount += analysis.stringCount;
        numberCount += analysis.numberCount;
        boolCount += analysis.boolCount;
        arrayCount += analysis.arrayCount;
        objectCount += analysis.objectCount;
        nullCount += analysis.nullCount;
        
        if (analysis.maxDepth > maxDepth) {
          maxDepth = analysis.maxDepth;
        }
      }

      return ValueAnalysis(stringCount, numberCount, boolCount, arrayCount, objectCount, nullCount, maxDepth);
    }

    return ValueAnalysis(0, 0, 0, 0, 0, 0, depth);
  }
}

class ValueAnalysis {
  final int stringCount;
  final int numberCount;
  final int boolCount;
  final int arrayCount;
  final int objectCount;
  final int nullCount;
  final int maxDepth;

  ValueAnalysis(
    this.stringCount,
    this.numberCount,
    this.boolCount,
    this.arrayCount,
    this.objectCount,
    this.nullCount,
    this.maxDepth,
  );
}

class StatisticsResult {
  final int totalElements;
  final int stringCount;
  final int numberCount;
  final int boolCount;
  final int arrayCount;
  final int objectCount;
  final int nullCount;
  final int nestedDepth;

  StatisticsResult({
    required this.totalElements,
    required this.stringCount,
    required this.numberCount,
    required this.boolCount,
    required this.arrayCount,
    required this.objectCount,
    required this.nullCount,
    required this.nestedDepth,
  });

  
  String toString() {
    return '''
✅ 统计结果:
- 顶层元素数量: $totalElements
- 字符串字段数: $stringCount
- 数字字段数: $numberCount
- 布尔字段数: $boolCount
- 数组字段数: $arrayCount
- 对象字段数: $objectCount
- 空值字段数: $nullCount
- 最大嵌套深度: $nestedDepth''';
  }
}

使用示例

void main() {
  final String complexJson = '''
  {
    "user": {
      "name": "张三",
      "age": 25,
      "isStudent": true,
      "scores": [95, 88, 92],
      "address": {
        "city": "北京",
        "district": "朝阳区"
      }
    },
    "projects": [
      {"name": "App1", "stars": 100},
      {"name": "App2", "stars": 200}
    ]
  }''';

  final StatisticsResult result = JsonStatistics.analyze(complexJson);
  print(result);
}

练习任务三:字段提取

任务描述

实现一个JSON字段提取工具,能够递归遍历JSON结构,提取所有字段的完整路径。

实现方案

import 'dart:convert';

class JsonFieldExtractor {
  static List<String> extractFieldPaths(String jsonString) {
    final List<String> paths = [];
    final dynamic parsed = json.decode(jsonString);
    
    if (parsed is Map<String, dynamic>) {
      _extractFromObject(parsed, '', paths);
    } else if (parsed is List) {
      _extractFromArray(parsed, '', paths);
    }
    
    return paths;
  }

  static void _extractFromObject(Map<String, dynamic> obj, String prefix, List<String> paths) {
    for (final String key in obj.keys) {
      final String currentPath = prefix.isEmpty ? key : '$prefix.$key';
      paths.add(currentPath);
      
      final dynamic value = obj[key];
      if (value is Map<String, dynamic>) {
        _extractFromObject(value, currentPath, paths);
      } else if (value is List) {
        _extractFromArray(value, currentPath, paths);
      }
    }
  }

  static void _extractFromArray(List<dynamic> arr, String prefix, List<String> paths) {
    for (int i = 0; i < arr.length; i++) {
      final String currentPath = '$prefix[$i]';
      
      final dynamic value = arr[i];
      if (value is Map<String, dynamic>) {
        _extractFromObject(value, currentPath, paths);
      } else if (value is List) {
        _extractFromArray(value, currentPath, paths);
      } else {
        paths.add(currentPath);
      }
    }
  }

  static Map<String, dynamic> extractFieldsByType(String jsonString, Type type) {
    final Map<String, dynamic> result = {};
    final dynamic parsed = json.decode(jsonString);
    
    if (parsed is Map<String, dynamic>) {
      _extractFieldsByTypeFromObject(parsed, '', type, result);
    } else if (parsed is List) {
      _extractFieldsByTypeFromArray(parsed, '', type, result);
    }
    
    return result;
  }

  static void _extractFieldsByTypeFromObject(
    Map<String, dynamic> obj,
    String prefix,
    Type type,
    Map<String, dynamic> result,
  ) {
    for (final String key in obj.keys) {
      final String currentPath = prefix.isEmpty ? key : '$prefix.$key';
      final dynamic value = obj[key];
      
      if (value.runtimeType == type) {
        result[currentPath] = value;
      } else if (value is Map<String, dynamic>) {
        _extractFieldsByTypeFromObject(value, currentPath, type, result);
      } else if (value is List) {
        _extractFieldsByTypeFromArray(value, currentPath, type, result);
      }
    }
  }

  static void _extractFieldsByTypeFromArray(
    List<dynamic> arr,
    String prefix,
    Type type,
    Map<String, dynamic> result,
  ) {
    for (int i = 0; i < arr.length; i++) {
      final String currentPath = '$prefix[$i]';
      final dynamic value = arr[i];
      
      if (value.runtimeType == type) {
        result[currentPath] = value;
      } else if (value is Map<String, dynamic>) {
        _extractFieldsByTypeFromObject(value, currentPath, type, result);
      } else if (value is List) {
        _extractFieldsByTypeFromArray(value, currentPath, type, result);
      }
    }
  }
}

使用示例

void main() {
  final String jsonString = '''
  {
    "user": {
      "name": "张三",
      "profile": {
        "city": "北京",
        "hobbies": ["编程", "阅读"]
      }
    },
    "projects": [
      {"name": "App1", "stars": 100},
      {"name": "App2", "stars": 200}
    ]
  }''';

  // 提取所有字段路径
  final List<String> allPaths = JsonFieldExtractor.extractFieldPaths(jsonString);
  print('所有字段路径:');
  for (final String path in allPaths) {
    print('• $path');
  }

  // 提取所有字符串字段
  final Map<String, dynamic> stringFields = JsonFieldExtractor.extractFieldsByType(jsonString, String);
  print('\n所有字符串字段:');
  for (final MapEntry<String, dynamic> entry in stringFields.entries) {
    print('${entry.key}: "${entry.value}"');
  }
}

练习任务四:数据转换

任务描述

实现一个JSON数据转换工具,能够将复杂的JSON结构转换为扁平化的键值对形式。

实现方案

import 'dart:convert';

class JsonTransformer {
  static Map<String, dynamic> flatten(String jsonString) {
    final Map<String, dynamic> result = {};
    final dynamic parsed = json.decode(jsonString);
    
    if (parsed is Map<String, dynamic>) {
      _flattenObject(parsed, '', result);
    } else if (parsed is List) {
      _flattenArray(parsed, '', result);
    } else {
      result[''] = parsed;
    }
    
    return result;
  }

  static void _flattenObject(Map<String, dynamic> obj, String prefix, Map<String, dynamic> result) {
    for (final String key in obj.keys) {
      final String currentKey = prefix.isEmpty ? key : '$prefix.$key';
      final dynamic value = obj[key];
      
      if (value is Map<String, dynamic>) {
        _flattenObject(value, currentKey, result);
      } else if (value is List) {
        _flattenArray(value, currentKey, result);
      } else {
        result[currentKey] = value;
      }
    }
  }

  static void _flattenArray(List<dynamic> arr, String prefix, Map<String, dynamic> result) {
    for (int i = 0; i < arr.length; i++) {
      final String currentKey = '$prefix[$i]';
      final dynamic value = arr[i];
      
      if (value is Map<String, dynamic>) {
        _flattenObject(value, currentKey, result);
      } else if (value is List) {
        _flattenArray(value, currentKey, result);
      } else {
        result[currentKey] = value;
      }
    }
  }

  static Map<String, String> extractAllStrings(String jsonString) {
    final Map<String, String> result = {};
    final Map<String, dynamic> flattened = flatten(jsonString);
    
    for (final String key in flattened.keys) {
      if (flattened[key] is String) {
        result[key] = flattened[key] as String;
      }
    }
    
    return result;
  }

  static Map<String, num> extractAllNumbers(String jsonString) {
    final Map<String, num> result = {};
    final Map<String, dynamic> flattened = flatten(jsonString);
    
    for (final String key in flattened.keys) {
      if (flattened[key] is num) {
        result[key] = flattened[key] as num;
      }
    }
    
    return result;
  }

  static List<Map<String, dynamic>> convertToListOfMaps(String jsonString) {
    final dynamic parsed = json.decode(jsonString);
    
    if (parsed is List) {
      return parsed.where((item) => item is Map).map((item) => item as Map<String, dynamic>).toList();
    } else if (parsed is Map) {
      return [parsed as Map<String, dynamic>];
    } else {
      return [];
    }
  }
}

使用示例

void main() {
  final String nestedJson = '''
  {
    "data": {
      "items": [
        {"id": 1, "name": "Item1", "details": {"price": 100, "stock": 50}},
        {"id": 2, "name": "Item2", "details": {"price": 200, "stock": 30}}
      ],
      "pagination": {"page": 1, "total": 100}
    }
  }''';

  // 扁平化JSON
  final Map<String, dynamic> flattened = JsonTransformer.flatten(nestedJson);
  print('扁平化结果:');
  for (final MapEntry<String, dynamic> entry in flattened.entries) {
    print('${entry.key}: ${entry.value}');
  }

  // 提取所有字符串
  final Map<String, String> strings = JsonTransformer.extractAllStrings(nestedJson);
  print('\n所有字符串:');
  for (final MapEntry<String, String> entry in strings.entries) {
    print('${entry.key}: "${entry.value}"');
  }

  // 提取所有数字
  final Map<String, num> numbers = JsonTransformer.extractAllNumbers(nestedJson);
  print('\n所有数字:');
  for (final MapEntry<String, num> entry in numbers.entries) {
    print('${entry.key}: ${entry.value}');
  }
}

综合案例:API响应处理

场景描述

在实际开发中,我们经常需要处理来自API的JSON响应。这个案例将展示如何综合运用前面学到的知识来处理一个完整的API响应。

实现方案

import 'dart:convert';

class ApiResponseHandler {
  static ApiResult<T> parseResponse<T>(
    String responseBody,
    T Function(Map<String, dynamic>) fromJson,
  ) {
    try {
      final Map<String, dynamic> response = json.decode(responseBody) as Map<String, dynamic>;

      // 验证响应格式
      if (!response.containsKey('data')) {
        return ApiResult.error('缺少data字段');
      }

      // 检查状态码
      final int? statusCode = response['status'] as int?;
      if (statusCode != null && statusCode >= 400) {
        final String? message = response['message'] as String?;
        return ApiResult.error(message ?? '请求失败');
      }

      // 提取数据
      final dynamic data = response['data'];
      
      if (data is List) {
        final List<T> items = data.map((item) => fromJson(item as Map<String, dynamic>)).toList();
        return ApiResult.success(items);
      } else if (data is Map) {
        final T item = fromJson(data as Map<String, dynamic>);
        return ApiResult.success(item);
      } else {
        return ApiResult.error('数据格式不正确');
      }
    } on FormatException catch (e) {
      return ApiResult.error('JSON格式错误: $e');
    } catch (e) {
      return ApiResult.error('解析失败: $e');
    }
  }

  static ApiResult<List<T>> parseListResponse<T>(
    String responseBody,
    T Function(Map<String, dynamic>) fromJson,
  ) {
    try {
      final Map<String, dynamic> response = json.decode(responseBody) as Map<String, dynamic>;

      if (!response.containsKey('data')) {
        return ApiResult.error('缺少data字段');
      }

      final dynamic data = response['data'];
      
      if (data is Map && data.containsKey('items')) {
        final dynamic items = data['items'];
        
        if (items is List) {
          final List<T> result = items.map((item) => fromJson(item as Map<String, dynamic>)).toList();
          
          // 提取分页信息
          final Pagination? pagination;
          if (data.containsKey('pagination')) {
            pagination = Pagination.fromJson(data['pagination'] as Map<String, dynamic>);
          } else {
            pagination = null;
          }
          
          return ApiResult.success(result, pagination: pagination);
        }
      } else if (data is List) {
        final List<T> result = data.map((item) => fromJson(item as Map<String, dynamic>)).toList();
        return ApiResult.success(result);
      }

      return ApiResult.error('数据格式不正确');
    } catch (e) {
      return ApiResult.error('解析失败: $e');
    }
  }
}

class ApiResult<T> {
  final bool success;
  final T? data;
  final String? error;
  final Pagination? pagination;

  ApiResult._({
    required this.success,
    this.data,
    this.error,
    this.pagination,
  });

  factory ApiResult.success(T data, {Pagination? pagination}) {
    return ApiResult._(
      success: true,
      data: data,
      pagination: pagination,
    );
  }

  factory ApiResult.error(String error) {
    return ApiResult._(
      success: false,
      error: error,
    );
  }
}

class Pagination {
  final int page;
  final int total;
  final int? pageSize;
  final int? totalPages;

  Pagination({
    required this.page,
    required this.total,
    this.pageSize,
    this.totalPages,
  });

  factory Pagination.fromJson(Map<String, dynamic> json) {
    return Pagination(
      page: json['page'] as int,
      total: json['total'] as int,
      pageSize: json['pageSize'] as int?,
      totalPages: json['totalPages'] as int?,
    );
  }
}

class Product {
  final int id;
  final String name;
  final double price;
  final int stock;

  Product({
    required this.id,
    required this.name,
    required this.price,
    required this.stock,
  });

  factory Product.fromJson(Map<String, dynamic> json) {
    return Product(
      id: json['id'] as int,
      name: json['name'] as String,
      price: (json['price'] as num).toDouble(),
      stock: json['stock'] as int,
    );
  }
}

使用示例

void main() {
  // 模拟API响应
  final String apiResponse = '''
  {
    "status": 200,
    "message": "success",
    "data": {
      "items": [
        {"id": 1, "name": "iPhone 15", "price": 5999, "stock": 100},
        {"id": 2, "name": "MacBook Pro", "price": 12999, "stock": 50},
        {"id": 3, "name": "iPad Air", "price": 4799, "stock": 80}
      ],
      "pagination": {
        "page": 1,
        "pageSize": 10,
        "total": 100,
        "totalPages": 10
      }
    }
  }''';

  // 解析响应
  final ApiResult<List<Product>> result = ApiResponseHandler.parseListResponse(
    apiResponse,
    Product.fromJson,
  );

  if (result.success && result.data != null) {
    print('✅ 解析成功!');
    print('产品数量: ${result.data!.length}');
    
    for (final Product product in result.data!) {
      print('• ${product.name}: ¥${product.price} (库存: ${product.stock})');
    }

    if (result.pagination != null) {
      print('\n分页信息:');
      print('当前页: ${result.pagination!.page}');
      print('总记录: ${result.pagination!.total}');
      print('总页数: ${result.pagination!.totalPages}');
    }
  } else {
    print('❌ 解析失败: ${result.error}');
  }
}

综合案例:数据清洗与转换

场景描述

在数据处理过程中,我们经常需要对原始JSON数据进行清洗和转换,去除不需要的字段,重命名字段,或者转换数据格式。

实现方案

import 'dart:convert';

class DataCleaner {
  static Map<String, dynamic> clean(
    Map<String, dynamic> data, {
    List<String>? removeFields,
    Map<String, String>? renameFields,
    Map<String, dynamic Function(dynamic)>? transformFields,
  }) {
    final Map<String, dynamic> result = {};

    for (final String key in data.keys) {
      // 移除指定字段
      if (removeFields != null && removeFields.contains(key)) {
        continue;
      }

      // 重命名字段
      final String newKey = renameFields != null && renameFields.containsKey(key)
          ? renameFields[key]!
          : key;

      dynamic value = data[key];

      // 转换字段值
      if (transformFields != null && transformFields.containsKey(key)) {
        try {
          value = transformFields[key]!(value);
        } catch (e) {
          print('字段转换失败: $key - $e');
        }
      }

      // 递归处理嵌套对象
      if (value is Map<String, dynamic>) {
        value = clean(value,
          removeFields: removeFields,
          renameFields: renameFields,
          transformFields: transformFields,
        );
      }

      // 递归处理数组
      if (value is List) {
        value = _cleanArray(value,
          removeFields: removeFields,
          renameFields: renameFields,
          transformFields: transformFields,
        );
      }

      result[newKey] = value;
    }

    return result;
  }

  static List<dynamic> _cleanArray(
    List<dynamic> arr, {
    List<String>? removeFields,
    Map<String, String>? renameFields,
    Map<String, dynamic Function(dynamic)>? transformFields,
  }) {
    final List<dynamic> result = [];

    for (final dynamic item in arr) {
      if (item is Map<String, dynamic>) {
        result.add(clean(item,
          removeFields: removeFields,
          renameFields: renameFields,
          transformFields: transformFields,
        ));
      } else if (item is List) {
        result.add(_cleanArray(item,
          removeFields: removeFields,
          renameFields: renameFields,
          transformFields: transformFields,
        ));
      } else {
        result.add(item);
      }
    }

    return result;
  }

  static Map<String, dynamic> filterByType(Map<String, dynamic> data, Type type) {
    final Map<String, dynamic> result = {};

    for (final String key in data.keys) {
      if (data[key].runtimeType == type) {
        result[key] = data[key];
      } else if (data[key] is Map<String, dynamic>) {
        final Map<String, dynamic> filtered = filterByType(data[key] as Map<String, dynamic>, type);
        if (filtered.isNotEmpty) {
          result[key] = filtered;
        }
      }
    }

    return result;
  }

  static Map<String, dynamic> merge(Map<String, dynamic> base, Map<String, dynamic> updates) {
    final Map<String, dynamic> result = Map.from(base);

    for (final String key in updates.keys) {
      if (base.containsKey(key) && base[key] is Map && updates[key] is Map) {
        result[key] = merge(base[key] as Map<String, dynamic>, updates[key] as Map<String, dynamic>);
      } else if (base.containsKey(key) && base[key] is List && updates[key] is List) {
        result[key] = [...base[key] as List, ...updates[key] as List];
      } else {
        result[key] = updates[key];
      }
    }

    return result;
  }
}

使用示例

void main() {
  // 原始数据
  final Map<String, dynamic> rawData = {
    'id': 1,
    'user_name': 'zhangsan',
    'user_email': 'zhang@example.com',
    'age': 25,
    'created_at': '2024-01-15T10:30:00Z',
    'password': '******',
    'meta': {
      'version': 1,
      'tags': ['admin', 'active'],
      'internal_id': 'INT-001',
    },
    'profile': {
      'full_name': '张三',
      'address': '北京市朝阳区',
      'phone': '13800138000',
    },
  };

  // 数据清洗配置
  final cleaned = DataCleaner.clean(
    rawData,
    removeFields: ['password', 'internal_id'],
    renameFields: {
      'user_name': 'username',
      'user_email': 'email',
      'created_at': 'createdAt',
      'full_name': 'fullName',
    },
    transformFields: {
      'created_at': (value) => DateTime.parse(value as String),
      'age': (value) => (value as int).toString(),
    },
  );

  print('清洗后的数据:');
  print(json.encode(cleaned));

  // 合并更新
  final Map<String, dynamic> updates = {
    'age': 26,
    'profile': {
      'phone': '13900139000',
    },
  };

  final Map<String, dynamic> merged = DataCleaner.merge(cleaned, updates);
  print('\n合并后的数据:');
  print(json.encode(merged));
}

鸿蒙平台综合实践

场景描述

在鸿蒙平台上开发一个完整的JSON数据处理应用,包含数据验证、统计、提取和转换功能。

实现方案

import 'dart:convert';
import 'package:flutter/material.dart';

class JsonPracticeApp extends StatelessWidget {
  const JsonPracticeApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'JSON综合练习',
      theme: ThemeData(primaryColor: const Color(0xFF00CEC9)),
      home: const JsonPracticeHome(),
    );
  }
}

class JsonPracticeHome extends StatefulWidget {
  const JsonPracticeHome({super.key});

  
  State<JsonPracticeHome> createState() => _JsonPracticeHomeState();
}

class _JsonPracticeHomeState extends State<JsonPracticeHome> {
  String _inputJson = '';
  String _result = '';
  bool _showResult = false;
  String _selectedTask = 'validate';

  void _executeTask() {
    setState(() {
      _showResult = false;
    });

    if (_inputJson.isEmpty) {
      setState(() {
        _result = '请输入JSON内容';
        _showResult = true;
      });
      return;
    }

    try {
      switch (_selectedTask) {
        case 'validate':
          _validateJson();
          break;
        case 'count':
          _countElements();
          break;
        case 'extract':
          _extractFields();
          break;
        case 'transform':
          _transformJson();
          break;
      }
    } catch (e) {
      setState(() {
        _result = '❌ 操作失败: $e';
        _showResult = true;
      });
    }
  }

  void _validateJson() {
    final dynamic parsed = json.decode(_inputJson);
    setState(() {
      if (parsed is Map) {
        _result = '✅ JSON验证通过!\n类型: Object\n键数量: ${parsed.length}';
      } else if (parsed is List) {
        _result = '✅ JSON验证通过!\n类型: Array\n元素数量: ${parsed.length}';
      } else {
        _result = '✅ JSON验证通过!\n值: $parsed';
      }
      _showResult = true;
    });
  }

  void _countElements() {
    final dynamic parsed = json.decode(_inputJson);
    
    if (parsed is Map<String, dynamic>) {
      final StatisticsResult stats = JsonStatistics.analyze(_inputJson);
      setState(() {
        _result = stats.toString();
        _showResult = true;
      });
    } else if (parsed is List) {
      setState(() {
        _result = '''
✅ 数组统计:
- 元素总数: ${parsed.length}
- 字符串元素: ${parsed.where((v) => v is String).length}
- 数字元素: ${parsed.where((v) => v is num).length}
- 布尔元素: ${parsed.where((v) => v is bool).length}
- 对象元素: ${parsed.where((v) => v is Map).length}''';
        _showResult = true;
      });
    }
  }

  void _extractFields() {
    final List<String> paths = JsonFieldExtractor.extractFieldPaths(_inputJson);
    setState(() {
      _result = '✅ 提取的字段路径:\n${paths.map((k) => '• $k').join('\n')}';
      _showResult = true;
    });
  }

  void _transformJson() {
    final Map<String, String> strings = JsonTransformer.extractAllStrings(_inputJson);
    setState(() {
      _result = '✅ 提取的字符串值:\n${strings.entries.map((e) => '${e.key}: "${e.value}"').join('\n')}';
      _showResult = true;
    });
  }

  void _loadExample(String type) {
    setState(() {
      switch (type) {
        case 'simple':
          _inputJson = '{"name":"张三","age":25,"isStudent":true,"scores":[95,88,92]}';
          break;
        case 'nested':
          _inputJson = '{"user":{"name":"张三","profile":{"city":"北京","hobbies":["编程","阅读"]}},"projects":[{"name":"App1","stars":100},{"name":"App2","stars":200}]}';
          break;
        case 'array':
          _inputJson = '["苹果","香蕉","橙子","葡萄"]';
          break;
        case 'complex':
          _inputJson = '{"status":200,"data":{"items":[{"id":1,"name":"Item1","details":{"price":100,"stock":50}},{"id":2,"name":"Item2","details":{"price":200,"stock":30}}],"pagination":{"page":1,"total":100}}}';
          break;
      }
    });
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('JSON综合练习'),
        backgroundColor: const Color(0xFF00CEC9),
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            _buildTaskSelector(),
            _buildInputArea(),
            _buildExampleButtons(),
            _buildExecuteButton(),
            if (_showResult) _buildResultCard(),
            _buildCaseStudy(),
          ],
        ),
      ),
    );
  }

  Widget _buildTaskSelector() {
    final tasks = [
      {'value': 'validate', 'label': '验证JSON', 'desc': '检查JSON格式是否正确'},
      {'value': 'count', 'label': '统计元素', 'desc': '统计JSON中的各种元素数量'},
      {'value': 'extract', 'label': '提取字段', 'desc': '提取所有字段路径'},
      {'value': 'transform', 'label': '转换数据', 'desc': '提取所有字符串值'},
    ];

    return Card(
      elevation: 4,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            const Text('选择练习任务', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
            const SizedBox(height: 12),
            ...tasks.map((task) => RadioListTile(
              title: Text(task['label']!),
              subtitle: Text(task['desc']!),
              value: task['value'],
              groupValue: _selectedTask,
              onChanged: (value) => setState(() => _selectedTask = value!),
              activeColor: const Color(0xFF00CEC9),
            )),
          ],
        ),
      ),
    );
  }

  Widget _buildInputArea() {
    return Card(
      elevation: 4,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const Text('输入JSON:', style: TextStyle(fontWeight: FontWeight.bold)),
            const SizedBox(height: 12),
            Container(
              height: 150,
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(8),
                color: const Color(0xFF1E1E1E),
                border: Border.all(color: Colors.grey),
              ),
              child: TextField(
                onChanged: (value) => _inputJson = value,
                maxLines: null,
                expands: true,
                decoration: const InputDecoration(
                  hintText: '在此输入JSON内容...',
                  border: InputBorder.none,
                  contentPadding: EdgeInsets.all(12),
                ),
                style: const TextStyle(fontFamily: 'Monaco', fontSize: 13, color: Colors.green),
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildExampleButtons() {
    final examples = [
      {'label': '简单对象', 'type': 'simple', 'color': Colors.green},
      {'label': '嵌套对象', 'type': 'nested', 'color': Colors.blue},
      {'label': '数组', 'type': 'array', 'color': Colors.orange},
      {'label': '复杂结构', 'type': 'complex', 'color': Colors.purple},
    ];

    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 12),
      child: Wrap(
        spacing: 8,
        children: examples.map((example) {
          final String type = example['type'] as String;
          return ElevatedButton(
            onPressed: () => _loadExample(type),
            style: ElevatedButton.styleFrom(
              backgroundColor: example['color'] as Color,
              padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
              shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
            ),
            child: Text(example['label']!, style: const TextStyle(fontSize: 13)),
          );
        }).toList(),
      ),
    );
  }

  Widget _buildExecuteButton() {
    return Center(
      child: ElevatedButton(
        onPressed: _executeTask,
        style: ElevatedButton.styleFrom(
          backgroundColor: const Color(0xFF00CEC9),
          padding: const EdgeInsets.symmetric(horizontal: 48, vertical: 16),
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
        ),
        child: const Text('执行任务', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
      ),
    );
  }

  Widget _buildResultCard() {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 16),
      child: Card(
        elevation: 4,
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
        color: _result.contains('❌') ? const Color(0xFFFFEBEE) : const Color(0xFFE8F5E9),
        child: Padding(
          padding: const EdgeInsets.all(16),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                _result.contains('❌') ? '❌ 执行结果' : '✅ 执行结果',
                style: TextStyle(
                  fontWeight: FontWeight.bold,
                  fontSize: 16,
                  color: _result.contains('❌') ? Colors.red : Colors.green,
                ),
              ),
              const SizedBox(height: 12),
              Text(_result),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildCaseStudy() {
    return Card(
      elevation: 4,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const Text('综合案例: API响应处理', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
            const SizedBox(height: 12),
            Container(
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(8),
                color: const Color(0xFFF5F5F5),
              ),
              padding: const EdgeInsets.all(12),
              child: const Text(
                '''// 综合应用场景:处理API响应

// 1. 验证响应格式
if (!response.containsKey('data')) {
  throw Exception('缺少data字段');
}

// 2. 提取列表数据
List<Product> products = (response['data']['items'] as List)
    .map((item) => Product.fromJson(item))
    .toList();

// 3. 获取分页信息
Pagination pagination = Pagination.fromJson(
    response['data']['pagination']);

// 4. 处理可选字段
String? nextPage = response['data']['next_page'] as String?;

// 5. 格式化输出日志
print(JsonEncoder.withIndent('  ').convert(response));''',
                style: TextStyle(fontFamily: 'Monaco', fontSize: 12),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

总结

通过本章的综合练习,我们将前面所学的JSON解析知识融会贯通,实现了以下功能:

  1. JSON验证:检查JSON格式是否正确,识别JSON类型
  2. 数据统计:统计JSON中各种元素的数量和嵌套深度
  3. 字段提取:递归遍历JSON结构,提取所有字段路径
  4. 数据转换:扁平化JSON结构,提取特定类型的数据
  5. API响应处理:完整的API响应解析和错误处理
  6. 数据清洗:字段移除、重命名和值转换

这些功能在实际开发中非常实用,掌握它们可以帮助我们更高效地处理JSON数据。在鸿蒙平台上开发时,还需要注意性能优化和资源限制,确保应用在各种设备上都能流畅运行。

通过不断练习和实践,相信你已经成为JSON处理的高手!

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐