インデント対応
諏訪子 <suwako@076.moe>、2026-06-30 02:01
コミット 87be4c0e1a4f35cd36d43878637a11e2c26ef19a
・親 0f739e4
・ファイルを見る
4個のファイル、+126 -50
| @@ -41,6 +41,11 @@ | ||
| 41 | 41 | UnterminatedString, |
| 42 | 42 | }; // enum class Error |
| 43 | 43 | |
| 44 | + struct SerializeOptions { | |
| 45 | + bool pretty = false; | |
| 46 | + size_t indent = 2; | |
| 47 | + }; | |
| 48 | + | |
| 44 | 49 | struct ParseResult; |
| 45 | 50 | struct StringHash; |
| 46 | 51 | class Object; |
| @@ -142,14 +147,15 @@ | ||
| 142 | 147 | static ParseResult parse(std::string_view jsonText); |
| 143 | 148 | |
| 144 | 149 | public: |
| 145 | - string serialize() const; | |
| 146 | - bool serialize_to_file(const string &path); | |
| 150 | + string serialize(const SerializeOptions &opts = {}) const; | |
| 151 | + bool serialize_to_file(const string &path, const SerializeOptions &opts = {}) const; | |
| 147 | 152 | |
| 148 | 153 | private: |
| 149 | - static string serialize_number(f64 n); | |
| 154 | + static string make_indent(size_t depth, size_t width); | |
| 155 | + | |
| 156 | + private: | |
| 157 | + string serialize_impl(const SerializeOptions &opts, size_t depth) const; | |
| 150 | 158 | static string serialize_string(const string &s); |
| 151 | - static string serialize_array(const Array &arr); | |
| 152 | - static string serialize_object(const Object &obj); | |
| 153 | 159 | |
| 154 | 160 | private: |
| 155 | 161 | Variant m_Value; |
変更
src/file.cc
| @@ -28,7 +28,8 @@ | ||
| 28 | 28 | |
| 29 | 29 | vector<u8> read_file(const string &filename, bool binary) { |
| 30 | 30 | std::ios::openmode flags = std::ios::in | std::ios::ate; |
| 31 | - if (binary) flags |= std::ios::binary; | |
| 31 | + //if (binary) flags |= std::ios::binary; | |
| 32 | + flags |= std::ios::binary; | |
| 32 | 33 | |
| 33 | 34 | std::ifstream file(filename, flags); |
| 34 | 35 | if (!file) { |
| @@ -36,7 +37,9 @@ | ||
| 36 | 37 | } |
| 37 | 38 | |
| 38 | 39 | auto size = file.tellg(); |
| 40 | + file.clear(); | |
| 39 | 41 | file.seekg(0, std::ios::beg); |
| 42 | + | |
| 40 | 43 | vector<u8> data(static_cast<size_t>(size)); |
| 41 | 44 | if (!file.read(reinterpret_cast<char *>(data.data()), size)) { |
| 42 | 45 | throw std::runtime_error("ファイルの読み込みに失敗。"); |
変更
src/json.cc
| @@ -224,25 +224,88 @@ | ||
| 224 | 224 | return Parser(content).parse(); |
| 225 | 225 | } |
| 226 | 226 | |
| 227 | - string Value::serialize() const { | |
| 228 | - if (is_null()) return "null"; | |
| 229 | - if (is_bool()) return as_bool() ? "true" : "false"; | |
| 230 | - if (is_number()) return serialize_number(as_number()); | |
| 231 | - if (is_string()) return serialize_string(as_string()); | |
| 232 | - if (is_array()) return serialize_array(as_array()); | |
| 233 | - if (is_object()) return serialize_object(as_object()); | |
| 227 | + string Value::serialize(const SerializeOptions &opts) const { | |
| 228 | + return serialize_impl(opts, 0); | |
| 229 | + } | |
| 230 | + | |
| 231 | + string Value::serialize_impl(const SerializeOptions &opts, size_t depth) const { | |
| 232 | + if (is_null()) return "null"; | |
| 233 | + if (is_bool()) return as_bool() ? "true" : "false"; | |
| 234 | + if (is_number()) { | |
| 235 | + std::ostringstream oss; | |
| 236 | + oss << as_number(); | |
| 237 | + return oss.str(); | |
| 238 | + } | |
| 239 | + if (is_string()) | |
| 240 | + return serialize_string(as_string()); | |
| 234 | 241 | |
| 242 | + if (is_array()) { | |
| 243 | + const auto &arr = as_array(); | |
| 244 | + | |
| 245 | + string out = "["; | |
| 246 | + if (opts.pretty && !arr.empty()) | |
| 247 | + out += "\n"; | |
| 248 | + | |
| 249 | + for (size_t i = 0; i < arr.size(); ++i) { | |
| 250 | + if (opts.pretty) | |
| 251 | + out += make_indent(depth + 1, opts.indent); | |
| 252 | + | |
| 253 | + out += arr[i].serialize_impl(opts, depth + 1); | |
| 254 | + | |
| 255 | + if (i + 1 < arr.size()) | |
| 256 | + out += ","; | |
| 257 | + | |
| 258 | + if (opts.pretty) | |
| 259 | + out += "\n"; | |
| 260 | + } | |
| 261 | + | |
| 262 | + if (opts.pretty && !arr.empty()) | |
| 263 | + out += make_indent(depth, opts.indent); | |
| 264 | + | |
| 265 | + out += "]"; | |
| 266 | + return out; | |
| 267 | + } | |
| 268 | + | |
| 269 | + if (is_object()) { | |
| 270 | + const auto &obj = as_object(); | |
| 271 | + | |
| 272 | + string out = "{"; | |
| 273 | + | |
| 274 | + if (opts.pretty && !obj.empty()) | |
| 275 | + out += "\n"; | |
| 276 | + | |
| 277 | + size_t i = 0; | |
| 278 | + for (const auto &[key, val] : obj) { | |
| 279 | + if (opts.pretty) | |
| 280 | + out += make_indent(depth + 1, opts.indent); | |
| 281 | + | |
| 282 | + out += serialize_string(key); | |
| 283 | + out += opts.pretty ? ": " : ":"; | |
| 284 | + out += val.serialize_impl(opts, depth + 1); | |
| 285 | + | |
| 286 | + if (++i < obj.size()) | |
| 287 | + out += ","; | |
| 288 | + | |
| 289 | + if (opts.pretty) | |
| 290 | + out += "\n"; | |
| 291 | + } | |
| 292 | + | |
| 293 | + if (opts.pretty && !obj.empty()) | |
| 294 | + out += make_indent(depth, opts.indent); | |
| 295 | + | |
| 296 | + out += "}"; | |
| 297 | + return out; | |
| 298 | + } | |
| 299 | + | |
| 235 | 300 | return "null"; |
| 236 | 301 | } |
| 237 | 302 | |
| 238 | - bool Value::serialize_to_file(const string &path) { | |
| 239 | - return write_file(path, { serialize() }, false); | |
| 303 | + bool Value::serialize_to_file(const string &path, const SerializeOptions &opts) const { | |
| 304 | + return write_file(path, { serialize(opts) }, false); | |
| 240 | 305 | } |
| 241 | 306 | |
| 242 | - string Value::serialize_number(f64 n) { | |
| 243 | - std::ostringstream oss; | |
| 244 | - oss << n; | |
| 245 | - return oss.str(); | |
| 307 | + string Value::make_indent(size_t depth, size_t width) { | |
| 308 | + return string(depth * width, ' '); | |
| 246 | 309 | } |
| 247 | 310 | |
| 248 | 311 | string Value::serialize_string(const string &s) { |
| @@ -260,34 +323,6 @@ | ||
| 260 | 323 | } |
| 261 | 324 | |
| 262 | 325 | out += "\""; |
| 263 | - return out; | |
| 264 | - } | |
| 265 | - | |
| 266 | - string Value::serialize_array(const Array &arr) { | |
| 267 | - string out = "["; | |
| 268 | - | |
| 269 | - for (size_t i = 0; i < arr.size(); ++i) { | |
| 270 | - if (i > 0) out += ","; | |
| 271 | - out += arr[i].serialize(); | |
| 272 | - } | |
| 273 | - | |
| 274 | - out += "]"; | |
| 275 | - return out; | |
| 276 | - } | |
| 277 | - | |
| 278 | - string Value::serialize_object(const Object &obj) { | |
| 279 | - string out = "{"; | |
| 280 | - bool first = true; | |
| 281 | - | |
| 282 | - for (const auto &[key, val] : obj) { | |
| 283 | - if (!first) out += ","; | |
| 284 | - first = false; | |
| 285 | - out += serialize_string(key); | |
| 286 | - out += ":"; | |
| 287 | - out += val.serialize(); | |
| 288 | - } | |
| 289 | - | |
| 290 | - out += "}"; | |
| 291 | 326 | return out; |
| 292 | 327 | } |
| 293 | 328 |
| @@ -40,10 +40,20 @@ | ||
| 40 | 40 | |
| 41 | 41 | auto out = res.value->serialize(); |
| 42 | 42 | std::cout << out << std::endl; |
| 43 | + auto out2 = res.value->serialize({ .pretty = true, .indent = 2 }); | |
| 44 | + std::cout << out2 << std::endl; | |
| 45 | + auto out3 = res.value->serialize({ .pretty = true, .indent = 8 }); | |
| 46 | + std::cout << out3 << std::endl; | |
| 43 | 47 | |
| 44 | 48 | auto res2 = json::Parser(out).parse(); |
| 45 | 49 | assert(res2.error == json::Error::None); |
| 46 | 50 | |
| 51 | + auto res3 = json::Parser(out2).parse(); | |
| 52 | + assert(res3.error == json::Error::None); | |
| 53 | + | |
| 54 | + auto res4 = json::Parser(out3).parse(); | |
| 55 | + assert(res4.error == json::Error::None); | |
| 56 | + | |
| 47 | 57 | std::cout << "Memory OK" << std::endl; |
| 48 | 58 | } |
| 49 | 59 | |
| @@ -76,10 +86,14 @@ | ||
| 76 | 86 | assert(subject->as_string() == "acct:suwako@sns.076.moe"); |
| 77 | 87 | |
| 78 | 88 | auto out = res.value->serialize_to_file("webfinger2.json"); |
| 89 | + auto out2 = res.value->serialize_to_file("webfinger3.json", { .pretty = true, .indent = 2 }); | |
| 79 | 90 | |
| 80 | 91 | auto res2 = json::Parser::parse_from_file("webfinger2.json"); |
| 81 | 92 | assert(res2.error == json::Error::None); |
| 82 | 93 | |
| 94 | + auto res3 = json::Parser::parse_from_file("webfinger3.json"); | |
| 95 | + assert(res3.error == json::Error::None); | |
| 96 | + | |
| 83 | 97 | std::cout << "WebFinger OK" << std::endl; |
| 84 | 98 | } |
| 85 | 99 | |
| @@ -105,7 +119,7 @@ | ||
| 105 | 119 | assert(scene.get("nodes")->is_array()); |
| 106 | 120 | auto &sceneNodes = scene.get("nodes")->as_array(); |
| 107 | 121 | assert(sceneNodes.size() == 1); |
| 108 | - assert(sceneNodes[0].is_number() == 0); | |
| 122 | + assert(sceneNodes[0].as_number() == 0); | |
| 109 | 123 | } |
| 110 | 124 | |
| 111 | 125 | { |
| @@ -115,7 +129,7 @@ | ||
| 115 | 129 | assert(arr[0].is_object()); |
| 116 | 130 | auto &node = arr[0].as_object(); |
| 117 | 131 | |
| 118 | - assert(node.get("mesh")->is_number() == 0); | |
| 132 | + assert(node.get("mesh")->as_number() == 0); | |
| 119 | 133 | } |
| 120 | 134 | |
| 121 | 135 | { |
| @@ -123,7 +137,7 @@ | ||
| 123 | 137 | auto &arr = buffers->as_array(); |
| 124 | 138 | assert(arr.size() == 1); |
| 125 | 139 | assert(arr[0].is_object()); |
| 126 | - assert(arr[0].as_object().get("uri")->as_string() == "data:application/octet-stream;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAA"); | |
| 140 | + assert(arr[0].as_object().get("uri")->as_string() == "data:application/octet-stream;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAA="); | |
| 127 | 141 | assert(arr[0].as_object().get("byteLength")->as_number() == 44); |
| 128 | 142 | } |
| 129 | 143 | |
| @@ -185,10 +199,14 @@ | ||
| 185 | 199 | } |
| 186 | 200 | |
| 187 | 201 | auto out = res.value->serialize_to_file("gltf2.json"); |
| 202 | + auto out2 = res.value->serialize_to_file("gltf3.json", {.pretty = true, .indent = 2}); | |
| 188 | 203 | |
| 189 | 204 | auto res2 = json::Parser::parse_from_file("gltf2.json"); |
| 190 | 205 | assert(res2.error == json::Error::None); |
| 191 | 206 | |
| 207 | + auto res3 = json::Parser::parse_from_file("gltf3.json"); | |
| 208 | + assert(res3.error == json::Error::None); | |
| 209 | + | |
| 192 | 210 | std::cout << "glTF OK" << std::endl; |
| 193 | 211 | } |
| 194 | 212 | |
| @@ -236,12 +254,26 @@ | ||
| 236 | 254 | assert(pubkey.get("publicKeyPem")->as_string() == "-----BEGIN PUBLIC KEY-----\n...\n----END PUBLIC KEY----"); |
| 237 | 255 | |
| 238 | 256 | auto out = res.value->serialize_to_file("activitypub2.json"); |
| 257 | + auto out2 = res.value->serialize_to_file("activitypub3.json", { .pretty = true, .indent = 2 }); | |
| 239 | 258 | |
| 240 | 259 | auto res2 = json::Parser::parse_from_file("activitypub2.json"); |
| 241 | 260 | assert(res2.error == json::Error::None); |
| 242 | 261 | |
| 262 | + auto res3 = json::Parser::parse_from_file("activitypub3.json"); | |
| 263 | + assert(res3.error == json::Error::None); | |
| 264 | + | |
| 243 | 265 | std::cout << "ActivityPub OK" << std::endl; |
| 244 | 266 | } |
| 267 | + | |
| 268 | + //{ | |
| 269 | + // const string src = R"({ | |
| 270 | + // "a": [,,,])"; | |
| 271 | + | |
| 272 | + // auto res = json::Parser(src).parse(); | |
| 273 | + // assert(res.error == json::Error::SyntaxError); | |
| 274 | + | |
| 275 | + // std::cout << "Bad OK" << std::endl; | |
| 276 | + //} | |
| 245 | 277 | |
| 246 | 278 | return 0; |
| 247 | 279 | } |