[{"data":1,"prerenderedAt":3021},["ShallowReactive",2],{"content-query-7IQEJ2HxgL":3},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"tags":11,"rowTypeId":17,"sitemap":18,"body":19,"_type":3015,"_id":3016,"_source":3017,"_file":3018,"_stem":3019,"_extension":3020},"/articles/tech/blazor/messagepack-pim-format","blazor",false,"","MessagePackで独自バイナリファイル形式（PIM）をバージョン管理対応で設計する","音楽作成アプリPICOMで独自のバイナリファイル形式「PIM」をMessagePackで実装しました。なぜJSONでなくMessagePackを選んだのか、`[Key]`番号の運用ルール、将来のフォーマット進化に備えるVersionフィールド戦略について解説します。","2026-04-15",[12,13,14,15,16],"C#","MessagePack","ファイルフォーマット","シリアライズ","PICOM",1,{"loc":4,"lastmod":10,"priority":17},{"type":20,"children":21,"toc":3000},"root",[22,30,53,66,91,97,110,122,128,163,169,174,186,213,278,284,311,743,762,821,832,837,869,881,1414,1426,1432,1467,1911,1923,1962,1974,2004,2010,2031,2118,2138,2151,2156,2162,2175,2424,2450,2456,2469,2503,2899,2935,2954,2959,2994],{"type":23,"tag":24,"props":25,"children":27},"element","h2",{"id":26},"はじめに",[28],{"type":29,"value":26},"text",{"type":23,"tag":31,"props":32,"children":33},"p",{},[34,36,42,44,51],{"type":29,"value":35},"PICOMというチップチューン向け音楽作成Webアプリを開発する中で、「楽譜データをどのフォーマットで保存するか」を決める必要がありました。最終的に選んだのが",{"type":23,"tag":37,"props":38,"children":40},"tooltip",{"content":39},"バイナリ形式のシリアライズフォーマット。JSONより小さくて速い",[41],{"type":29,"value":13},{"type":29,"value":43},"で、これに",{"type":23,"tag":45,"props":46,"children":48},"code",{"className":47},[],[49],{"type":29,"value":50},".pim",{"type":29,"value":52},"という独自拡張子を付けて「PIMフォーマット」と呼んでいます。",{"type":23,"tag":31,"props":54,"children":55},{},[56,58,64],{"type":29,"value":57},"この記事では、JSONではなくMessagePackを選んだ理由と、将来のフォーマット変更に備えた",{"type":23,"tag":59,"props":60,"children":61},"strong",{},[62],{"type":29,"value":63},"バージョン管理の設計",{"type":29,"value":65},"、そして実装で気をつけた点を書きます。「自作アプリのセーブデータをどう設計するか」で悩んでいる方の参考になれば嬉しいです。",{"type":23,"tag":67,"props":68,"children":69},"summary-box",{},[70],{"type":23,"tag":31,"props":71,"children":72},{},[73,75,81,83,89],{"type":29,"value":74},"PICOMのセーブデータ形式「PIM」はMessagePackベースで、バイナリの軽量さとフォーマット拡張への耐性を両立させています。モデルクラスに",{"type":23,"tag":45,"props":76,"children":78},{"className":77},[],[79],{"type":29,"value":80},"Version",{"type":29,"value":82},"プロパティを埋め込むことで将来のフォーマット変更に備え、",{"type":23,"tag":45,"props":84,"children":86},{"className":85},[],[87],{"type":29,"value":88},"[Key]",{"type":29,"value":90},"番号の管理ルールやReadResult enumでデシリアライズ失敗を型で表現する設計も紹介します。",{"type":23,"tag":24,"props":92,"children":94},{"id":93},"なぜjsonではなくmessagepackを選んだのか",[95],{"type":29,"value":96},"なぜJSONではなくMessagePackを選んだのか",{"type":23,"tag":31,"props":98,"children":99},{},[100,102,108],{"type":29,"value":101},"一番最初はJSONで実装するつもりでした。デバッグしやすいですし、",{"type":23,"tag":45,"props":103,"children":105},{"className":104},[],[106],{"type":29,"value":107},"System.Text.Json",{"type":29,"value":109},"は.NET標準で追加の依存がいりません。ただ、PICOMで扱うデータの性質を考えた結果、MessagePackに切り替えました。",{"type":23,"tag":31,"props":111,"children":112},{},[113,115,120],{"type":29,"value":114},"決め手を一言で言うと、",{"type":23,"tag":59,"props":116,"children":117},{},[118],{"type":29,"value":119},"バイナリフォーマットの利点が欲しかったから",{"type":29,"value":121},"です。",{"type":23,"tag":123,"props":124,"children":126},"h3",{"id":125},"サイズが小さい",[127],{"type":29,"value":125},{"type":23,"tag":31,"props":129,"children":130},{},[131,133,139,141,147,148,154,156,161],{"type":29,"value":132},"PICOMの1曲は数十〜数百の音符と、各音符にエフェクト情報が付きます。JSONで書くとキー名（",{"type":23,"tag":45,"props":134,"children":136},{"className":135},[],[137],{"type":29,"value":138},"\"scale\"",{"type":29,"value":140},", ",{"type":23,"tag":45,"props":142,"children":144},{"className":143},[],[145],{"type":29,"value":146},"\"scaleNumber\"",{"type":29,"value":140},{"type":23,"tag":45,"props":149,"children":151},{"className":150},[],[152],{"type":29,"value":153},"\"length\"",{"type":29,"value":155},"...）が毎音符ごとに繰り返され、中身より外側が重くなりがちです。MessagePackはキーを整数に置き換えられるので、同じデータで比較すると",{"type":23,"tag":59,"props":157,"children":158},{},[159],{"type":29,"value":160},"ざっくり半分以下",{"type":29,"value":162},"になります。ブラウザのIndexedDBに保存する以上、軽いに越したことはありません。",{"type":23,"tag":123,"props":164,"children":166},{"id":165},"indexeddbとの相性が良い",[167],{"type":29,"value":168},"IndexedDBとの相性が良い",{"type":23,"tag":31,"props":170,"children":171},{},[172],{"type":29,"value":173},"最終的にIndexedDBに入れるのはバイト列なので、中間でJSON文字列を経由するよりも、最初からバイナリで扱える方がシンプルになります。余計な文字列⇔バイト列の変換が挟まらない分、保存・読み込み周りのコードの見通しも良くなります。",{"type":23,"tag":31,"props":175,"children":176},{},[177,179,184],{"type":29,"value":178},"ただし、バイナリなら何でも良かったわけではありません。独自のバイナリ形式を自力で設計すると、",{"type":23,"tag":59,"props":180,"children":181},{},[182],{"type":29,"value":183},"フィールドを1つ追加しただけで既存のセーブデータが一切読めなくなる",{"type":29,"value":185}," という事態に容易に陥ります。PICOMの楽譜データには、今後も新しいフィールドを追加していく可能性が残っており、拡張に耐えるフォーマットであることは必須条件でした。",{"type":23,"tag":31,"props":187,"children":188},{},[189,191,197,199,204,206,211],{"type":29,"value":190},"MessagePackを選んだのは、この問題への答えを持っていたからです。",{"type":23,"tag":45,"props":192,"children":194},{"className":193},[],[195],{"type":29,"value":196},"[Key(n)]",{"type":29,"value":198}," の番号運用ルールさえ守れば、",{"type":23,"tag":59,"props":200,"children":201},{},[202],{"type":29,"value":203},"新しいフィールドを追加しても古いデータが壊れず、逆に古いコードで新しいデータを読んでも余分なフィールドが無視されるだけで済みます",{"type":29,"value":205},"。どう拡張に耐えるかは後述の「Versionフィールドで将来のフォーマット変更に備える」で具体的に扱いますが、",{"type":23,"tag":59,"props":207,"children":208},{},[209],{"type":29,"value":210},"「バイナリの扱いやすさ」と「フォーマット拡張への耐性」が両立している",{"type":29,"value":212}," 点こそが、PICOMが最終的にMessagePackを選んだ決定打でした。",{"type":23,"tag":214,"props":215,"children":218},"tradeoff-box",{"cons-label":216,"pros-label":217},"デメリット","メリット",[219,248],{"type":23,"tag":220,"props":221,"children":222},"template",{"v-slot:pros":7},[223],{"type":23,"tag":224,"props":225,"children":226},"ul",{},[227,233,238],{"type":23,"tag":228,"props":229,"children":230},"li",{},[231],{"type":29,"value":232},"JSONより小さい（同じデータで半分程度）",{"type":23,"tag":228,"props":234,"children":235},{},[236],{"type":29,"value":237},"バイナリなのでIndexedDBへの保存時に余計な文字列変換が要らない",{"type":23,"tag":228,"props":239,"children":240},{},[241,246],{"type":23,"tag":45,"props":242,"children":244},{"className":243},[],[245],{"type":29,"value":88},{"type":29,"value":247},"番号を守ればフィールド追加しても古いデータが壊れない拡張性",{"type":23,"tag":220,"props":249,"children":250},{"v-slot:cons":7},[251,256,268],{"type":23,"tag":228,"props":252,"children":253},{},[254],{"type":29,"value":255},"人間が目視できない（デバッグ時にbase64→MessagePack CLI必須）",{"type":23,"tag":228,"props":257,"children":258},{},[259,261,266],{"type":29,"value":260},"NuGetパッケージ",{"type":23,"tag":45,"props":262,"children":264},{"className":263},[],[265],{"type":29,"value":13},{"type":29,"value":267},"への依存が増える",{"type":23,"tag":228,"props":269,"children":270},{},[271,276],{"type":23,"tag":45,"props":272,"children":274},{"className":273},[],[275],{"type":29,"value":88},{"type":29,"value":277},"番号を後から変更できない",{"type":23,"tag":24,"props":279,"children":281},{"id":280},"pimフォーマットのデータモデル",[282],{"type":29,"value":283},"PIMフォーマットのデータモデル",{"type":23,"tag":31,"props":285,"children":286},{},[287,289,295,296,302,304,309],{"type":29,"value":288},"セーブデータのトップレベルは次の",{"type":23,"tag":45,"props":290,"children":292},{"className":291},[],[293],{"type":29,"value":294},"MusicDataModel",{"type":29,"value":121},{"type":23,"tag":45,"props":297,"children":299},{"className":298},[],[300],{"type":29,"value":301},"[MessagePackObject]",{"type":29,"value":303},"と",{"type":23,"tag":45,"props":305,"children":307},{"className":306},[],[308],{"type":29,"value":196},{"type":29,"value":310},"の組み合わせでフィールド位置を明示しています。",{"type":23,"tag":312,"props":313,"children":317},"pre",{"className":314,"code":315,"language":316,"meta":7,"style":7},"language-csharp shiki shiki-themes vitesse-dark","[MessagePackObject]\npublic class MusicDataModel\n{\n    [Key(0)]\n    public int Version { get; set; } = 2;\n\n    [Key(1)]\n    public MusicMetadataModel Metadata { get; set; } = new();\n\n    [Key(2)]\n    public MusicSettingsModel Settings { get; set; } = new();\n\n    [Key(3)]\n    public TrackDataModel[] Tracks { get; set; } = [];\n}\n","csharp",[318],{"type":23,"tag":45,"props":319,"children":320},{"__ignoreMap":7},[321,343,363,372,402,467,477,502,558,566,591,645,653,678,734],{"type":23,"tag":322,"props":323,"children":325},"span",{"class":324,"line":17},"line",[326,332,338],{"type":23,"tag":322,"props":327,"children":329},{"style":328},"--shiki-default:#666666",[330],{"type":29,"value":331},"[",{"type":23,"tag":322,"props":333,"children":335},{"style":334},"--shiki-default:#5DA994",[336],{"type":29,"value":337},"MessagePackObject",{"type":23,"tag":322,"props":339,"children":340},{"style":328},[341],{"type":29,"value":342},"]\n",{"type":23,"tag":322,"props":344,"children":346},{"class":324,"line":345},2,[347,353,358],{"type":23,"tag":322,"props":348,"children":350},{"style":349},"--shiki-default:#CB7676",[351],{"type":29,"value":352},"public",{"type":23,"tag":322,"props":354,"children":355},{"style":349},[356],{"type":29,"value":357}," class",{"type":23,"tag":322,"props":359,"children":360},{"style":334},[361],{"type":29,"value":362}," MusicDataModel\n",{"type":23,"tag":322,"props":364,"children":366},{"class":324,"line":365},3,[367],{"type":23,"tag":322,"props":368,"children":369},{"style":328},[370],{"type":29,"value":371},"{\n",{"type":23,"tag":322,"props":373,"children":375},{"class":324,"line":374},4,[376,381,386,391,397],{"type":23,"tag":322,"props":377,"children":378},{"style":328},[379],{"type":29,"value":380},"    [",{"type":23,"tag":322,"props":382,"children":383},{"style":334},[384],{"type":29,"value":385},"Key",{"type":23,"tag":322,"props":387,"children":388},{"style":328},[389],{"type":29,"value":390},"(",{"type":23,"tag":322,"props":392,"children":394},{"style":393},"--shiki-default:#4C9A91",[395],{"type":29,"value":396},"0",{"type":23,"tag":322,"props":398,"children":399},{"style":328},[400],{"type":29,"value":401},")]\n",{"type":23,"tag":322,"props":403,"children":405},{"class":324,"line":404},5,[406,411,417,423,428,433,438,443,447,452,457,462],{"type":23,"tag":322,"props":407,"children":408},{"style":349},[409],{"type":29,"value":410},"    public",{"type":23,"tag":322,"props":412,"children":414},{"style":413},"--shiki-default:#4D9375",[415],{"type":29,"value":416}," int",{"type":23,"tag":322,"props":418,"children":420},{"style":419},"--shiki-default:#80A665",[421],{"type":29,"value":422}," Version",{"type":23,"tag":322,"props":424,"children":425},{"style":328},[426],{"type":29,"value":427}," {",{"type":23,"tag":322,"props":429,"children":430},{"style":349},[431],{"type":29,"value":432}," get",{"type":23,"tag":322,"props":434,"children":435},{"style":328},[436],{"type":29,"value":437},";",{"type":23,"tag":322,"props":439,"children":440},{"style":349},[441],{"type":29,"value":442}," set",{"type":23,"tag":322,"props":444,"children":445},{"style":328},[446],{"type":29,"value":437},{"type":23,"tag":322,"props":448,"children":449},{"style":328},[450],{"type":29,"value":451}," }",{"type":23,"tag":322,"props":453,"children":454},{"style":328},[455],{"type":29,"value":456}," =",{"type":23,"tag":322,"props":458,"children":459},{"style":393},[460],{"type":29,"value":461}," 2",{"type":23,"tag":322,"props":463,"children":464},{"style":328},[465],{"type":29,"value":466},";\n",{"type":23,"tag":322,"props":468,"children":470},{"class":324,"line":469},6,[471],{"type":23,"tag":322,"props":472,"children":474},{"emptyLinePlaceholder":473},true,[475],{"type":29,"value":476},"\n",{"type":23,"tag":322,"props":478,"children":480},{"class":324,"line":479},7,[481,485,489,493,498],{"type":23,"tag":322,"props":482,"children":483},{"style":328},[484],{"type":29,"value":380},{"type":23,"tag":322,"props":486,"children":487},{"style":334},[488],{"type":29,"value":385},{"type":23,"tag":322,"props":490,"children":491},{"style":328},[492],{"type":29,"value":390},{"type":23,"tag":322,"props":494,"children":495},{"style":393},[496],{"type":29,"value":497},"1",{"type":23,"tag":322,"props":499,"children":500},{"style":328},[501],{"type":29,"value":401},{"type":23,"tag":322,"props":503,"children":505},{"class":324,"line":504},8,[506,510,515,520,524,528,532,536,540,544,548,553],{"type":23,"tag":322,"props":507,"children":508},{"style":349},[509],{"type":29,"value":410},{"type":23,"tag":322,"props":511,"children":512},{"style":334},[513],{"type":29,"value":514}," MusicMetadataModel",{"type":23,"tag":322,"props":516,"children":517},{"style":419},[518],{"type":29,"value":519}," Metadata",{"type":23,"tag":322,"props":521,"children":522},{"style":328},[523],{"type":29,"value":427},{"type":23,"tag":322,"props":525,"children":526},{"style":349},[527],{"type":29,"value":432},{"type":23,"tag":322,"props":529,"children":530},{"style":328},[531],{"type":29,"value":437},{"type":23,"tag":322,"props":533,"children":534},{"style":349},[535],{"type":29,"value":442},{"type":23,"tag":322,"props":537,"children":538},{"style":328},[539],{"type":29,"value":437},{"type":23,"tag":322,"props":541,"children":542},{"style":328},[543],{"type":29,"value":451},{"type":23,"tag":322,"props":545,"children":546},{"style":328},[547],{"type":29,"value":456},{"type":23,"tag":322,"props":549,"children":550},{"style":349},[551],{"type":29,"value":552}," new",{"type":23,"tag":322,"props":554,"children":555},{"style":328},[556],{"type":29,"value":557},"();\n",{"type":23,"tag":322,"props":559,"children":561},{"class":324,"line":560},9,[562],{"type":23,"tag":322,"props":563,"children":564},{"emptyLinePlaceholder":473},[565],{"type":29,"value":476},{"type":23,"tag":322,"props":567,"children":569},{"class":324,"line":568},10,[570,574,578,582,587],{"type":23,"tag":322,"props":571,"children":572},{"style":328},[573],{"type":29,"value":380},{"type":23,"tag":322,"props":575,"children":576},{"style":334},[577],{"type":29,"value":385},{"type":23,"tag":322,"props":579,"children":580},{"style":328},[581],{"type":29,"value":390},{"type":23,"tag":322,"props":583,"children":584},{"style":393},[585],{"type":29,"value":586},"2",{"type":23,"tag":322,"props":588,"children":589},{"style":328},[590],{"type":29,"value":401},{"type":23,"tag":322,"props":592,"children":594},{"class":324,"line":593},11,[595,599,604,609,613,617,621,625,629,633,637,641],{"type":23,"tag":322,"props":596,"children":597},{"style":349},[598],{"type":29,"value":410},{"type":23,"tag":322,"props":600,"children":601},{"style":334},[602],{"type":29,"value":603}," MusicSettingsModel",{"type":23,"tag":322,"props":605,"children":606},{"style":419},[607],{"type":29,"value":608}," Settings",{"type":23,"tag":322,"props":610,"children":611},{"style":328},[612],{"type":29,"value":427},{"type":23,"tag":322,"props":614,"children":615},{"style":349},[616],{"type":29,"value":432},{"type":23,"tag":322,"props":618,"children":619},{"style":328},[620],{"type":29,"value":437},{"type":23,"tag":322,"props":622,"children":623},{"style":349},[624],{"type":29,"value":442},{"type":23,"tag":322,"props":626,"children":627},{"style":328},[628],{"type":29,"value":437},{"type":23,"tag":322,"props":630,"children":631},{"style":328},[632],{"type":29,"value":451},{"type":23,"tag":322,"props":634,"children":635},{"style":328},[636],{"type":29,"value":456},{"type":23,"tag":322,"props":638,"children":639},{"style":349},[640],{"type":29,"value":552},{"type":23,"tag":322,"props":642,"children":643},{"style":328},[644],{"type":29,"value":557},{"type":23,"tag":322,"props":646,"children":648},{"class":324,"line":647},12,[649],{"type":23,"tag":322,"props":650,"children":651},{"emptyLinePlaceholder":473},[652],{"type":29,"value":476},{"type":23,"tag":322,"props":654,"children":656},{"class":324,"line":655},13,[657,661,665,669,674],{"type":23,"tag":322,"props":658,"children":659},{"style":328},[660],{"type":29,"value":380},{"type":23,"tag":322,"props":662,"children":663},{"style":334},[664],{"type":29,"value":385},{"type":23,"tag":322,"props":666,"children":667},{"style":328},[668],{"type":29,"value":390},{"type":23,"tag":322,"props":670,"children":671},{"style":393},[672],{"type":29,"value":673},"3",{"type":23,"tag":322,"props":675,"children":676},{"style":328},[677],{"type":29,"value":401},{"type":23,"tag":322,"props":679,"children":681},{"class":324,"line":680},14,[682,686,691,696,701,705,709,713,717,721,725,729],{"type":23,"tag":322,"props":683,"children":684},{"style":349},[685],{"type":29,"value":410},{"type":23,"tag":322,"props":687,"children":688},{"style":334},[689],{"type":29,"value":690}," TrackDataModel",{"type":23,"tag":322,"props":692,"children":693},{"style":328},[694],{"type":29,"value":695},"[]",{"type":23,"tag":322,"props":697,"children":698},{"style":419},[699],{"type":29,"value":700}," Tracks",{"type":23,"tag":322,"props":702,"children":703},{"style":328},[704],{"type":29,"value":427},{"type":23,"tag":322,"props":706,"children":707},{"style":349},[708],{"type":29,"value":432},{"type":23,"tag":322,"props":710,"children":711},{"style":328},[712],{"type":29,"value":437},{"type":23,"tag":322,"props":714,"children":715},{"style":349},[716],{"type":29,"value":442},{"type":23,"tag":322,"props":718,"children":719},{"style":328},[720],{"type":29,"value":437},{"type":23,"tag":322,"props":722,"children":723},{"style":328},[724],{"type":29,"value":451},{"type":23,"tag":322,"props":726,"children":727},{"style":328},[728],{"type":29,"value":456},{"type":23,"tag":322,"props":730,"children":731},{"style":328},[732],{"type":29,"value":733}," [];\n",{"type":23,"tag":322,"props":735,"children":737},{"class":324,"line":736},15,[738],{"type":23,"tag":322,"props":739,"children":740},{"style":328},[741],{"type":29,"value":742},"}\n",{"type":23,"tag":31,"props":744,"children":745},{},[746,748,753,755,760],{"type":29,"value":747},"ここで一番重要なのが",{"type":23,"tag":45,"props":749,"children":751},{"className":750},[],[752],{"type":29,"value":80},{"type":29,"value":754},"プロパティです。後述しますが、これは",{"type":23,"tag":59,"props":756,"children":757},{},[758],{"type":29,"value":759},"書き込んだ時のフォーマットバージョン",{"type":29,"value":761},"を自己申告するためのフィールドで、将来のフォーマット進化の鍵になります。",{"type":23,"tag":31,"props":763,"children":764},{},[765,767,773,775,781,783,789,790,796,797,803,805,811,813,819],{"type":29,"value":766},"Trackの中身はもっとツリーが深く、",{"type":23,"tag":45,"props":768,"children":770},{"className":769},[],[771],{"type":29,"value":772},"ComponentDataModel",{"type":29,"value":774},"を基底にした",{"type":23,"tag":45,"props":776,"children":778},{"className":777},[],[779],{"type":29,"value":780},"NoteDataModel",{"type":29,"value":782}," / ",{"type":23,"tag":45,"props":784,"children":786},{"className":785},[],[787],{"type":29,"value":788},"RestDataModel",{"type":29,"value":782},{"type":23,"tag":45,"props":791,"children":793},{"className":792},[],[794],{"type":29,"value":795},"TieDataModel",{"type":29,"value":782},{"type":23,"tag":45,"props":798,"children":800},{"className":799},[],[801],{"type":29,"value":802},"TupletDataModel",{"type":29,"value":804},"のサブクラスが入っています。MessagePackでは",{"type":23,"tag":45,"props":806,"children":808},{"className":807},[],[809],{"type":29,"value":810},"[Union]",{"type":29,"value":812},"属性で多態を表現する方法と、サブクラスを判別する",{"type":23,"tag":45,"props":814,"children":816},{"className":815},[],[817],{"type":29,"value":818},"Type",{"type":29,"value":820},"フィールドを持たせる方法がありますが、PIMでは前者を使っています。",{"type":23,"tag":24,"props":822,"children":824},{"id":823},"key番号は追記のみ絶対に削除しない",[825,830],{"type":23,"tag":45,"props":826,"children":828},{"className":827},[],[829],{"type":29,"value":88},{"type":29,"value":831},"番号は追記のみ、絶対に削除しない",{"type":23,"tag":31,"props":833,"children":834},{},[835],{"type":29,"value":836},"DBの主キーもそうですが、キーはイミュータブルな運用としています。",{"type":23,"tag":31,"props":838,"children":839},{},[840,846,847,853,854,860,862,867],{"type":23,"tag":45,"props":841,"children":843},{"className":842},[],[844],{"type":29,"value":845},"[Key(0)]",{"type":29,"value":140},{"type":23,"tag":45,"props":848,"children":850},{"className":849},[],[851],{"type":29,"value":852},"[Key(1)]",{"type":29,"value":140},{"type":23,"tag":45,"props":855,"children":857},{"className":856},[],[858],{"type":29,"value":859},"[Key(2)]",{"type":29,"value":861},"... の番号は、一度付けたら",{"type":23,"tag":59,"props":863,"children":864},{},[865],{"type":29,"value":866},"絶対に変えてはいけません",{"type":29,"value":868},"。番号はバイナリ内の物理的な位置そのもので、番号を変えると古いセーブデータが読み込めなくなります。",{"type":23,"tag":31,"props":870,"children":871},{},[872,874,879],{"type":29,"value":873},"フィールドを増やすときはどうするか。次のように常に",{"type":23,"tag":59,"props":875,"children":876},{},[877],{"type":29,"value":878},"新しい番号を末尾に追加",{"type":29,"value":880},"します。",{"type":23,"tag":312,"props":882,"children":884},{"className":314,"code":883,"language":316,"meta":7,"style":7},"// 悪い例: 既存の番号を詰め直してしまう\n[Key(0)] public int Version { get; set; }\n[Key(1)] public MusicSettingsModel Settings { get; set; }  // Metadataを消して番号を詰めた\n[Key(2)] public TrackDataModel[] Tracks { get; set; }\n\n// 良い例: 既存番号はそのまま、新規は末尾に追加\n[Key(0)] public int Version { get; set; }\n[Key(1)] public MusicMetadataModel Metadata { get; set; }\n[Key(2)] public MusicSettingsModel Settings { get; set; }\n[Key(3)] public TrackDataModel[] Tracks { get; set; }\n[Key(4)] public string? Comment { get; set; }  // 新規追加\n",[885],{"type":23,"tag":45,"props":886,"children":887},{"__ignoreMap":7},[888,897,960,1024,1087,1094,1102,1161,1220,1279,1342],{"type":23,"tag":322,"props":889,"children":890},{"class":324,"line":17},[891],{"type":23,"tag":322,"props":892,"children":894},{"style":893},"--shiki-default:#758575DD",[895],{"type":29,"value":896},"// 悪い例: 既存の番号を詰め直してしまう\n",{"type":23,"tag":322,"props":898,"children":899},{"class":324,"line":345},[900,904,908,912,916,921,926,930,935,939,943,947,951,955],{"type":23,"tag":322,"props":901,"children":902},{"style":328},[903],{"type":29,"value":331},{"type":23,"tag":322,"props":905,"children":906},{"style":334},[907],{"type":29,"value":385},{"type":23,"tag":322,"props":909,"children":910},{"style":328},[911],{"type":29,"value":390},{"type":23,"tag":322,"props":913,"children":914},{"style":393},[915],{"type":29,"value":396},{"type":23,"tag":322,"props":917,"children":918},{"style":328},[919],{"type":29,"value":920},")]",{"type":23,"tag":322,"props":922,"children":923},{"style":349},[924],{"type":29,"value":925}," public",{"type":23,"tag":322,"props":927,"children":928},{"style":413},[929],{"type":29,"value":416},{"type":23,"tag":322,"props":931,"children":933},{"style":932},"--shiki-default:#BD976A",[934],{"type":29,"value":422},{"type":23,"tag":322,"props":936,"children":937},{"style":328},[938],{"type":29,"value":427},{"type":23,"tag":322,"props":940,"children":941},{"style":932},[942],{"type":29,"value":432},{"type":23,"tag":322,"props":944,"children":945},{"style":328},[946],{"type":29,"value":437},{"type":23,"tag":322,"props":948,"children":949},{"style":932},[950],{"type":29,"value":442},{"type":23,"tag":322,"props":952,"children":953},{"style":328},[954],{"type":29,"value":437},{"type":23,"tag":322,"props":956,"children":957},{"style":328},[958],{"type":29,"value":959}," }\n",{"type":23,"tag":322,"props":961,"children":962},{"class":324,"line":365},[963,967,971,975,979,983,987,991,995,999,1003,1007,1011,1015,1019],{"type":23,"tag":322,"props":964,"children":965},{"style":328},[966],{"type":29,"value":331},{"type":23,"tag":322,"props":968,"children":969},{"style":334},[970],{"type":29,"value":385},{"type":23,"tag":322,"props":972,"children":973},{"style":328},[974],{"type":29,"value":390},{"type":23,"tag":322,"props":976,"children":977},{"style":393},[978],{"type":29,"value":497},{"type":23,"tag":322,"props":980,"children":981},{"style":328},[982],{"type":29,"value":920},{"type":23,"tag":322,"props":984,"children":985},{"style":349},[986],{"type":29,"value":925},{"type":23,"tag":322,"props":988,"children":989},{"style":932},[990],{"type":29,"value":603},{"type":23,"tag":322,"props":992,"children":993},{"style":932},[994],{"type":29,"value":608},{"type":23,"tag":322,"props":996,"children":997},{"style":328},[998],{"type":29,"value":427},{"type":23,"tag":322,"props":1000,"children":1001},{"style":932},[1002],{"type":29,"value":432},{"type":23,"tag":322,"props":1004,"children":1005},{"style":328},[1006],{"type":29,"value":437},{"type":23,"tag":322,"props":1008,"children":1009},{"style":932},[1010],{"type":29,"value":442},{"type":23,"tag":322,"props":1012,"children":1013},{"style":328},[1014],{"type":29,"value":437},{"type":23,"tag":322,"props":1016,"children":1017},{"style":328},[1018],{"type":29,"value":451},{"type":23,"tag":322,"props":1020,"children":1021},{"style":893},[1022],{"type":29,"value":1023},"  // Metadataを消して番号を詰めた\n",{"type":23,"tag":322,"props":1025,"children":1026},{"class":324,"line":374},[1027,1031,1035,1039,1043,1047,1051,1055,1059,1063,1067,1071,1075,1079,1083],{"type":23,"tag":322,"props":1028,"children":1029},{"style":328},[1030],{"type":29,"value":331},{"type":23,"tag":322,"props":1032,"children":1033},{"style":334},[1034],{"type":29,"value":385},{"type":23,"tag":322,"props":1036,"children":1037},{"style":328},[1038],{"type":29,"value":390},{"type":23,"tag":322,"props":1040,"children":1041},{"style":393},[1042],{"type":29,"value":586},{"type":23,"tag":322,"props":1044,"children":1045},{"style":328},[1046],{"type":29,"value":920},{"type":23,"tag":322,"props":1048,"children":1049},{"style":349},[1050],{"type":29,"value":925},{"type":23,"tag":322,"props":1052,"children":1053},{"style":932},[1054],{"type":29,"value":690},{"type":23,"tag":322,"props":1056,"children":1057},{"style":328},[1058],{"type":29,"value":695},{"type":23,"tag":322,"props":1060,"children":1061},{"style":932},[1062],{"type":29,"value":700},{"type":23,"tag":322,"props":1064,"children":1065},{"style":328},[1066],{"type":29,"value":427},{"type":23,"tag":322,"props":1068,"children":1069},{"style":932},[1070],{"type":29,"value":432},{"type":23,"tag":322,"props":1072,"children":1073},{"style":328},[1074],{"type":29,"value":437},{"type":23,"tag":322,"props":1076,"children":1077},{"style":932},[1078],{"type":29,"value":442},{"type":23,"tag":322,"props":1080,"children":1081},{"style":328},[1082],{"type":29,"value":437},{"type":23,"tag":322,"props":1084,"children":1085},{"style":328},[1086],{"type":29,"value":959},{"type":23,"tag":322,"props":1088,"children":1089},{"class":324,"line":404},[1090],{"type":23,"tag":322,"props":1091,"children":1092},{"emptyLinePlaceholder":473},[1093],{"type":29,"value":476},{"type":23,"tag":322,"props":1095,"children":1096},{"class":324,"line":469},[1097],{"type":23,"tag":322,"props":1098,"children":1099},{"style":893},[1100],{"type":29,"value":1101},"// 良い例: 既存番号はそのまま、新規は末尾に追加\n",{"type":23,"tag":322,"props":1103,"children":1104},{"class":324,"line":479},[1105,1109,1113,1117,1121,1125,1129,1133,1137,1141,1145,1149,1153,1157],{"type":23,"tag":322,"props":1106,"children":1107},{"style":328},[1108],{"type":29,"value":331},{"type":23,"tag":322,"props":1110,"children":1111},{"style":334},[1112],{"type":29,"value":385},{"type":23,"tag":322,"props":1114,"children":1115},{"style":328},[1116],{"type":29,"value":390},{"type":23,"tag":322,"props":1118,"children":1119},{"style":393},[1120],{"type":29,"value":396},{"type":23,"tag":322,"props":1122,"children":1123},{"style":328},[1124],{"type":29,"value":920},{"type":23,"tag":322,"props":1126,"children":1127},{"style":349},[1128],{"type":29,"value":925},{"type":23,"tag":322,"props":1130,"children":1131},{"style":413},[1132],{"type":29,"value":416},{"type":23,"tag":322,"props":1134,"children":1135},{"style":932},[1136],{"type":29,"value":422},{"type":23,"tag":322,"props":1138,"children":1139},{"style":328},[1140],{"type":29,"value":427},{"type":23,"tag":322,"props":1142,"children":1143},{"style":932},[1144],{"type":29,"value":432},{"type":23,"tag":322,"props":1146,"children":1147},{"style":328},[1148],{"type":29,"value":437},{"type":23,"tag":322,"props":1150,"children":1151},{"style":932},[1152],{"type":29,"value":442},{"type":23,"tag":322,"props":1154,"children":1155},{"style":328},[1156],{"type":29,"value":437},{"type":23,"tag":322,"props":1158,"children":1159},{"style":328},[1160],{"type":29,"value":959},{"type":23,"tag":322,"props":1162,"children":1163},{"class":324,"line":504},[1164,1168,1172,1176,1180,1184,1188,1192,1196,1200,1204,1208,1212,1216],{"type":23,"tag":322,"props":1165,"children":1166},{"style":328},[1167],{"type":29,"value":331},{"type":23,"tag":322,"props":1169,"children":1170},{"style":334},[1171],{"type":29,"value":385},{"type":23,"tag":322,"props":1173,"children":1174},{"style":328},[1175],{"type":29,"value":390},{"type":23,"tag":322,"props":1177,"children":1178},{"style":393},[1179],{"type":29,"value":497},{"type":23,"tag":322,"props":1181,"children":1182},{"style":328},[1183],{"type":29,"value":920},{"type":23,"tag":322,"props":1185,"children":1186},{"style":349},[1187],{"type":29,"value":925},{"type":23,"tag":322,"props":1189,"children":1190},{"style":932},[1191],{"type":29,"value":514},{"type":23,"tag":322,"props":1193,"children":1194},{"style":932},[1195],{"type":29,"value":519},{"type":23,"tag":322,"props":1197,"children":1198},{"style":328},[1199],{"type":29,"value":427},{"type":23,"tag":322,"props":1201,"children":1202},{"style":932},[1203],{"type":29,"value":432},{"type":23,"tag":322,"props":1205,"children":1206},{"style":328},[1207],{"type":29,"value":437},{"type":23,"tag":322,"props":1209,"children":1210},{"style":932},[1211],{"type":29,"value":442},{"type":23,"tag":322,"props":1213,"children":1214},{"style":328},[1215],{"type":29,"value":437},{"type":23,"tag":322,"props":1217,"children":1218},{"style":328},[1219],{"type":29,"value":959},{"type":23,"tag":322,"props":1221,"children":1222},{"class":324,"line":560},[1223,1227,1231,1235,1239,1243,1247,1251,1255,1259,1263,1267,1271,1275],{"type":23,"tag":322,"props":1224,"children":1225},{"style":328},[1226],{"type":29,"value":331},{"type":23,"tag":322,"props":1228,"children":1229},{"style":334},[1230],{"type":29,"value":385},{"type":23,"tag":322,"props":1232,"children":1233},{"style":328},[1234],{"type":29,"value":390},{"type":23,"tag":322,"props":1236,"children":1237},{"style":393},[1238],{"type":29,"value":586},{"type":23,"tag":322,"props":1240,"children":1241},{"style":328},[1242],{"type":29,"value":920},{"type":23,"tag":322,"props":1244,"children":1245},{"style":349},[1246],{"type":29,"value":925},{"type":23,"tag":322,"props":1248,"children":1249},{"style":932},[1250],{"type":29,"value":603},{"type":23,"tag":322,"props":1252,"children":1253},{"style":932},[1254],{"type":29,"value":608},{"type":23,"tag":322,"props":1256,"children":1257},{"style":328},[1258],{"type":29,"value":427},{"type":23,"tag":322,"props":1260,"children":1261},{"style":932},[1262],{"type":29,"value":432},{"type":23,"tag":322,"props":1264,"children":1265},{"style":328},[1266],{"type":29,"value":437},{"type":23,"tag":322,"props":1268,"children":1269},{"style":932},[1270],{"type":29,"value":442},{"type":23,"tag":322,"props":1272,"children":1273},{"style":328},[1274],{"type":29,"value":437},{"type":23,"tag":322,"props":1276,"children":1277},{"style":328},[1278],{"type":29,"value":959},{"type":23,"tag":322,"props":1280,"children":1281},{"class":324,"line":568},[1282,1286,1290,1294,1298,1302,1306,1310,1314,1318,1322,1326,1330,1334,1338],{"type":23,"tag":322,"props":1283,"children":1284},{"style":328},[1285],{"type":29,"value":331},{"type":23,"tag":322,"props":1287,"children":1288},{"style":334},[1289],{"type":29,"value":385},{"type":23,"tag":322,"props":1291,"children":1292},{"style":328},[1293],{"type":29,"value":390},{"type":23,"tag":322,"props":1295,"children":1296},{"style":393},[1297],{"type":29,"value":673},{"type":23,"tag":322,"props":1299,"children":1300},{"style":328},[1301],{"type":29,"value":920},{"type":23,"tag":322,"props":1303,"children":1304},{"style":349},[1305],{"type":29,"value":925},{"type":23,"tag":322,"props":1307,"children":1308},{"style":932},[1309],{"type":29,"value":690},{"type":23,"tag":322,"props":1311,"children":1312},{"style":328},[1313],{"type":29,"value":695},{"type":23,"tag":322,"props":1315,"children":1316},{"style":932},[1317],{"type":29,"value":700},{"type":23,"tag":322,"props":1319,"children":1320},{"style":328},[1321],{"type":29,"value":427},{"type":23,"tag":322,"props":1323,"children":1324},{"style":932},[1325],{"type":29,"value":432},{"type":23,"tag":322,"props":1327,"children":1328},{"style":328},[1329],{"type":29,"value":437},{"type":23,"tag":322,"props":1331,"children":1332},{"style":932},[1333],{"type":29,"value":442},{"type":23,"tag":322,"props":1335,"children":1336},{"style":328},[1337],{"type":29,"value":437},{"type":23,"tag":322,"props":1339,"children":1340},{"style":328},[1341],{"type":29,"value":959},{"type":23,"tag":322,"props":1343,"children":1344},{"class":324,"line":593},[1345,1349,1353,1357,1362,1366,1370,1375,1380,1385,1389,1393,1397,1401,1405,1409],{"type":23,"tag":322,"props":1346,"children":1347},{"style":328},[1348],{"type":29,"value":331},{"type":23,"tag":322,"props":1350,"children":1351},{"style":334},[1352],{"type":29,"value":385},{"type":23,"tag":322,"props":1354,"children":1355},{"style":328},[1356],{"type":29,"value":390},{"type":23,"tag":322,"props":1358,"children":1359},{"style":393},[1360],{"type":29,"value":1361},"4",{"type":23,"tag":322,"props":1363,"children":1364},{"style":328},[1365],{"type":29,"value":920},{"type":23,"tag":322,"props":1367,"children":1368},{"style":349},[1369],{"type":29,"value":925},{"type":23,"tag":322,"props":1371,"children":1372},{"style":413},[1373],{"type":29,"value":1374}," string",{"type":23,"tag":322,"props":1376,"children":1377},{"style":349},[1378],{"type":29,"value":1379},"?",{"type":23,"tag":322,"props":1381,"children":1382},{"style":932},[1383],{"type":29,"value":1384}," Comment",{"type":23,"tag":322,"props":1386,"children":1387},{"style":328},[1388],{"type":29,"value":427},{"type":23,"tag":322,"props":1390,"children":1391},{"style":932},[1392],{"type":29,"value":432},{"type":23,"tag":322,"props":1394,"children":1395},{"style":328},[1396],{"type":29,"value":437},{"type":23,"tag":322,"props":1398,"children":1399},{"style":932},[1400],{"type":29,"value":442},{"type":23,"tag":322,"props":1402,"children":1403},{"style":328},[1404],{"type":29,"value":437},{"type":23,"tag":322,"props":1406,"children":1407},{"style":328},[1408],{"type":29,"value":451},{"type":23,"tag":322,"props":1410,"children":1411},{"style":893},[1412],{"type":29,"value":1413},"  // 新規追加\n",{"type":23,"tag":31,"props":1415,"children":1416},{},[1417,1419,1424],{"type":29,"value":1418},"削除したいフィールドがあっても、番号は",{"type":23,"tag":59,"props":1420,"children":1421},{},[1422],{"type":29,"value":1423},"空き番として予約",{"type":29,"value":1425},"しておき、コメントで「deprecated」と書いておくのが安全です。",{"type":23,"tag":24,"props":1427,"children":1429},{"id":1428},"versionフィールドで将来のフォーマット変更に備える",[1430],{"type":29,"value":1431},"Versionフィールドで将来のフォーマット変更に備える",{"type":23,"tag":31,"props":1433,"children":1434},{},[1435,1437,1443,1445,1451,1453,1458,1460,1465],{"type":29,"value":1436},"たとえば「トラックに色を付けたい」という要件が来たとします。単純に",{"type":23,"tag":45,"props":1438,"children":1440},{"className":1439},[],[1441],{"type":29,"value":1442},"TrackDataModel",{"type":29,"value":1444},"に",{"type":23,"tag":45,"props":1446,"children":1448},{"className":1447},[],[1449],{"type":29,"value":1450},"[Key(n)] int ColorHex",{"type":29,"value":1452},"を足すだけでも新しい番号なら動きますが、PICOMでは",{"type":23,"tag":59,"props":1454,"children":1455},{},[1456],{"type":29,"value":1457},"モデル自体が大きく変わる将来",{"type":29,"value":1459},"にも備えて",{"type":23,"tag":45,"props":1461,"children":1463},{"className":1462},[],[1464],{"type":29,"value":80},{"type":29,"value":1466},"を数値で持たせています。",{"type":23,"tag":312,"props":1468,"children":1470},{"className":314,"code":1469,"language":316,"meta":7,"style":7},"public (ReadResult resultType, Music? music) Read(byte[] data, int musicId)\n{\n    MusicDataModel model;\n    try\n    {\n        model = MessagePackSerializer.Deserialize\u003CMusicDataModel>(data);\n        Console.WriteLine($\"[PIM] Read: deserialized OK, version = {model.Version}, tracks = {model.Tracks.Length}\");\n    }\n    catch (Exception ex)\n    {\n        Console.WriteLine($\"[PIM] Read: deserialize error: {ex.Message}\");\n        return (ReadResult.FormatError, null);\n    }\n    // ... model.Version に応じて分岐する余地がある\n}\n",[1471],{"type":23,"tag":45,"props":1472,"children":1473},{"__ignoreMap":7},[1474,1561,1568,1585,1593,1601,1652,1752,1760,1786,1793,1851,1889,1896,1904],{"type":23,"tag":322,"props":1475,"children":1476},{"class":324,"line":17},[1477,1481,1486,1491,1496,1501,1506,1510,1515,1520,1525,1529,1534,1538,1543,1547,1551,1556],{"type":23,"tag":322,"props":1478,"children":1479},{"style":349},[1480],{"type":29,"value":352},{"type":23,"tag":322,"props":1482,"children":1483},{"style":328},[1484],{"type":29,"value":1485}," (",{"type":23,"tag":322,"props":1487,"children":1488},{"style":334},[1489],{"type":29,"value":1490},"ReadResult",{"type":23,"tag":322,"props":1492,"children":1493},{"style":419},[1494],{"type":29,"value":1495}," resultType",{"type":23,"tag":322,"props":1497,"children":1498},{"style":328},[1499],{"type":29,"value":1500},",",{"type":23,"tag":322,"props":1502,"children":1503},{"style":334},[1504],{"type":29,"value":1505}," Music",{"type":23,"tag":322,"props":1507,"children":1508},{"style":328},[1509],{"type":29,"value":1379},{"type":23,"tag":322,"props":1511,"children":1512},{"style":419},[1513],{"type":29,"value":1514}," music",{"type":23,"tag":322,"props":1516,"children":1517},{"style":328},[1518],{"type":29,"value":1519},")",{"type":23,"tag":322,"props":1521,"children":1522},{"style":419},[1523],{"type":29,"value":1524}," Read",{"type":23,"tag":322,"props":1526,"children":1527},{"style":328},[1528],{"type":29,"value":390},{"type":23,"tag":322,"props":1530,"children":1531},{"style":413},[1532],{"type":29,"value":1533},"byte",{"type":23,"tag":322,"props":1535,"children":1536},{"style":328},[1537],{"type":29,"value":695},{"type":23,"tag":322,"props":1539,"children":1540},{"style":419},[1541],{"type":29,"value":1542}," data",{"type":23,"tag":322,"props":1544,"children":1545},{"style":328},[1546],{"type":29,"value":1500},{"type":23,"tag":322,"props":1548,"children":1549},{"style":413},[1550],{"type":29,"value":416},{"type":23,"tag":322,"props":1552,"children":1553},{"style":419},[1554],{"type":29,"value":1555}," musicId",{"type":23,"tag":322,"props":1557,"children":1558},{"style":328},[1559],{"type":29,"value":1560},")\n",{"type":23,"tag":322,"props":1562,"children":1563},{"class":324,"line":345},[1564],{"type":23,"tag":322,"props":1565,"children":1566},{"style":328},[1567],{"type":29,"value":371},{"type":23,"tag":322,"props":1569,"children":1570},{"class":324,"line":365},[1571,1576,1581],{"type":23,"tag":322,"props":1572,"children":1573},{"style":334},[1574],{"type":29,"value":1575},"    MusicDataModel",{"type":23,"tag":322,"props":1577,"children":1578},{"style":419},[1579],{"type":29,"value":1580}," model",{"type":23,"tag":322,"props":1582,"children":1583},{"style":328},[1584],{"type":29,"value":466},{"type":23,"tag":322,"props":1586,"children":1587},{"class":324,"line":374},[1588],{"type":23,"tag":322,"props":1589,"children":1590},{"style":413},[1591],{"type":29,"value":1592},"    try\n",{"type":23,"tag":322,"props":1594,"children":1595},{"class":324,"line":404},[1596],{"type":23,"tag":322,"props":1597,"children":1598},{"style":328},[1599],{"type":29,"value":1600},"    {\n",{"type":23,"tag":322,"props":1602,"children":1603},{"class":324,"line":469},[1604,1609,1613,1618,1623,1628,1633,1637,1642,1647],{"type":23,"tag":322,"props":1605,"children":1606},{"style":932},[1607],{"type":29,"value":1608},"        model",{"type":23,"tag":322,"props":1610,"children":1611},{"style":328},[1612],{"type":29,"value":456},{"type":23,"tag":322,"props":1614,"children":1615},{"style":932},[1616],{"type":29,"value":1617}," MessagePackSerializer",{"type":23,"tag":322,"props":1619,"children":1620},{"style":328},[1621],{"type":29,"value":1622},".",{"type":23,"tag":322,"props":1624,"children":1625},{"style":419},[1626],{"type":29,"value":1627},"Deserialize",{"type":23,"tag":322,"props":1629,"children":1630},{"style":328},[1631],{"type":29,"value":1632},"\u003C",{"type":23,"tag":322,"props":1634,"children":1635},{"style":334},[1636],{"type":29,"value":294},{"type":23,"tag":322,"props":1638,"children":1639},{"style":328},[1640],{"type":29,"value":1641},">(",{"type":23,"tag":322,"props":1643,"children":1644},{"style":932},[1645],{"type":29,"value":1646},"data",{"type":23,"tag":322,"props":1648,"children":1649},{"style":328},[1650],{"type":29,"value":1651},");\n",{"type":23,"tag":322,"props":1653,"children":1654},{"class":324,"line":479},[1655,1660,1664,1669,1673,1679,1685,1690,1695,1699,1703,1708,1713,1717,1721,1725,1730,1734,1739,1743,1748],{"type":23,"tag":322,"props":1656,"children":1657},{"style":932},[1658],{"type":29,"value":1659},"        Console",{"type":23,"tag":322,"props":1661,"children":1662},{"style":328},[1663],{"type":29,"value":1622},{"type":23,"tag":322,"props":1665,"children":1666},{"style":419},[1667],{"type":29,"value":1668},"WriteLine",{"type":23,"tag":322,"props":1670,"children":1671},{"style":328},[1672],{"type":29,"value":390},{"type":23,"tag":322,"props":1674,"children":1676},{"style":1675},"--shiki-default:#C98A7D77",[1677],{"type":29,"value":1678},"$\"",{"type":23,"tag":322,"props":1680,"children":1682},{"style":1681},"--shiki-default:#C98A7D",[1683],{"type":29,"value":1684},"[PIM] Read: deserialized OK, version = ",{"type":23,"tag":322,"props":1686,"children":1687},{"style":328},[1688],{"type":29,"value":1689},"{",{"type":23,"tag":322,"props":1691,"children":1692},{"style":1681},[1693],{"type":29,"value":1694},"model",{"type":23,"tag":322,"props":1696,"children":1697},{"style":328},[1698],{"type":29,"value":1622},{"type":23,"tag":322,"props":1700,"children":1701},{"style":1681},[1702],{"type":29,"value":80},{"type":23,"tag":322,"props":1704,"children":1705},{"style":328},[1706],{"type":29,"value":1707},"}",{"type":23,"tag":322,"props":1709,"children":1710},{"style":1681},[1711],{"type":29,"value":1712},", tracks = ",{"type":23,"tag":322,"props":1714,"children":1715},{"style":328},[1716],{"type":29,"value":1689},{"type":23,"tag":322,"props":1718,"children":1719},{"style":1681},[1720],{"type":29,"value":1694},{"type":23,"tag":322,"props":1722,"children":1723},{"style":328},[1724],{"type":29,"value":1622},{"type":23,"tag":322,"props":1726,"children":1727},{"style":1681},[1728],{"type":29,"value":1729},"Tracks",{"type":23,"tag":322,"props":1731,"children":1732},{"style":328},[1733],{"type":29,"value":1622},{"type":23,"tag":322,"props":1735,"children":1736},{"style":1681},[1737],{"type":29,"value":1738},"Length",{"type":23,"tag":322,"props":1740,"children":1741},{"style":328},[1742],{"type":29,"value":1707},{"type":23,"tag":322,"props":1744,"children":1745},{"style":1675},[1746],{"type":29,"value":1747},"\"",{"type":23,"tag":322,"props":1749,"children":1750},{"style":328},[1751],{"type":29,"value":1651},{"type":23,"tag":322,"props":1753,"children":1754},{"class":324,"line":504},[1755],{"type":23,"tag":322,"props":1756,"children":1757},{"style":328},[1758],{"type":29,"value":1759},"    }\n",{"type":23,"tag":322,"props":1761,"children":1762},{"class":324,"line":560},[1763,1768,1772,1777,1782],{"type":23,"tag":322,"props":1764,"children":1765},{"style":413},[1766],{"type":29,"value":1767},"    catch",{"type":23,"tag":322,"props":1769,"children":1770},{"style":328},[1771],{"type":29,"value":1485},{"type":23,"tag":322,"props":1773,"children":1774},{"style":334},[1775],{"type":29,"value":1776},"Exception",{"type":23,"tag":322,"props":1778,"children":1779},{"style":419},[1780],{"type":29,"value":1781}," ex",{"type":23,"tag":322,"props":1783,"children":1784},{"style":328},[1785],{"type":29,"value":1560},{"type":23,"tag":322,"props":1787,"children":1788},{"class":324,"line":568},[1789],{"type":23,"tag":322,"props":1790,"children":1791},{"style":328},[1792],{"type":29,"value":1600},{"type":23,"tag":322,"props":1794,"children":1795},{"class":324,"line":593},[1796,1800,1804,1808,1812,1816,1821,1825,1830,1834,1839,1843,1847],{"type":23,"tag":322,"props":1797,"children":1798},{"style":932},[1799],{"type":29,"value":1659},{"type":23,"tag":322,"props":1801,"children":1802},{"style":328},[1803],{"type":29,"value":1622},{"type":23,"tag":322,"props":1805,"children":1806},{"style":419},[1807],{"type":29,"value":1668},{"type":23,"tag":322,"props":1809,"children":1810},{"style":328},[1811],{"type":29,"value":390},{"type":23,"tag":322,"props":1813,"children":1814},{"style":1675},[1815],{"type":29,"value":1678},{"type":23,"tag":322,"props":1817,"children":1818},{"style":1681},[1819],{"type":29,"value":1820},"[PIM] Read: deserialize error: ",{"type":23,"tag":322,"props":1822,"children":1823},{"style":328},[1824],{"type":29,"value":1689},{"type":23,"tag":322,"props":1826,"children":1827},{"style":1681},[1828],{"type":29,"value":1829},"ex",{"type":23,"tag":322,"props":1831,"children":1832},{"style":328},[1833],{"type":29,"value":1622},{"type":23,"tag":322,"props":1835,"children":1836},{"style":1681},[1837],{"type":29,"value":1838},"Message",{"type":23,"tag":322,"props":1840,"children":1841},{"style":328},[1842],{"type":29,"value":1707},{"type":23,"tag":322,"props":1844,"children":1845},{"style":1675},[1846],{"type":29,"value":1747},{"type":23,"tag":322,"props":1848,"children":1849},{"style":328},[1850],{"type":29,"value":1651},{"type":23,"tag":322,"props":1852,"children":1853},{"class":324,"line":647},[1854,1859,1863,1867,1871,1876,1880,1885],{"type":23,"tag":322,"props":1855,"children":1856},{"style":413},[1857],{"type":29,"value":1858},"        return",{"type":23,"tag":322,"props":1860,"children":1861},{"style":328},[1862],{"type":29,"value":1485},{"type":23,"tag":322,"props":1864,"children":1865},{"style":932},[1866],{"type":29,"value":1490},{"type":23,"tag":322,"props":1868,"children":1869},{"style":328},[1870],{"type":29,"value":1622},{"type":23,"tag":322,"props":1872,"children":1873},{"style":932},[1874],{"type":29,"value":1875},"FormatError",{"type":23,"tag":322,"props":1877,"children":1878},{"style":328},[1879],{"type":29,"value":1500},{"type":23,"tag":322,"props":1881,"children":1882},{"style":349},[1883],{"type":29,"value":1884}," null",{"type":23,"tag":322,"props":1886,"children":1887},{"style":328},[1888],{"type":29,"value":1651},{"type":23,"tag":322,"props":1890,"children":1891},{"class":324,"line":655},[1892],{"type":23,"tag":322,"props":1893,"children":1894},{"style":328},[1895],{"type":29,"value":1759},{"type":23,"tag":322,"props":1897,"children":1898},{"class":324,"line":680},[1899],{"type":23,"tag":322,"props":1900,"children":1901},{"style":893},[1902],{"type":29,"value":1903},"    // ... model.Version に応じて分岐する余地がある\n",{"type":23,"tag":322,"props":1905,"children":1906},{"class":324,"line":736},[1907],{"type":23,"tag":322,"props":1908,"children":1909},{"style":328},[1910],{"type":29,"value":742},{"type":23,"tag":31,"props":1912,"children":1913},{},[1914,1916,1921],{"type":29,"value":1915},"現状PIMはv2で統一されていますが、",{"type":23,"tag":45,"props":1917,"children":1919},{"className":1918},[],[1920],{"type":29,"value":80},{"type":29,"value":1922},"フィールドを持たせているおかげで、いずれv3が必要になったら次のような選択肢を取れます。",{"type":23,"tag":1924,"props":1925,"children":1926},"ol",{},[1927,1952],{"type":23,"tag":228,"props":1928,"children":1929},{},[1930,1936,1937,1943,1945,1950],{"type":23,"tag":45,"props":1931,"children":1933},{"className":1932},[],[1934],{"type":29,"value":1935},"MusicDataModelV2",{"type":29,"value":303},{"type":23,"tag":45,"props":1938,"children":1940},{"className":1939},[],[1941],{"type":29,"value":1942},"MusicDataModelV3",{"type":29,"value":1944},"を別クラスで定義し、読み込み時に",{"type":23,"tag":45,"props":1946,"children":1948},{"className":1947},[],[1949],{"type":29,"value":80},{"type":29,"value":1951},"を見て振り分ける",{"type":23,"tag":228,"props":1953,"children":1954},{},[1955,1960],{"type":23,"tag":45,"props":1956,"children":1958},{"className":1957},[],[1959],{"type":29,"value":294},{"type":29,"value":1961},"を大胆に拡張しつつ、v2データは古い部分だけを読み込むフォールバックを用意する",{"type":23,"tag":31,"props":1963,"children":1964},{},[1965,1967,1972],{"type":29,"value":1966},"ここで効いてくるのが、「",{"type":23,"tag":59,"props":1968,"children":1969},{},[1970],{"type":29,"value":1971},"JSONではなくMessagePackであることの副次効果",{"type":29,"value":1973},"」です。MessagePackは余分なフィールドを自動で無視してくれるので、v3で追加したフィールドを持たないv2データを読んでも壊れません。逆に言うと、v3を書いたものをv2のコードで読むときは、追加フィールドが欠落した状態になります。",{"type":23,"tag":1975,"props":1976,"children":1977},"caution-box",{},[1978],{"type":23,"tag":31,"props":1979,"children":1980},{},[1981,1983,1988,1990,1995,1997,2002],{"type":29,"value":1982},"Versionフィールドを",{"type":23,"tag":45,"props":1984,"children":1986},{"className":1985},[],[1987],{"type":29,"value":845},{"type":29,"value":1989},"に置くのは意図的です。一番最初に来る数値なので、バイナリの先頭数バイトを見るだけで「これはPIM v2だ」と判定できます。ツール側からの識別が楽になるので、新しく独自フォーマットを作るなら",{"type":23,"tag":45,"props":1991,"children":1993},{"className":1992},[],[1994],{"type":29,"value":80},{"type":29,"value":1996},"を",{"type":23,"tag":45,"props":1998,"children":2000},{"className":1999},[],[2001],{"type":29,"value":845},{"type":29,"value":2003},"に置くことを強くお勧めします。",{"type":23,"tag":24,"props":2005,"children":2007},{"id":2006},"readresult-enum-でデシリアライズ失敗を型で表現する",[2008],{"type":29,"value":2009},"ReadResult enum でデシリアライズ失敗を型で表現する",{"type":23,"tag":31,"props":2011,"children":2012},{},[2013,2015,2021,2023,2029],{"type":29,"value":2014},"読み込み結果は単純な",{"type":23,"tag":45,"props":2016,"children":2018},{"className":2017},[],[2019],{"type":29,"value":2020},"Music?",{"type":29,"value":2022},"ではなく、",{"type":23,"tag":45,"props":2024,"children":2026},{"className":2025},[],[2027],{"type":29,"value":2028},"(ReadResult, Music?)",{"type":29,"value":2030},"のタプルで返しています。",{"type":23,"tag":312,"props":2032,"children":2034},{"className":314,"code":2033,"language":316,"meta":7,"style":7},"public enum ReadResult\n{\n    Success,\n    NotFound,\n    FormatError,\n    SoundError,\n}\n",[2035],{"type":23,"tag":45,"props":2036,"children":2037},{"__ignoreMap":7},[2038,2055,2062,2075,2087,2099,2111],{"type":23,"tag":322,"props":2039,"children":2040},{"class":324,"line":17},[2041,2045,2050],{"type":23,"tag":322,"props":2042,"children":2043},{"style":349},[2044],{"type":29,"value":352},{"type":23,"tag":322,"props":2046,"children":2047},{"style":349},[2048],{"type":29,"value":2049}," enum",{"type":23,"tag":322,"props":2051,"children":2052},{"style":334},[2053],{"type":29,"value":2054}," ReadResult\n",{"type":23,"tag":322,"props":2056,"children":2057},{"class":324,"line":345},[2058],{"type":23,"tag":322,"props":2059,"children":2060},{"style":328},[2061],{"type":29,"value":371},{"type":23,"tag":322,"props":2063,"children":2064},{"class":324,"line":365},[2065,2070],{"type":23,"tag":322,"props":2066,"children":2067},{"style":419},[2068],{"type":29,"value":2069},"    Success",{"type":23,"tag":322,"props":2071,"children":2072},{"style":328},[2073],{"type":29,"value":2074},",\n",{"type":23,"tag":322,"props":2076,"children":2077},{"class":324,"line":374},[2078,2083],{"type":23,"tag":322,"props":2079,"children":2080},{"style":419},[2081],{"type":29,"value":2082},"    NotFound",{"type":23,"tag":322,"props":2084,"children":2085},{"style":328},[2086],{"type":29,"value":2074},{"type":23,"tag":322,"props":2088,"children":2089},{"class":324,"line":404},[2090,2095],{"type":23,"tag":322,"props":2091,"children":2092},{"style":419},[2093],{"type":29,"value":2094},"    FormatError",{"type":23,"tag":322,"props":2096,"children":2097},{"style":328},[2098],{"type":29,"value":2074},{"type":23,"tag":322,"props":2100,"children":2101},{"class":324,"line":469},[2102,2107],{"type":23,"tag":322,"props":2103,"children":2104},{"style":419},[2105],{"type":29,"value":2106},"    SoundError",{"type":23,"tag":322,"props":2108,"children":2109},{"style":328},[2110],{"type":29,"value":2074},{"type":23,"tag":322,"props":2112,"children":2113},{"class":324,"line":479},[2114],{"type":23,"tag":322,"props":2115,"children":2116},{"style":328},[2117],{"type":29,"value":742},{"type":23,"tag":31,"props":2119,"children":2120},{},[2121,2123,2129,2131,2136],{"type":29,"value":2122},"最初は",{"type":23,"tag":45,"props":2124,"children":2126},{"className":2125},[],[2127],{"type":29,"value":2128},"try-catch",{"type":29,"value":2130},"で例外を投げる設計だったのですが、 ",{"type":23,"tag":59,"props":2132,"children":2133},{},[2134],{"type":29,"value":2135},"「ユーザーに見せるエラーメッセージを分岐させたい」",{"type":29,"value":2137}," という要件には例外クラスよりenumの方が相性が良いと気付きました。たとえば「フォーマットが壊れている」と「音階の値が不正」では、ユーザーに見せるメッセージが違います。前者は「ファイルが壊れています」で終わりですが、後者は「PICOMのバージョンが古い可能性があります」と案内したい。enumで分岐すればswitch式で網羅チェックも効くので、例外より安全です。",{"type":23,"tag":31,"props":2139,"children":2140},{},[2141,2143,2149],{"type":29,"value":2142},"C#では",{"type":23,"tag":45,"props":2144,"children":2146},{"className":2145},[],[2147],{"type":29,"value":2148},"TryXX",{"type":29,"value":2150},"というメソッドを作ってout引数で結果を返す方法が主流ですが、非同期メソッドでは使えないという問題もあります。PICOMの一部メソッドは非同期で実装されており、できる限りソースコードのパターンを揃えた方が良いという理由もあり、タプル+enumの形で統一しました。",{"type":23,"tag":31,"props":2152,"children":2153},{},[2154],{"type":29,"value":2155},"ただ、この方法にも弱点があり、MusicのNullチェックとReadResultがSuccessかどうかの両方をチェックしないといけないという手間があります。",{"type":23,"tag":24,"props":2157,"children":2159},{"id":2158},"バックアップ機能にも対応複数の曲をまとめて1ファイルに",[2160],{"type":29,"value":2161},"バックアップ機能にも対応！複数の曲をまとめて1ファイルに",{"type":23,"tag":31,"props":2163,"children":2164},{},[2165,2167,2173],{"type":29,"value":2166},"PIMはもう一つ、バックアップ用の",{"type":23,"tag":45,"props":2168,"children":2170},{"className":2169},[],[2171],{"type":29,"value":2172},"BackupDataModel",{"type":29,"value":2174},"を持っています。",{"type":23,"tag":312,"props":2176,"children":2178},{"className":314,"code":2177,"language":316,"meta":7,"style":7},"public byte[] WriteBackup(List\u003CMusic> musics)\n{\n    var backup = new BackupDataModel\n    {\n        Version = 1,\n        CreatedAt = DateTime.UtcNow,\n        Musics = musics.Select(ToDataModel).ToArray(),\n    };\n    return MessagePackSerializer.Serialize(backup);\n}\n",[2179],{"type":23,"tag":45,"props":2180,"children":2181},{"__ignoreMap":7},[2182,2235,2242,2268,2275,2296,2326,2375,2383,2417],{"type":23,"tag":322,"props":2183,"children":2184},{"class":324,"line":17},[2185,2189,2194,2198,2203,2207,2212,2216,2221,2226,2231],{"type":23,"tag":322,"props":2186,"children":2187},{"style":349},[2188],{"type":29,"value":352},{"type":23,"tag":322,"props":2190,"children":2191},{"style":413},[2192],{"type":29,"value":2193}," byte",{"type":23,"tag":322,"props":2195,"children":2196},{"style":328},[2197],{"type":29,"value":695},{"type":23,"tag":322,"props":2199,"children":2200},{"style":419},[2201],{"type":29,"value":2202}," WriteBackup",{"type":23,"tag":322,"props":2204,"children":2205},{"style":328},[2206],{"type":29,"value":390},{"type":23,"tag":322,"props":2208,"children":2209},{"style":334},[2210],{"type":29,"value":2211},"List",{"type":23,"tag":322,"props":2213,"children":2214},{"style":328},[2215],{"type":29,"value":1632},{"type":23,"tag":322,"props":2217,"children":2218},{"style":334},[2219],{"type":29,"value":2220},"Music",{"type":23,"tag":322,"props":2222,"children":2223},{"style":328},[2224],{"type":29,"value":2225},">",{"type":23,"tag":322,"props":2227,"children":2228},{"style":419},[2229],{"type":29,"value":2230}," musics",{"type":23,"tag":322,"props":2232,"children":2233},{"style":328},[2234],{"type":29,"value":1560},{"type":23,"tag":322,"props":2236,"children":2237},{"class":324,"line":345},[2238],{"type":23,"tag":322,"props":2239,"children":2240},{"style":328},[2241],{"type":29,"value":371},{"type":23,"tag":322,"props":2243,"children":2244},{"class":324,"line":365},[2245,2250,2255,2259,2263],{"type":23,"tag":322,"props":2246,"children":2247},{"style":349},[2248],{"type":29,"value":2249},"    var",{"type":23,"tag":322,"props":2251,"children":2252},{"style":419},[2253],{"type":29,"value":2254}," backup",{"type":23,"tag":322,"props":2256,"children":2257},{"style":328},[2258],{"type":29,"value":456},{"type":23,"tag":322,"props":2260,"children":2261},{"style":349},[2262],{"type":29,"value":552},{"type":23,"tag":322,"props":2264,"children":2265},{"style":334},[2266],{"type":29,"value":2267}," BackupDataModel\n",{"type":23,"tag":322,"props":2269,"children":2270},{"class":324,"line":374},[2271],{"type":23,"tag":322,"props":2272,"children":2273},{"style":328},[2274],{"type":29,"value":1600},{"type":23,"tag":322,"props":2276,"children":2277},{"class":324,"line":404},[2278,2283,2287,2292],{"type":23,"tag":322,"props":2279,"children":2280},{"style":932},[2281],{"type":29,"value":2282},"        Version",{"type":23,"tag":322,"props":2284,"children":2285},{"style":328},[2286],{"type":29,"value":456},{"type":23,"tag":322,"props":2288,"children":2289},{"style":393},[2290],{"type":29,"value":2291}," 1",{"type":23,"tag":322,"props":2293,"children":2294},{"style":328},[2295],{"type":29,"value":2074},{"type":23,"tag":322,"props":2297,"children":2298},{"class":324,"line":469},[2299,2304,2308,2313,2317,2322],{"type":23,"tag":322,"props":2300,"children":2301},{"style":932},[2302],{"type":29,"value":2303},"        CreatedAt",{"type":23,"tag":322,"props":2305,"children":2306},{"style":328},[2307],{"type":29,"value":456},{"type":23,"tag":322,"props":2309,"children":2310},{"style":932},[2311],{"type":29,"value":2312}," DateTime",{"type":23,"tag":322,"props":2314,"children":2315},{"style":328},[2316],{"type":29,"value":1622},{"type":23,"tag":322,"props":2318,"children":2319},{"style":932},[2320],{"type":29,"value":2321},"UtcNow",{"type":23,"tag":322,"props":2323,"children":2324},{"style":328},[2325],{"type":29,"value":2074},{"type":23,"tag":322,"props":2327,"children":2328},{"class":324,"line":479},[2329,2334,2338,2342,2346,2351,2355,2360,2365,2370],{"type":23,"tag":322,"props":2330,"children":2331},{"style":932},[2332],{"type":29,"value":2333},"        Musics",{"type":23,"tag":322,"props":2335,"children":2336},{"style":328},[2337],{"type":29,"value":456},{"type":23,"tag":322,"props":2339,"children":2340},{"style":932},[2341],{"type":29,"value":2230},{"type":23,"tag":322,"props":2343,"children":2344},{"style":328},[2345],{"type":29,"value":1622},{"type":23,"tag":322,"props":2347,"children":2348},{"style":419},[2349],{"type":29,"value":2350},"Select",{"type":23,"tag":322,"props":2352,"children":2353},{"style":328},[2354],{"type":29,"value":390},{"type":23,"tag":322,"props":2356,"children":2357},{"style":932},[2358],{"type":29,"value":2359},"ToDataModel",{"type":23,"tag":322,"props":2361,"children":2362},{"style":328},[2363],{"type":29,"value":2364},").",{"type":23,"tag":322,"props":2366,"children":2367},{"style":419},[2368],{"type":29,"value":2369},"ToArray",{"type":23,"tag":322,"props":2371,"children":2372},{"style":328},[2373],{"type":29,"value":2374},"(),\n",{"type":23,"tag":322,"props":2376,"children":2377},{"class":324,"line":504},[2378],{"type":23,"tag":322,"props":2379,"children":2380},{"style":328},[2381],{"type":29,"value":2382},"    };\n",{"type":23,"tag":322,"props":2384,"children":2385},{"class":324,"line":560},[2386,2391,2395,2399,2404,2408,2413],{"type":23,"tag":322,"props":2387,"children":2388},{"style":413},[2389],{"type":29,"value":2390},"    return",{"type":23,"tag":322,"props":2392,"children":2393},{"style":932},[2394],{"type":29,"value":1617},{"type":23,"tag":322,"props":2396,"children":2397},{"style":328},[2398],{"type":29,"value":1622},{"type":23,"tag":322,"props":2400,"children":2401},{"style":419},[2402],{"type":29,"value":2403},"Serialize",{"type":23,"tag":322,"props":2405,"children":2406},{"style":328},[2407],{"type":29,"value":390},{"type":23,"tag":322,"props":2409,"children":2410},{"style":932},[2411],{"type":29,"value":2412},"backup",{"type":23,"tag":322,"props":2414,"children":2415},{"style":328},[2416],{"type":29,"value":1651},{"type":23,"tag":322,"props":2418,"children":2419},{"class":324,"line":568},[2420],{"type":23,"tag":322,"props":2421,"children":2422},{"style":328},[2423],{"type":29,"value":742},{"type":23,"tag":31,"props":2425,"children":2426},{},[2427,2429,2434,2436,2441,2443,2448],{"type":29,"value":2428},"このバックアップの",{"type":23,"tag":45,"props":2430,"children":2432},{"className":2431},[],[2433],{"type":29,"value":80},{"type":29,"value":2435},"は曲単体の",{"type":23,"tag":45,"props":2437,"children":2439},{"className":2438},[],[2440],{"type":29,"value":80},{"type":29,"value":2442},"とは",{"type":23,"tag":59,"props":2444,"children":2445},{},[2446],{"type":29,"value":2447},"別系統",{"type":29,"value":2449},"で管理しています。最初は同じ番号を使っていたのですが、「バックアップ形式だけ変えたい（曲フォーマットは据え置き）」みたいな要件が出て来る可能性を考えて別系統に分けました。",{"type":23,"tag":24,"props":2451,"children":2453},{"id":2452},"追記実際にv3へ上げました",[2454],{"type":29,"value":2455},"追記：実際にv3へ上げました",{"type":23,"tag":31,"props":2457,"children":2458},{},[2459,2461,2467],{"type":29,"value":2460},"この記事を書いた直後に、本文で触れた「いずれv3が必要になったら」が実際に発生しました。PICOMの実装を眺めていて、楽曲IDが「IndexedDBのキー」と「メモリ上の ",{"type":23,"tag":45,"props":2462,"children":2464},{"className":2463},[],[2465],{"type":29,"value":2466},"Music.Id",{"type":29,"value":2468},"」の2箇所に散っており、PIMバイナリ自体はIDを持たない状態だと気付いたのがきっかけです。",{"type":23,"tag":31,"props":2470,"children":2471},{},[2472,2474,2479,2481,2487,2489,2494,2496,2501],{"type":29,"value":2473},"そこで ",{"type":23,"tag":45,"props":2475,"children":2477},{"className":2476},[],[2478],{"type":29,"value":294},{"type":29,"value":2480}," に ",{"type":23,"tag":45,"props":2482,"children":2484},{"className":2483},[],[2485],{"type":29,"value":2486},"[Key(4)] public int Id { get; set; }",{"type":29,"value":2488}," を追加し、",{"type":23,"tag":45,"props":2490,"children":2492},{"className":2491},[],[2493],{"type":29,"value":80},{"type":29,"value":2495}," を ",{"type":23,"tag":45,"props":2497,"children":2499},{"className":2498},[],[2500],{"type":29,"value":673},{"type":29,"value":2502}," に上げました。",{"type":23,"tag":312,"props":2504,"children":2506},{"className":314,"code":2505,"language":316,"meta":7,"style":7},"[MessagePackObject]\npublic class MusicDataModel\n{\n    [Key(0)] public int Version { get; set; } = 3;\n    [Key(1)] public MusicMetadataModel Metadata { get; set; } = new();\n    [Key(2)] public MusicSettingsModel Settings { get; set; } = new();\n    [Key(3)] public TrackDataModel[] Tracks { get; set; } = [];\n    [Key(4)] public int Id { get; set; }\n}\n",[2507],{"type":23,"tag":45,"props":2508,"children":2509},{"__ignoreMap":7},[2510,2525,2540,2547,2619,2690,2761,2832,2892],{"type":23,"tag":322,"props":2511,"children":2512},{"class":324,"line":17},[2513,2517,2521],{"type":23,"tag":322,"props":2514,"children":2515},{"style":328},[2516],{"type":29,"value":331},{"type":23,"tag":322,"props":2518,"children":2519},{"style":334},[2520],{"type":29,"value":337},{"type":23,"tag":322,"props":2522,"children":2523},{"style":328},[2524],{"type":29,"value":342},{"type":23,"tag":322,"props":2526,"children":2527},{"class":324,"line":345},[2528,2532,2536],{"type":23,"tag":322,"props":2529,"children":2530},{"style":349},[2531],{"type":29,"value":352},{"type":23,"tag":322,"props":2533,"children":2534},{"style":349},[2535],{"type":29,"value":357},{"type":23,"tag":322,"props":2537,"children":2538},{"style":334},[2539],{"type":29,"value":362},{"type":23,"tag":322,"props":2541,"children":2542},{"class":324,"line":365},[2543],{"type":23,"tag":322,"props":2544,"children":2545},{"style":328},[2546],{"type":29,"value":371},{"type":23,"tag":322,"props":2548,"children":2549},{"class":324,"line":374},[2550,2554,2558,2562,2566,2570,2574,2578,2582,2586,2590,2594,2598,2602,2606,2610,2615],{"type":23,"tag":322,"props":2551,"children":2552},{"style":328},[2553],{"type":29,"value":380},{"type":23,"tag":322,"props":2555,"children":2556},{"style":334},[2557],{"type":29,"value":385},{"type":23,"tag":322,"props":2559,"children":2560},{"style":328},[2561],{"type":29,"value":390},{"type":23,"tag":322,"props":2563,"children":2564},{"style":393},[2565],{"type":29,"value":396},{"type":23,"tag":322,"props":2567,"children":2568},{"style":328},[2569],{"type":29,"value":920},{"type":23,"tag":322,"props":2571,"children":2572},{"style":349},[2573],{"type":29,"value":925},{"type":23,"tag":322,"props":2575,"children":2576},{"style":413},[2577],{"type":29,"value":416},{"type":23,"tag":322,"props":2579,"children":2580},{"style":419},[2581],{"type":29,"value":422},{"type":23,"tag":322,"props":2583,"children":2584},{"style":328},[2585],{"type":29,"value":427},{"type":23,"tag":322,"props":2587,"children":2588},{"style":349},[2589],{"type":29,"value":432},{"type":23,"tag":322,"props":2591,"children":2592},{"style":328},[2593],{"type":29,"value":437},{"type":23,"tag":322,"props":2595,"children":2596},{"style":349},[2597],{"type":29,"value":442},{"type":23,"tag":322,"props":2599,"children":2600},{"style":328},[2601],{"type":29,"value":437},{"type":23,"tag":322,"props":2603,"children":2604},{"style":328},[2605],{"type":29,"value":451},{"type":23,"tag":322,"props":2607,"children":2608},{"style":328},[2609],{"type":29,"value":456},{"type":23,"tag":322,"props":2611,"children":2612},{"style":393},[2613],{"type":29,"value":2614}," 3",{"type":23,"tag":322,"props":2616,"children":2617},{"style":328},[2618],{"type":29,"value":466},{"type":23,"tag":322,"props":2620,"children":2621},{"class":324,"line":404},[2622,2626,2630,2634,2638,2642,2646,2650,2654,2658,2662,2666,2670,2674,2678,2682,2686],{"type":23,"tag":322,"props":2623,"children":2624},{"style":328},[2625],{"type":29,"value":380},{"type":23,"tag":322,"props":2627,"children":2628},{"style":334},[2629],{"type":29,"value":385},{"type":23,"tag":322,"props":2631,"children":2632},{"style":328},[2633],{"type":29,"value":390},{"type":23,"tag":322,"props":2635,"children":2636},{"style":393},[2637],{"type":29,"value":497},{"type":23,"tag":322,"props":2639,"children":2640},{"style":328},[2641],{"type":29,"value":920},{"type":23,"tag":322,"props":2643,"children":2644},{"style":349},[2645],{"type":29,"value":925},{"type":23,"tag":322,"props":2647,"children":2648},{"style":334},[2649],{"type":29,"value":514},{"type":23,"tag":322,"props":2651,"children":2652},{"style":419},[2653],{"type":29,"value":519},{"type":23,"tag":322,"props":2655,"children":2656},{"style":328},[2657],{"type":29,"value":427},{"type":23,"tag":322,"props":2659,"children":2660},{"style":349},[2661],{"type":29,"value":432},{"type":23,"tag":322,"props":2663,"children":2664},{"style":328},[2665],{"type":29,"value":437},{"type":23,"tag":322,"props":2667,"children":2668},{"style":349},[2669],{"type":29,"value":442},{"type":23,"tag":322,"props":2671,"children":2672},{"style":328},[2673],{"type":29,"value":437},{"type":23,"tag":322,"props":2675,"children":2676},{"style":328},[2677],{"type":29,"value":451},{"type":23,"tag":322,"props":2679,"children":2680},{"style":328},[2681],{"type":29,"value":456},{"type":23,"tag":322,"props":2683,"children":2684},{"style":349},[2685],{"type":29,"value":552},{"type":23,"tag":322,"props":2687,"children":2688},{"style":328},[2689],{"type":29,"value":557},{"type":23,"tag":322,"props":2691,"children":2692},{"class":324,"line":469},[2693,2697,2701,2705,2709,2713,2717,2721,2725,2729,2733,2737,2741,2745,2749,2753,2757],{"type":23,"tag":322,"props":2694,"children":2695},{"style":328},[2696],{"type":29,"value":380},{"type":23,"tag":322,"props":2698,"children":2699},{"style":334},[2700],{"type":29,"value":385},{"type":23,"tag":322,"props":2702,"children":2703},{"style":328},[2704],{"type":29,"value":390},{"type":23,"tag":322,"props":2706,"children":2707},{"style":393},[2708],{"type":29,"value":586},{"type":23,"tag":322,"props":2710,"children":2711},{"style":328},[2712],{"type":29,"value":920},{"type":23,"tag":322,"props":2714,"children":2715},{"style":349},[2716],{"type":29,"value":925},{"type":23,"tag":322,"props":2718,"children":2719},{"style":334},[2720],{"type":29,"value":603},{"type":23,"tag":322,"props":2722,"children":2723},{"style":419},[2724],{"type":29,"value":608},{"type":23,"tag":322,"props":2726,"children":2727},{"style":328},[2728],{"type":29,"value":427},{"type":23,"tag":322,"props":2730,"children":2731},{"style":349},[2732],{"type":29,"value":432},{"type":23,"tag":322,"props":2734,"children":2735},{"style":328},[2736],{"type":29,"value":437},{"type":23,"tag":322,"props":2738,"children":2739},{"style":349},[2740],{"type":29,"value":442},{"type":23,"tag":322,"props":2742,"children":2743},{"style":328},[2744],{"type":29,"value":437},{"type":23,"tag":322,"props":2746,"children":2747},{"style":328},[2748],{"type":29,"value":451},{"type":23,"tag":322,"props":2750,"children":2751},{"style":328},[2752],{"type":29,"value":456},{"type":23,"tag":322,"props":2754,"children":2755},{"style":349},[2756],{"type":29,"value":552},{"type":23,"tag":322,"props":2758,"children":2759},{"style":328},[2760],{"type":29,"value":557},{"type":23,"tag":322,"props":2762,"children":2763},{"class":324,"line":479},[2764,2768,2772,2776,2780,2784,2788,2792,2796,2800,2804,2808,2812,2816,2820,2824,2828],{"type":23,"tag":322,"props":2765,"children":2766},{"style":328},[2767],{"type":29,"value":380},{"type":23,"tag":322,"props":2769,"children":2770},{"style":334},[2771],{"type":29,"value":385},{"type":23,"tag":322,"props":2773,"children":2774},{"style":328},[2775],{"type":29,"value":390},{"type":23,"tag":322,"props":2777,"children":2778},{"style":393},[2779],{"type":29,"value":673},{"type":23,"tag":322,"props":2781,"children":2782},{"style":328},[2783],{"type":29,"value":920},{"type":23,"tag":322,"props":2785,"children":2786},{"style":349},[2787],{"type":29,"value":925},{"type":23,"tag":322,"props":2789,"children":2790},{"style":334},[2791],{"type":29,"value":690},{"type":23,"tag":322,"props":2793,"children":2794},{"style":328},[2795],{"type":29,"value":695},{"type":23,"tag":322,"props":2797,"children":2798},{"style":419},[2799],{"type":29,"value":700},{"type":23,"tag":322,"props":2801,"children":2802},{"style":328},[2803],{"type":29,"value":427},{"type":23,"tag":322,"props":2805,"children":2806},{"style":349},[2807],{"type":29,"value":432},{"type":23,"tag":322,"props":2809,"children":2810},{"style":328},[2811],{"type":29,"value":437},{"type":23,"tag":322,"props":2813,"children":2814},{"style":349},[2815],{"type":29,"value":442},{"type":23,"tag":322,"props":2817,"children":2818},{"style":328},[2819],{"type":29,"value":437},{"type":23,"tag":322,"props":2821,"children":2822},{"style":328},[2823],{"type":29,"value":451},{"type":23,"tag":322,"props":2825,"children":2826},{"style":328},[2827],{"type":29,"value":456},{"type":23,"tag":322,"props":2829,"children":2830},{"style":328},[2831],{"type":29,"value":733},{"type":23,"tag":322,"props":2833,"children":2834},{"class":324,"line":504},[2835,2839,2843,2847,2851,2855,2859,2863,2868,2872,2876,2880,2884,2888],{"type":23,"tag":322,"props":2836,"children":2837},{"style":328},[2838],{"type":29,"value":380},{"type":23,"tag":322,"props":2840,"children":2841},{"style":334},[2842],{"type":29,"value":385},{"type":23,"tag":322,"props":2844,"children":2845},{"style":328},[2846],{"type":29,"value":390},{"type":23,"tag":322,"props":2848,"children":2849},{"style":393},[2850],{"type":29,"value":1361},{"type":23,"tag":322,"props":2852,"children":2853},{"style":328},[2854],{"type":29,"value":920},{"type":23,"tag":322,"props":2856,"children":2857},{"style":349},[2858],{"type":29,"value":925},{"type":23,"tag":322,"props":2860,"children":2861},{"style":413},[2862],{"type":29,"value":416},{"type":23,"tag":322,"props":2864,"children":2865},{"style":419},[2866],{"type":29,"value":2867}," Id",{"type":23,"tag":322,"props":2869,"children":2870},{"style":328},[2871],{"type":29,"value":427},{"type":23,"tag":322,"props":2873,"children":2874},{"style":349},[2875],{"type":29,"value":432},{"type":23,"tag":322,"props":2877,"children":2878},{"style":328},[2879],{"type":29,"value":437},{"type":23,"tag":322,"props":2881,"children":2882},{"style":349},[2883],{"type":29,"value":442},{"type":23,"tag":322,"props":2885,"children":2886},{"style":328},[2887],{"type":29,"value":437},{"type":23,"tag":322,"props":2889,"children":2890},{"style":328},[2891],{"type":29,"value":959},{"type":23,"tag":322,"props":2893,"children":2894},{"class":324,"line":560},[2895],{"type":23,"tag":322,"props":2896,"children":2897},{"style":328},[2898],{"type":29,"value":742},{"type":23,"tag":31,"props":2900,"children":2901},{},[2902,2904,2909,2911,2917,2919,2925,2927,2933],{"type":29,"value":2903},"本文で強調した「",{"type":23,"tag":45,"props":2905,"children":2907},{"className":2906},[],[2908],{"type":29,"value":88},{"type":29,"value":2910},"番号は追記のみ」のルールに従って、既存の ",{"type":23,"tag":45,"props":2912,"children":2914},{"className":2913},[],[2915],{"type":29,"value":2916},"[Key(0)]〜[Key(3)]",{"type":29,"value":2918}," は一切触らず、末尾に ",{"type":23,"tag":45,"props":2920,"children":2922},{"className":2921},[],[2923],{"type":29,"value":2924},"[Key(4)]",{"type":29,"value":2926}," を足すだけで済みました。そして、読み込み時に ",{"type":23,"tag":45,"props":2928,"children":2930},{"className":2929},[],[2931],{"type":29,"value":2932},"model.Version \u003C 3",{"type":29,"value":2934}," かどうかで旧形式かを判定するだけで済み、バイナリ先頭を見れば版が分かる設計のありがたみを感じました。",{"type":23,"tag":31,"props":2936,"children":2937},{},[2938,2940,2945,2947,2952],{"type":29,"value":2939},"記事本文で「",{"type":23,"tag":45,"props":2941,"children":2943},{"className":2942},[],[2944],{"type":29,"value":80},{"type":29,"value":2946},"フィールドは最初から入れる。後付けすると必ず苦労する」と書きましたが、",{"type":23,"tag":59,"props":2948,"children":2949},{},[2950],{"type":29,"value":2951},"自分で書いた記事の主張に後から自分で救われた",{"type":29,"value":2953},"形になりました。バージョン管理対応の思想を最初から仕込んでおく価値は、実際に次の版を作るまで実感しづらいのですが、こうやって発揮される瞬間が必ず来ます。",{"type":23,"tag":24,"props":2955,"children":2957},{"id":2956},"まとめ",[2958],{"type":29,"value":2956},{"type":23,"tag":224,"props":2960,"children":2961},{},[2962,2967,2977,2989],{"type":23,"tag":228,"props":2963,"children":2964},{},[2965],{"type":29,"value":2966},"デバッグ性よりサイズ・バイナリとしての扱いやすさ・フォーマット拡張への耐性が大事なら、JSONではなくMessagePackを選ぶ価値がある",{"type":23,"tag":228,"props":2968,"children":2969},{},[2970,2975],{"type":23,"tag":45,"props":2971,"children":2973},{"className":2972},[],[2974],{"type":29,"value":88},{"type":29,"value":2976},"番号は一度付けたら絶対に変えない、削除もしない",{"type":23,"tag":228,"props":2978,"children":2979},{},[2980,2982,2987],{"type":29,"value":2981},"フォーマットの",{"type":23,"tag":45,"props":2983,"children":2985},{"className":2984},[],[2986],{"type":29,"value":80},{"type":29,"value":2988},"フィールドは最初から入れる。これを後付けすると必ず苦労する",{"type":23,"tag":228,"props":2990,"children":2991},{},[2992],{"type":29,"value":2993},"デシリアライズ結果はenumで分岐できるようにしておくと、ユーザーへのエラーメッセージ設計が楽になる",{"type":23,"tag":2995,"props":2996,"children":2997},"style",{},[2998],{"type":29,"value":2999},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":7,"searchDepth":345,"depth":345,"links":3001},[3002,3003,3007,3008,3010,3011,3012,3013,3014],{"id":26,"depth":345,"text":26},{"id":93,"depth":345,"text":96,"children":3004},[3005,3006],{"id":125,"depth":365,"text":125},{"id":165,"depth":365,"text":168},{"id":280,"depth":345,"text":283},{"id":823,"depth":345,"text":3009},"[Key]番号は追記のみ、絶対に削除しない",{"id":1428,"depth":345,"text":1431},{"id":2006,"depth":345,"text":2009},{"id":2158,"depth":345,"text":2161},{"id":2452,"depth":345,"text":2455},{"id":2956,"depth":345,"text":2956},"markdown","content:articles:tech:blazor:messagepack-pim-format.md","content","articles/tech/blazor/messagepack-pim-format.md","articles/tech/blazor/messagepack-pim-format","md",1776266248598]