1 module vest.json.tojson; 2 3 import std.json : JSONValue; 4 import std.array : array, empty; 5 import std.algorithm : map, filter; 6 import std.traits : isType, isFunction, isArray, isAssociativeArray, isIterable, isPointer, isSomeChar, isSomeString; 7 import std.traits : Unqual, OriginalType; 8 import std.conv : to; 9 import std.typecons : isTuple; 10 import std.meta : Alias; 11 import std.typecons : tuple; 12 13 // Is field property of T 14 private 15 template isProperty(T, string field) 16 { 17 alias fieldValue = Alias!(__traits(getMember, T, field)); 18 enum isProperty = !isType!(fieldValue) && !isFunction!(fieldValue) && !is(typeof(fieldValue) == void); 19 } 20 21 22 // Check if field is accessible 23 private 24 enum isAccessible(T, string field) = __traits(compiles, __traits(getMember, T, field)); 25 26 private 27 enum isPublic(T, string field) = ("public" == __traits(getProtection, mixin("T." ~ field)) ); 28 29 // Helper function to retrive all structure properties 30 private 31 template _retriveProperties(T, string[] fields) 32 { 33 static if(!fields.length) { 34 enum string[] _retriveProperties = []; 35 } else { 36 static if( isAccessible!(T, fields[0]) && isProperty!(T, fields[0]) ) { 37 enum _retriveProperties = [fields[0]] ~ _retriveProperties!(T, fields[1..$]); 38 } else { 39 enum _retriveProperties = _retriveProperties!(T, fields[1..$]); 40 } 41 } 42 } 43 44 // All structure properties (not members) 45 private 46 enum allProperties(T) = _retriveProperties!(T, [__traits(allMembers, T)]); 47 48 // Retrive associative tuple fields 49 private 50 string[] expandFieldNames(Names...)( Names names) 51 { 52 static if( Names.length ) { 53 return names[0] ~ expandFieldNames(names[1..$]); 54 } else { 55 return []; 56 } 57 } 58 59 // Check if tuple is associative 60 private 61 enum isAssocTuple(alias T) = expandFieldNames(T.fieldNames) 62 .filter!(x => !x.empty) 63 .array 64 .length; 65 66 // Forward JSONValue directly 67 auto toJson()(auto ref JSONValue value) 68 { 69 return value; 70 } 71 72 // Convert to json 73 JSONValue toJson(T)(auto ref T value) 74 { 75 static if( isTuple!T ) { 76 // Tuples 77 static if(isAssocTuple!T) { 78 JSONValue rez; 79 static foreach(enum i, enum subfield; value.fieldNames) { 80 static if(subfield.empty) { 81 rez["_" ~ i.to!string] = toJson(value[i]); 82 } else { 83 rez[subfield] = toJson(value[i]); 84 } 85 } 86 return rez; 87 } else { 88 JSONValue[] rez; 89 foreach(ref v; value.expand) { 90 rez ~= [toJson(v)]; 91 } 92 return JSONValue(rez); 93 } 94 } else static if(is(T == enum)) { 95 // Enums 96 static foreach(enum field; allProperties!T ) { 97 if(value == __traits(getMember, T, field)) { 98 return tuple!("key", "value")(field, cast(OriginalType!T) value) 99 .toJson(); 100 } 101 } 102 assert(0); 103 } else static if( Unqual!T.stringof == "DateTime" ) { 104 // DateTime 105 return JSONValue(value.toISOExtString()); 106 } else static if( isArray!T && !isSomeString!T ) { 107 // Arrays 108 return JSONValue(value.map!(x => x.toJson()).array); 109 } else static if( isAssociativeArray!T ) { 110 // AssociativeArrays 111 JSONValue rez; 112 foreach(subfield, ref v; value) { 113 rez[subfield] = toJson(v); 114 } 115 return rez; 116 } else static if( isIterable!T && !isSomeString!T ) { 117 // Iterable 118 return JSONValue(value.map!(x => x.toJson()).array); 119 } else static if( is(T == struct) ) { 120 // Structures 121 JSONValue rez; 122 static foreach(enum field; allProperties!T ) { 123 rez[field] = toJson(__traits(getMember, value, field)); 124 } 125 return rez; 126 } else static if( isPointer!T ) { 127 // Pointers 128 return (value is null) ? JSONValue() : toJson(*value); 129 } else static if( isSomeChar!T ) { 130 // Chars 131 return JSONValue([value]); 132 } else { 133 // Other 134 return JSONValue(value); 135 } 136 } 137 138 // cd source 139 // rdmd -unittest -main vest/json/tojson 140 unittest { 141 import std.typecons : tuple, Tuple; 142 import std.range : iota; 143 import std.array : array; 144 import std.algorithm : map, equal; 145 import std.json : parseJSON; 146 //import std.stdio : writeln; 147 148 static struct SubNest { 149 size_t q = 100; 150 auto r1 = tuple(5, "55"); 151 auto r2 = tuple!("r2")(55); 152 153 float get_q() const 154 { 155 return q; 156 } 157 } 158 159 SubNest sn; 160 assert(sn.toJson.toString == `{"q":100,"r1":[5,"55"],"r2":{"r2":55}}`); 161 162 auto dic = [ 163 "one" : SubNest(1), 164 "two" : SubNest(2), 165 ]; 166 assert(dic.toJson.toString == `{"one":{"q":1,"r1":[5,"55"],"r2":{"r2":55}},"two":{"q":2,"r1":[5,"55"],"r2":{"r2":55}}}`); 167 168 auto arr1 = [tuple!("fld1", "fld2")(55, 66), tuple!("fld1", "fld2")(77, 88)]; 169 assert(arr1.toJson.toString == `[{"fld1":55,"fld2":66},{"fld1":77,"fld2":88}]`); 170 171 auto arr2 = [tuple(11, 22), tuple(33, 44)]; 172 assert(arr2.toJson.toString == `[[11,22],[33,44]]`); 173 174 int i1 = 15; 175 int *pi1 = &i1; 176 int *pi2; 177 auto arr3 = [pi1, pi2]; 178 assert(arr3.toJson.toString == `[15,null]`); 179 180 auto tpl1 = tuple(true, 'e', 'Ё', "Hi", iota(0, 3)); 181 assert(tpl1.toJson.toString == `[true,"e","Ё","Hi",[0,1,2]]`); 182 183 static struct MyStruct 184 { 185 int i = 5; 186 auto rng1 = iota(0, 3); 187 auto rng2 = iota(4, 6).map!( (int v) {return tuple(v, SubNest(5 * v));} ); 188 189 string str = "Hi"; 190 191 static int q = 55; 192 enum r = 15; 193 194 string[] strs = ["1", "2", "3"]; 195 196 static struct Nest { 197 int x = 11; 198 SubNest[] sns = [SubNest(1), SubNest(2)]; 199 }; 200 Nest nest; 201 202 int * ptr1; 203 int * ptr2; 204 205 SubNest[string] dic; 206 207 bool flag = true; 208 char c1 = 'e'; 209 dchar c2 = 'Ё'; 210 auto tpl1 = tuple(10, "%"); 211 auto tpl2 = tuple!("x", "y", "z")(2, 3, 4); 212 Tuple!(int, "id", string, uint, SubNest) tpl3 = tuple(2, "3", 4, SubNest(101)); 213 214 this(string a){} 215 216 ~this(){} 217 218 int get_i() const 219 { 220 return i; 221 } 222 string get_str() const 223 { 224 return str; 225 } 226 } 227 228 MyStruct mstr; 229 mstr.dic = [ 230 "first" : SubNest(1), 231 "second" : SubNest(2), 232 ]; 233 mstr.ptr2 = &mstr.i; 234 assert(mstr.toJson.toString == `{"c1":"e","c2":"Ё","dic":{"first":{"q":1,"r1":[5,"55"],"r2":{"r2":55}},"second":{"q":2,"r1":[5,"55"],"r2":{"r2":55}}},"flag":true,"i":5,"nest":{"sns":[{"q":1,"r1":[5,"55"],"r2":{"r2":55}},{"q":2,"r1":[5,"55"],"r2":{"r2":55}}],"x":11},"ptr1":null,"ptr2":5,"q":55,"r":15,"rng1":[0,1,2],"rng2":[[4,{"q":20,"r1":[5,"55"],"r2":{"r2":55}}],[5,{"q":25,"r1":[5,"55"],"r2":{"r2":55}}]],"str":"Hi","strs":["1","2","3"],"tpl1":[10,"%"],"tpl2":{"x":2,"y":3,"z":4},"tpl3":{"_1":"3","_2":4,"_3":{"q":101,"r1":[5,"55"],"r2":{"r2":55}},"id":2}}`); 235 236 // Structure has template methods 237 static struct HasTpl 238 { 239 int i = 5; 240 this(string a){} 241 ~this(){} 242 int get_i() const {return i;} 243 int get_itpl()() const {return i;} 244 int get_itpl1(T)(T t) const {return i*t;} 245 } 246 HasTpl htpl; 247 assert(htpl.toJson.toString == `{"i":5}`); 248 249 // Forward json directly 250 assert(`{"q":100,"r":[1,2]}`.parseJSON.toJson.toString == `{"q":100,"r":[1,2]}`); 251 252 // Enums 253 enum kla_t : int {kla1 = 1, kla2, kla3} 254 assert(kla_t.kla2.toJson.toString == `{"key":"kla2","value":2}`); 255 256 enum sta_t : string 257 { 258 sta1 = "sta1_v", 259 sta2 = "sta2_v", 260 sta3 = "sta3_v" 261 } 262 assert(sta_t.sta3.toJson.toString == `{"key":"sta3","value":"sta3_v"}`); 263 }