{"record":{"id":"e06a2ff2d9c65643","repo":"babalae/better-genshin-impact","slug":"notes","errorCode":null,"errorMessage":"曲谱缺少 notes 字段","messagePattern":"曲谱缺少 notes 字段","errorType":"exception","errorClass":"FormatException","httpStatus":null,"severity":"warning","filePath":"BetterGenshinImpact/GameTask/Music/Service/MusicScoreParser.cs","lineNumber":63,"sourceCode":"        }\n        catch (Exception e)\n        {\n            return CreateInvalidScore(path, rootFolder, e.Message);\n        }\n    }\n\n    private static async Task<PerformanceScore> ParseJsonAsync(\n        string path,\n        string rootFolder,\n        CancellationToken cancellationToken)\n    {\n        var text = await File.ReadAllTextAsync(path, cancellationToken);\n        var json = JObject.Parse(text);\n        var type = GetString(json, \"type\", \"yuanqin\").Trim().ToLowerInvariant();\n        var bpm = GetDouble(json, \"bpm\", 120);\n        var ticks = Math.Max(1, (int)Math.Round(GetDouble(json, \"ticks\", 480)));\n        var timeSignature = GetString(json, \"time_signature\", \"4/4\");\n        var notes = json[\"notes\"] ?? throw new FormatException(\"曲谱缺少 notes 字段\");\n\n        var format = type switch\n        {\n            \"yuanqin\" => MusicScoreFormat.YuanQin,\n            \"midi\" => MusicScoreFormat.MidiJson,\n            \"keyboard\" => MusicScoreFormat.Keyboard,\n            _ => throw new FormatException($\"不支持的 AutoYuanQin 曲谱类型：{type}\")\n        };\n\n        var timeline = format switch\n        {\n            MusicScoreFormat.YuanQin => ParseYuanQinTimeline(notes, bpm, timeSignature),\n            MusicScoreFormat.MidiJson => ParseMidiJsonTimeline(GetNotesString(notes, format), bpm, ticks),\n            MusicScoreFormat.Keyboard => ParseKeyboardTimeline(GetNotesString(notes, format), bpm),\n            _ => PerformanceTimeline.Empty\n        };\n\n        return new PerformanceScore","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/babalae/better-genshin-impact/blob/a7cb36712dcb409be610257d877fcea3597e9d6b/BetterGenshinImpact/GameTask/Music/Service/MusicScoreParser.cs#L45-L81","documentation":"Thrown by MusicScoreParser.ParseJsonAsync when the parsed JSON object does not contain a 'notes' property (json[\"notes\"] is null). The 'notes' field is mandatory for all supported score types (yuanqin, midi, keyboard). Note: at the outer ParseAsync level, this FormatException is caught and converted into an invalid PerformanceScore with an Error field, so it does not crash the application.","triggerScenarios":"Loading a .json score file that has valid JSON but lacks the top-level 'notes' key. The JObject.Parse succeeds, but json[\"notes\"] returns null.","commonSituations":"A user-edited or auto-generated score file omits the notes field; the file uses a different schema; a template/stub file was saved without content.","solutions":["Open the score JSON file and add a 'notes' field (string for yuanqin/keyboard, array for yuanqin object form, or string for midi format).","Validate the JSON schema before parsing — check for 'notes' presence with a JSON schema validator.","Refer to existing valid score files in the project for the correct schema.","Check the PerformanceScore.Error property after ParseAsync to surface the message to the user gracefully."],"exampleFix":"// before\nvar notes = json[\"notes\"] ?? throw new FormatException(\"曲谱缺少 notes 字段\");\n\n// after — include the file path for diagnosis\nvar notes = json[\"notes\"] ?? throw new FormatException(\n    $\"曲谱缺少 notes 字段，文件：{Path.GetFileName(path)}\");","handlingStrategy":"validation","validationCode":"var json = JObject.Parse(text);\nif (json[\"notes\"] == null)\n{\n    logger.LogError(\"曲谱缺少 notes 字段\");\n    return; // or return invalid score\n}","typeGuard":"static bool HasNotesField(JObject json) => json[\"notes\"] != null;","tryCatchPattern":"var score = await parser.ParseAsync(path, rootFolder, ct);\nif (!string.IsNullOrEmpty(score.Error)) { logger.LogWarning(\"曲谱解析失败：{Error}\", score.Error); }","preventionTips":["Validate score JSON files have a 'notes' field before loading.","Check PerformanceScore.Error after ParseAsync instead of catching exceptions.","Provide a template/example score file for users."],"tags":["music","json","format-exception","user-content"],"backgroundTag":null,"analyzedSha":"a7cb36712dcb409be610257d877fcea3597e9d6b","analyzedAt":"2026-08-13T16:44:57.548Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}