Flutter解析Django JSON时User类型转换错误及关联问题
问题根源分析
你遇到的第一个错误 Exception: type 'String' is not a subtype of type 'User' in type cast,核心原因是后端返回的author字段是用户UUID字符串,但你在Post.fromJSON中直接尝试将这个字符串强转为User类型,这显然不符合类型转换规则。而当你把author改为String类型后出现的序列化错误,是因为原代码的toJson逻辑没有同步适配,且你的需求是要访问用户的username、id等属性,必须保留User类型的author字段。
下面给出两种针对性的解决方案:
方案1:修改后端Django返回嵌套User数据(推荐)
如果可以调整后端逻辑,这是最简洁的解决方式。在Django的序列化器中配置author字段为嵌套序列化,让后端直接返回完整的用户对象:
后端Django调整示例
from rest_framework import serializers from .models import Article, User class UserSerializer(serializers.ModelSerializer): class Meta: model = User fields = ['id', 'email', 'username'] class ArticleSerializer(serializers.ModelSerializer): # 使用嵌套序列化器返回完整用户信息 author = UserSerializer() class Meta: model = Article fields = ['id', 'author', 'caption']
调整后后端返回的JSON会变为:
[ { "id": "3ab6e7c8-6a4c-42c2-a08d-e8a6c0fcdecb", "author": { "id": "dbb0446d-d0ec-4c24-9c1e-8ca9b6b06a7d", "email": "user@example.com", "username": "testuser" }, "caption": "caption here" } ]
前端Dart代码调整
只需要修改Post.fromJSON方法,用User.fromJSON解析嵌套的用户对象:
factory Post.fromJSON(Map<String, dynamic> jsonMap) { return Post( id: jsonMap['id'] as String, caption: jsonMap['caption'] as String, // 解析嵌套的User对象 author: User.fromJSON(jsonMap['author'] as Map<String, dynamic>), ); }
这样就能直接正常访问post.author.username等属性,且不会有类型错误。
方案2:后端无法修改,前端通过UUID获取用户信息
如果后端不能调整返回结构,就需要分两步处理:先解析帖子的基础信息(保存用户UUID),再通过UUID获取完整用户数据并填充到Post对象中。
步骤1:调整Post模型
新增authorId字段存储UUID,author字段改为可空类型后续填充:
class Post { final String id; final String authorId; // 存储后端返回的用户UUID User? author; // 后续填充的完整User对象 final String caption; Post({required this.id, required this.authorId, this.author, required this.caption}); factory Post.fromJSON(Map<String, dynamic> jsonMap) { return Post( id: jsonMap['id'] as String, caption: jsonMap['caption'] as String, authorId: jsonMap['author'] as String, // 读取UUID字符串 ); } // 适配序列化逻辑:有User对象则序列化对象,否则用UUID Map<String, dynamic> toJson() => { 'id': id, 'author': author?.toJson() ?? authorId, 'caption': caption, }; // 修正toString方法,输出有效调试信息 @override String toString() { return 'Post(id: $id, author: ${author?.toString()}, caption: $caption)'; } }
步骤2:修改帖子获取逻辑,批量获取用户信息
先获取帖子列表,再根据收集到的UUID批量请求用户详情,填充到Post对象中:
Future<List<Post>> fetchPosts(http.Client client) async { final response = await http.get(Uri.parse("$SERVER_IP/api/articles/?format=json")); List<Post> posts = compute(parsePosts, response.body); // 收集所有唯一的用户UUID Set<String> authorIds = posts.map((post) => post.authorId).toSet(); // 批量请求用户信息(需后端支持批量查询接口,比如/api/users/?ids=xxx,xxx) Map<String, User> userMap = await fetchUsersByIds(client, authorIds.toList()); // 为每个帖子填充User对象 return posts.map((post) { post.author = userMap[post.authorId]; return post; }).toList(); } // 批量获取用户的方法 Future<Map<String, User>> fetchUsersByIds(http.Client client, List<String> ids) async { String idsQuery = ids.join(','); final response = await http.get(Uri.parse("$SERVER_IP/api/users/?ids=$idsQuery&format=json")); List<dynamic> parsed = jsonDecode(response.body); return { for (var userJson in parsed) userJson['id'] as String: User.fromJSON(userJson as Map<String, dynamic>) }; } // 原解析方法保持不变,只处理基础帖子信息 List<Post> parsePosts(String responseBody) { final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>(); return parsed.map<Post>((json) => Post.fromJSON(json)).toList(); }
额外代码修正
同时需要修正User类的toString方法,输出有效信息:
@override String toString() { return 'User(id: $id, email: $email, username: $username)'; }
另外删除Post类中错误的String rawJson = jsonEncode(Map);代码,这行代码会导致编译错误。
内容的提问来源于stack exchange,提问作者user14132439

