Konsistenzfehler im JSON Vermeiden #125

Merged
flixcoo merged 12 commits from enhancement/70-konsistenzfehler-im-json-vermeiden into development 2026-01-07 11:27:44 +00:00
Showing only changes of commit 4a67dae456 - Show all commits

View File

@@ -31,9 +31,25 @@ class DataTransferService {
// Construct a JSON representation of the data // Construct a JSON representation of the data
final Map<String, dynamic> jsonMap = { final Map<String, dynamic> jsonMap = {
'matches': matches.map((match) => match.toJson()).toList(), 'players': players.map((p) => p.toJson()).toList(),
'groups': groups.map((group) => group.toJson()).toList(),
'players': players.map((player) => player.toJson()).toList(), 'groups': groups
.map((g) => {
'id': g.id,
'name': g.name,
'createdAt': g.createdAt.toIso8601String(),
'memberIds': (g.members ?? []).map((m) => m.id).toList(),
}).toList(),
'matches': matches
.map((m) => {
'id': m.id,
'name': m.name,
'createdAt': m.createdAt.toIso8601String(),
'groupId': m.group?.id,
'playerIds': (m.players ?? []).map((p) => p.id).toList(),
'winnerId': m.winner?.id,
}).toList(),
}; };
return json.encode(jsonMap); return json.encode(jsonMap);
@@ -46,7 +62,7 @@ class DataTransferService {
/// [fileName] The desired name for the exported file (without extension). /// [fileName] The desired name for the exported file (without extension).
static Future<ExportResult> exportData( static Future<ExportResult> exportData(
String jsonString, String jsonString,
String fileName, String fileName
) async { ) async {
try { try {
final bytes = Uint8List.fromList(utf8.encode(jsonString)); final bytes = Uint8List.fromList(utf8.encode(jsonString));
@@ -54,11 +70,13 @@ class DataTransferService {
fileName: '$fileName.json', fileName: '$fileName.json',
bytes: bytes, bytes: bytes,
); );
if (path == null) { if (path == null) {
return ExportResult.canceled; return ExportResult.canceled;
} else { } else {
return ExportResult.success; return ExportResult.success;
} }
} catch (e, stack) { } catch (e, stack) {
print('[exportData] $e'); print('[exportData] $e');
print(stack); print(stack);
@@ -81,42 +99,78 @@ class DataTransferService {
try { try {
final jsonString = await _readFileContent(path.files.single); final jsonString = await _readFileContent(path.files.single);
if (jsonString == null) { if (jsonString == null) return ImportResult.fileReadError;
return ImportResult.fileReadError;
}
if (await _validateJsonSchema(jsonString)) { final isValid = await _validateJsonSchema(jsonString);
final Map<String, dynamic> jsonData = if (!isValid) return ImportResult.invalidSchema;
json.decode(jsonString) as Map<String, dynamic>;
final List<dynamic>? matchesJson = final dynamic decoded = json.decode(jsonString);
jsonData['matches'] as List<dynamic>?; if (decoded is! Map<String, dynamic>) return ImportResult.invalidSchema;
gelbeinhalb marked this conversation as resolved
Review

Macht das sinn hier? Ist nicht durch die schema Validierung das schon sichergestellt?

Macht das sinn hier? Ist nicht durch die schema Validierung das schon sichergestellt?
Review

Ja an sich schon, das würde nur greifen, wenn die schema Validierung einen fehler macht.

Ja an sich schon, das würde nur greifen, wenn die schema Validierung einen fehler macht.
final List<dynamic>? groupsJson = jsonData['groups'] as List<dynamic>?;
final List<dynamic>? playersJson =
jsonData['players'] as List<dynamic>?;
final List<Match> importedMatches = final List<dynamic> playersJson = (decoded['players'] as List<dynamic>?) ?? [];
matchesJson final List<dynamic> groupsJson = (decoded['groups'] as List<dynamic>?) ?? [];
?.map((g) => Match.fromJson(g as Map<String, dynamic>)) final List<dynamic> matchesJson = (decoded['matches'] as List<dynamic>?) ?? [];
.toList() ??
[]; // Players
final List<Group> importedGroups = final List<Player> importedPlayers = playersJson
groupsJson .map((p) => Player.fromJson(p as Map<String, dynamic>))
?.map((g) => Group.fromJson(g as Map<String, dynamic>)) .toList();
.toList() ??
[]; final Map<String, Player> playerById = {
final List<Player> importedPlayers = for (final p in importedPlayers) p.id: p,
playersJson };
?.map((p) => Player.fromJson(p as Map<String, dynamic>))
.toList() ?? // Groups
[]; final List<Group> importedGroups = groupsJson.map((g) {
final map = g as Map<String, dynamic>;
final memberIds = (map['memberIds'] as List<dynamic>? ?? []).cast<String>();
final members = memberIds
.map((id) => playerById[id])
.whereType<Player>()
.toList();
return Group(
id: map['id'] as String,
name: map['name'] as String,
members: members,
createdAt: DateTime.parse(map['createdAt'] as String),
);
}).toList();
final Map<String, Group> groupById = {
for (final g in importedGroups) g.id: g,
};
// Matches
final List<Match> importedMatches = matchesJson.map((m) {
final map = m as Map<String, dynamic>;
final String? groupId = map['groupId'] as String?;
final List<String> playerIds = (map['playerIds'] as List<dynamic>? ?? []).cast<String>();
final String? winnerId = map['winnerId'] as String?;
final group = (groupId == null) ? null : groupById[groupId];
final players = playerIds
.map((id) => playerById[id])
.whereType<Player>()
.toList();
final winner = (winnerId == null) ? null : playerById[winnerId];
return Match(
id: map['id'] as String,
name: map['name'] as String,
group: group,
players: players,
createdAt: DateTime.parse(map['createdAt'] as String),
winner: winner,
);
}).toList();
await db.playerDao.addPlayersAsList(players: importedPlayers); await db.playerDao.addPlayersAsList(players: importedPlayers);
await db.groupDao.addGroupsAsList(groups: importedGroups); await db.groupDao.addGroupsAsList(groups: importedGroups);
await db.matchDao.addMatchAsList(matches: importedMatches); await db.matchDao.addMatchAsList(matches: importedMatches);
} else {
return ImportResult.invalidSchema;
}
return ImportResult.success; return ImportResult.success;
} on FormatException catch (e, stack) { } on FormatException catch (e, stack) {
print('[importData] FormatException'); print('[importData] FormatException');