1 // Written in D the D Programming Language 2 /** 3 任意の構造体やクラスをJSONに変換したり、その逆変換を行う 4 */ 5 module graphite.utils.json; 6 7 import std.algorithm; 8 import std.array; 9 import std.complex; 10 import std.conv; 11 import std.exception; 12 import std.json; 13 import std.process; 14 import std.range; 15 import std.string; 16 import std.traits; 17 import std.typecons; 18 import std.typetuple; 19 import std.variant; 20 21 22 private template _StaticIota(size_t M, size_t N) 23 if(M <= N) 24 { 25 static if(M == N) 26 alias _StaticIota = TypeTuple!(); 27 else 28 alias _StaticIota = TypeTuple!(M, _StaticIota!(M+1, N)); 29 } 30 31 32 class JSONException : Exception 33 { 34 this(string msg, string file = null, size_t line = 0) 35 { 36 super(msg, file, line); 37 } 38 } 39 40 41 template JSONEnv(alias overloads) 42 { 43 /** 44 45 */ 46 void fromJSONValue(T)(JSONValue json, ref T dst) 47 { 48 static if(is(typeof(overloads.fromJSONValueImpl(json, dst)))) 49 overloads.fromJSONValueImpl(json, dst); 50 else static if(is(typeof(T.fromJSONValueImpl(json)) : T)) 51 dst = T.fromJSONValueImpl(json); 52 else 53 fromJSONValueImpl(json, dst); 54 } 55 56 57 /// 58 JSONValue toJSONValue(T)(auto ref T t) 59 { 60 static if(is(typeof(overloads.toJSONValueImpl(t)) == JSONValue)) 61 return overloads.toJSONValueImpl(forward!t); 62 else static if(is(typeof(t.toJSONValueImpl()) == JSONValue)) 63 return t.toJSONValueImpl(); 64 else 65 return toJSONValueImpl(forward!t); 66 } 67 68 69 /// 70 JSONValue toJSONValueImpl(T)(T value) 71 if(is(T == JSONValue)) 72 { 73 return value; 74 } 75 76 77 /// 78 JSONValue toJSONValueImpl(T)(T value) 79 if(is(T == typeof(null))) 80 out(result){ 81 assert(result.type == JSON_TYPE.NULL); 82 } 83 body{ 84 return JSONValue(null); 85 } 86 87 88 /// 89 JSONValue toJSONValueImpl(T)(T value) 90 if(isSomeString!T) 91 out(result){ 92 assert(result.type == JSON_TYPE.STRING); 93 } 94 body{ 95 return JSONValue(value.to!string); 96 } 97 98 99 /// 100 JSONValue toJSONValueImpl(T)(T value) 101 if(isUnsigned!T && isIntegral!T) 102 out(result){ 103 assert(result.type == JSON_TYPE.UINTEGER); 104 } 105 body{ 106 return JSONValue(value); 107 } 108 109 110 /// 111 JSONValue toJSONValueImpl(T)(T value) 112 if(isSigned!T && isIntegral!T) 113 out(result){ 114 assert(result.type == JSON_TYPE.INTEGER); 115 } 116 body{ 117 return JSONValue(value); 118 } 119 120 121 /// 122 JSONValue toJSONValueImpl(T)(T value) 123 if(is(T == bool)) 124 out(result){ 125 assert(result.type == JSON_TYPE.TRUE || result.type == JSON_TYPE.FALSE); 126 } 127 body{ 128 return JSONValue(value); 129 } 130 131 132 /// 133 JSONValue toJSONValueImpl(T)(T value) 134 if(isFloatingPoint!T) 135 out(result){ 136 assert(result.type == JSON_TYPE.FLOAT); 137 } 138 body{ 139 return JSONValue(value); 140 } 141 142 143 /// 144 JSONValue toJSONValueImpl(R)(R range) 145 if(isInputRange!R && !isSomeString!R) 146 out(result){ 147 assert(result.type == JSON_TYPE.ARRAY); 148 } 149 body{ 150 auto app = appender!(JSONValue[])(); 151 152 app.put(range.map!(a => toJSONValue(a))); 153 154 return JSONValue(app.data); 155 } 156 157 158 /// 159 JSONValue toJSONValueImpl(AA)(AA aa) 160 if(isAssociativeArray!AA) 161 out(result){ 162 assert(result.type == JSON_TYPE.OBJECT); 163 } 164 body{ 165 JSONValue[string] dst; 166 foreach(k, v; aa){ 167 static if(is(typeof(k) : string)) 168 dst[k] = toJSONValue(v); 169 else 170 dst[k.to!string()] = toJSONValue(v); 171 } 172 173 return JSONValue(dst); 174 } 175 176 177 /// 178 JSONValue toJSONValueImpl(T)(T v) 179 if(is(T == Variant)) 180 { 181 if(!v.hasValue) 182 return JSONValue(null); 183 else 184 { 185 bool bSuccess; 186 JSONValue jv; 187 188 alias TT = TypeTuple!(Variant, Variant[], Variant[string], Variant[Variant], 189 byte, ubyte, short, ushort, 190 int, uint, long, ulong, 191 float, double, real, 192 string, wstring, dstring, 193 bool, typeof(null)); 194 195 foreach(U; TT){ 196 if(auto p = v.peek!U){ 197 jv = toJSONValue(*p); 198 bSuccess = true; 199 break; 200 } 201 } 202 203 if(!bSuccess) 204 jv = JSONValue(v.to!string()); 205 206 return jv; 207 } 208 } 209 210 211 private string createFromJSONValueExceptionMsg(T)(JSONValue json) 212 { 213 return "cannot convert to '" ~ T.stringof ~ "' from "`"` ~ toJSON(&json) ~ `"`; 214 } 215 216 217 /// 218 void fromJSONValueImpl(T)(JSONValue json, ref T dst) 219 if(is(T == JSONValue)) 220 { 221 dst = json; 222 } 223 224 225 /// 226 void fromJSONValueImpl(T)(JSONValue json, ref T dst) 227 if(is(T == typeof(null))) 228 { 229 enforceEx!JSONException(json.type == JSON_TYPE.NULL, createFromJSONValueExceptionMsg!T(json)); 230 } 231 232 233 /// 234 void fromJSONValueImpl(T)(JSONValue json, ref T dst) 235 if(isSomeString!T) 236 { 237 enforceEx!JSONException(json.type == JSON_TYPE.STRING, createFromJSONValueExceptionMsg!T(json)); 238 dst = json.str.to!T; 239 } 240 241 242 /// 243 void fromJSONValueImpl(T)(JSONValue json, ref T dst) 244 if(isIntegral!T && isUnsigned!T) 245 { 246 enforceEx!JSONException(json.type == JSON_TYPE.UINTEGER || json.type == JSON_TYPE.INTEGER, createFromJSONValueExceptionMsg!T(json)); 247 248 if(json.type == JSON_TYPE.UINTEGER) 249 dst = json.uinteger.to!T(); 250 else 251 dst = json.integer.to!T(); 252 } 253 254 255 /// 256 void fromJSONValueImpl(T)(JSONValue json, ref T dst) 257 if(isIntegral!T && isSigned!T) 258 { 259 enforceEx!JSONException(json.type == JSON_TYPE.INTEGER || json.type == JSON_TYPE.UINTEGER, createFromJSONValueExceptionMsg!T(json)); 260 261 if(json.type == JSON_TYPE.INTEGER) 262 dst = json.integer.to!T(); 263 else 264 dst = json.uinteger.to!T(); 265 } 266 267 268 /// 269 void fromJSONValueImpl(T)(JSONValue json, ref T dst) 270 if(is(T == bool)) 271 { 272 enforceEx!JSONException(json.type == JSON_TYPE.TRUE || json.type == JSON_TYPE.FALSE, createFromJSONValueExceptionMsg!T(json)); 273 dst = json.type == JSON_TYPE.TRUE; 274 } 275 276 277 /// 278 void fromJSONValueImpl(T)(JSONValue json, ref T dst) 279 if(isFloatingPoint!T) 280 { 281 enforceEx!JSONException(json.type == JSON_TYPE.FLOAT 282 || json.type == JSON_TYPE.INTEGER 283 || json.type == JSON_TYPE.UINTEGER, createFromJSONValueExceptionMsg!T(json)); 284 285 if(json.type == JSON_TYPE.FLOAT) 286 dst = json.floating; 287 else if(json.type == JSON_TYPE.INTEGER) 288 dst = json.integer; 289 else 290 dst = json.uinteger; 291 } 292 293 294 /// 295 void fromJSONValueImpl(T)(JSONValue json, ref T dst) 296 if(isArray!T && !isSomeString!T) 297 { 298 enforceEx!JSONException(json.type == JSON_TYPE.ARRAY, createFromJSONValueExceptionMsg!T(json)); 299 300 T data = new T(json.array.length); 301 302 foreach(i, e; json.array){ 303 typeof(data[i]) elem; 304 fromJSONValue(e, elem); 305 data[i] = elem; 306 } 307 308 dst = data; 309 } 310 311 312 /// 313 void fromJSONValueImpl(T)(JSONValue json, ref T dst) 314 if(isInputRange!T && isOutputRange!(T, Unqual!(ElementType!T)) && !isArray!T) 315 { 316 enforceEx!JSONException(json.type == JSON_TYPE.ARRAY, createFromJSONValueExceptionMsg!T(json)); 317 318 foreach(e; json.array){ 319 alias Elem = Unqual!(ElementType!T); 320 Elem elem; 321 fromJSONValue(e, elem); 322 dst.put(elem); 323 } 324 } 325 326 327 /// 328 void fromJSONValueImpl(T)(JSONValue json, ref T dst) 329 if(isAssociativeArray!(T)) 330 { 331 enforceEx!JSONException(json.type == JSON_TYPE.OBJECT, createFromJSONValueExceptionMsg!T(json)); 332 333 alias V = typeof(T.init.values[0]); 334 alias K = typeof(T.init.keys[0]); 335 336 foreach(k, v; json.object){ 337 V elem; 338 fromJSONValue(v, elem); 339 dst[k.to!K] = elem; 340 } 341 } 342 343 344 /// 345 void fromJSONValueImpl(T)(JSONValue json, ref T dst) 346 if(is(T == Variant)) 347 { 348 static void impl(T)(JSONValue json, ref Variant dst) 349 { 350 T v; 351 fromJSONValue(json, v); 352 dst = v; 353 } 354 355 356 final switch(json.type) 357 { 358 case JSON_TYPE.STRING: 359 impl!string(json, dst); 360 return; 361 362 case JSON_TYPE.INTEGER: 363 impl!long(json, dst); 364 return; 365 366 case JSON_TYPE.UINTEGER: 367 impl!ulong(json, dst); 368 return; 369 370 case JSON_TYPE.FLOAT: 371 impl!real(json, dst); 372 return; 373 374 case JSON_TYPE.OBJECT: 375 impl!(Variant[string])(json, dst); 376 return; 377 378 case JSON_TYPE.ARRAY: 379 impl!(Variant[])(json, dst); 380 return; 381 382 case JSON_TYPE.TRUE, JSON_TYPE.FALSE: 383 impl!(bool)(json, dst); 384 return; 385 386 case JSON_TYPE.NULL: 387 impl!(typeof(null))(json, dst); 388 return; 389 } 390 } 391 } 392 393 394 /** 395 任意のユーザー定義型をJSONでのObjectに変換する際に便利なテンプレート 396 */ 397 mixin template JSONObject(fields...) 398 if(fields.length && fields.length % 2 == 0) 399 { 400 JSONValue toJSONValueImpl() @property 401 { 402 JSONValue[string] aa; 403 404 foreach(i; _StaticIota!(0, fields.length)) 405 { 406 static if(i % 2 == 0) 407 { 408 static assert(is(typeof(fields[i]) == string)); 409 410 static if(is(typeof(mixin(fields[i+1])))) 411 aa[fields[i]] = toJSONValue(mixin(fields[i+1])); 412 else 413 aa[fields[i]] = toJSONValue(fields[i+1]); 414 } 415 } 416 417 return JSONValue(aa); 418 } 419 420 421 private ref typeof(this) fromJSONValueImpl_(JSONValue jv) 422 { 423 foreach(i; _StaticIota!(0, fields.length)) 424 { 425 static if(i % 2 == 0) 426 { 427 static if(is(typeof(&(fields[i+1]())) == U*, U)) 428 fromJSONValue(jv.object[fields[i]], fields[i+1]()); 429 else static if(is(typeof(&(fields[i+1])) == U*, U)) 430 fromJSONValue(jv.object[fields[i]], fields[i+1]); 431 else static if(is(typeof(&(mixin(fields[i+1]))) == U*, U) && 432 !is(typeof(&(mixin(fields[i+1]))) == V function(W), V, W...)) 433 { 434 fromJSONValue(jv.object[fields[i]], mixin(fields[i+1])); 435 } 436 else 437 { 438 static if(is(typeof(fields[i+1]()))) // property 439 alias X = typeof({auto x = fields[i+1](); return x;}()); 440 else static if(is(typeof(mixin(fields[i+1])()))) // property(string) 441 alias X = typeof(mixin(fields[i+1])()); 442 else 443 alias X = ParameterTypeTuple!(fields[i+1])[0]; 444 445 X x; 446 fromJSONValue(jv.object[fields[i]], x); 447 448 static if(is(typeof(mixin(fields[i+1])))) 449 mixin(fields[i+1]) = x; 450 else 451 fields[i+1](x); 452 } 453 } 454 } 455 456 457 return this; 458 } 459 460 461 static typeof(this) fromJSONValueImpl(JSONValue jv) 462 { 463 typeof(this) dst; 464 dst.fromJSONValueImpl_(jv); 465 return dst; 466 } 467 } 468 469 470 /// 471 unittest{ 472 // Custom JSON Convertor 473 // ユーザーは、任意の型をJSONへ変換するための変換器を定義できる 474 static struct CustomJSONConvertor 475 { 476 static { mixin JSONEnv!null _dummy; } 477 478 static JSONValue toJSONValueImpl(string str) 479 { 480 return _dummy.toJSONValueImpl("Custom Convertor-String : " ~ str); 481 } 482 483 484 static void fromJSONValueImpl(JSONValue json, ref string str) 485 { 486 assert(json.type == JSON_TYPE.STRING, "Error"); 487 str = json.str.find(" : ").drop(3); 488 } 489 } 490 491 492 // グローバルなsetterとgetterだと仮定 493 static struct Foo 494 { 495 int gloF() @property { return _gloF; } 496 void gloF(int a) @property { _gloF = a; } 497 498 static int _gloF = 12345; 499 } 500 501 502 // グローバル変数だと仮定 503 static string gloG = "global variable"; 504 505 506 // JSONへ変換したり、JSONから変換する対象のオブジェクト 507 static struct S1 508 { 509 // JSON Convertorの定義 510 // 通常はモジュールで定義すればよい 511 static { 512 mixin JSONEnv!CustomJSONConvertor; 513 } 514 515 516 // refで返すプロパティ 517 ref real flt() @property 518 { 519 return _flt; 520 } 521 522 523 // getter 524 int[] arr() @property 525 { 526 return _arr; 527 } 528 529 // setter 530 void arr(int[] arr) @property 531 { 532 _arr = arr; 533 } 534 535 536 // JSONでのオブジェクトの定義 537 mixin JSONObject!("intA", a, // メンバ変数 538 "strB", "b", // メンバ変数(文字列) 539 "fltC", flt, // refを返すメンバ関数(プロパティ) 540 "arrD", "arr", // setter, getter 541 "aasE", "aas", // static setter, getter 542 "gloF", "Foo.init.gloF", // global setter, getter 543 "gloG", gloG, // グローバル変数など外部スコープの変数 544 ); 545 546 547 private: 548 int a; 549 string b; 550 real _flt; 551 int[] _arr; 552 553 554 static: 555 556 // staticなgetter 557 int[int] aas() @property 558 { 559 return [1 : 2, 2 : 3, 3 : 4, 4 : 5]; 560 } 561 562 563 // staticなsetter 564 void aas(int[int] aa) @property 565 {} 566 } 567 568 auto s1 = S1(12, "foo", 2.0, [1, 2, 3, 4]); 569 auto jv = S1.toJSONValue(s1); 570 571 auto jvtext = parseJSON(`{"gloF":12345,"strB":"Custom Convertor-String : foo","fltC":2,"gloG":"Custom Convertor-String : global variable","intA":12,"aasE":{"4":5,"1":2,"2":3,"3":4},"arrD":[1,2,3,4]}`); 572 assert(toJSON(&jv) == toJSON(&jvtext)); 573 574 auto s2 = S1.fromJSONValueImpl(jv); 575 auto s3 = S1.fromJSONValueImpl(jvtext); 576 577 assert(s1 == s2); 578 assert(s2 == s3); 579 580 assert(Foo.init.gloF == 12345); 581 assert(gloG == "global variable"); 582 } 583 584 585 /** 586 JSON Objectを構築します 587 */ 588 struct JSONObjectType(alias jsonEnv, fields...) 589 if(fields.length && isValidJSONObjectTypeFields!fields) 590 { 591 static { 592 mixin JSONEnv!jsonEnv; 593 } 594 595 mixin(genStructField); 596 mixin(`mixin JSONObject!(` ~ genMixinFields ~ `);`); 597 598 private: 599 static: 600 string genStructField() 601 { 602 string dst; 603 604 foreach(i; _StaticIota!(0, fields.length)) 605 { 606 static if(i % 2 == 0) 607 { 608 dst ~= fields[i].stringof; // type 609 dst ~= " "; 610 dst ~= fields[i+1]; // identifier 611 dst ~= ";\n"; 612 } 613 } 614 615 return dst; 616 } 617 618 619 string genMixinFields() 620 { 621 string dst; 622 623 foreach(i; _StaticIota!(0, fields.length)) 624 { 625 static if(i % 2 == 0) 626 { 627 dst ~= `"` ~ fields[i+1] ~ `",`; // tag 628 dst ~= `"` ~ fields[i+1] ~ `",`; // identifier 629 } 630 } 631 632 return dst; 633 } 634 } 635 636 637 unittest{ 638 JSONObjectType!(null, 639 int, "intA", 640 string, "strB", 641 float, "fltC") jot; 642 643 jot.intA = 12; 644 jot.strB = "foo-bar"; 645 jot.fltC = 12.5; 646 647 auto x = jot.toJSONValueImpl(); 648 auto y = parseJSON(`{"strB":"foo-bar","fltC":12.5,"intA":12}`); 649 assert(toJSON(&x) == toJSON(&y)); 650 651 assert(typeof(jot).fromJSONValueImpl(x) == jot); 652 assert(typeof(jot).fromJSONValueImpl(y) == jot); 653 } 654 655 656 private 657 template isValidJSONObjectTypeFields(fields...) 658 { 659 //template isValidImpl(T, string name) { enum isValidImpl = true; } 660 enum isValidImpl_(T, string name) = name.length; 661 enum isValidImpl(X...) = is(typeof({static assert(isValidImpl_!X);})); 662 663 static if(fields.length) 664 { 665 static if(fields.length % 2 == 0) 666 enum isValidJSONObjectTypeFields = isValidImpl!(fields[0 .. 2]) 667 && isValidJSONObjectTypeFields!(fields[2 .. $]); 668 else 669 enum isValidJSONObjectTypeFields = false; 670 } 671 else 672 enum isValidJSONObjectTypeFields = true; 673 }