1 module telega.serialization; 2 3 import std.meta : AliasSeq, staticIndexOf; 4 import asdf : Asdf, serializeValue, serializeToAsdf, serializeToJson, serializedAs, parseJson; 5 6 version (unittest) 7 { 8 import telega.test : assertEquals; 9 } 10 11 string serializeToJsonString(T)(T value) 12 { 13 import std.conv : to; 14 15 Asdf asdf = serializeToAsdf(value); 16 removeNulledNodes(asdf); 17 18 return asdf.to!string; 19 } 20 21 unittest 22 { 23 alias FewTypes = AliasSeq!(int, string); 24 25 struct S 26 { 27 JsonableAlgebraicProxy!FewTypes a; 28 } 29 30 S s; 31 s.a = 42; 32 33 s.serializeToJson() 34 .assertEquals(`{"a":42}`); 35 36 s.a = "123"; 37 s.serializeToJson() 38 .assertEquals(`{"a":"123"}`); 39 } 40 41 /** 42 * Proxy struct for serializing string enums as their values, not key names 43 */ 44 @serializedAs!string 45 struct SerializableEnumProxy(T) 46 if (is(T : string)) 47 { 48 T e; 49 50 this(T e) 51 { 52 this.e = e; 53 } 54 55 string toString() 56 { 57 return cast(string)e; 58 } 59 } 60 61 version (unittest) 62 { 63 @serializedAs!(SerializableEnumProxy!E) 64 static enum E : string 65 { 66 Val1 = "value_1" 67 } 68 } 69 70 unittest 71 { 72 E e = E.Val1; 73 74 assertEquals(e.serializeToJson(), `"value_1"`); 75 } 76 77 void removeNulledNodes(ref Asdf a) 78 { 79 if (a.kind == Asdf.Kind.null_) { 80 a.remove(); 81 } else if (a.kind == Asdf.Kind.array) { 82 foreach (v; a.byElement) { 83 removeNulledNodes(v); 84 } 85 } else if (a.kind == Asdf.Kind.object) { 86 foreach (kv; a.byKeyValue) { 87 removeNulledNodes(kv.value); 88 } 89 } 90 } 91 92 unittest 93 { 94 import std.conv: to; 95 96 string jsonWithNulls = `{ 97 "chat_id":"100000001", 98 "text":"o", 99 "reply_markup":{ 100 "keyboard":[ 101 [ 102 {"text":"First Button","request_contact":null,"request_location":null}, 103 {"text":"Second Button","request_contact":null,"request_location":null} 104 ], 105 [ 106 {"text":"Ask location","request_contact":false,"request_location":true} 107 ], 108 [ 109 {"text":"Remove Keyboard","request_contact":null,"request_location":null} 110 ] 111 ], 112 "resize_keyboard":false, 113 "selective":false, 114 "one_time_keyboard":false 115 } 116 }`; 117 const string cleanJson = `{ 118 "chat_id":"100000001", 119 "text":"o", 120 "reply_markup":{ 121 "keyboard":[ 122 [ 123 {"text":"First Button"}, 124 {"text":"Second Button"} 125 ], 126 [ 127 {"text":"Ask location","request_contact":false,"request_location":true} 128 ], 129 [ 130 {"text":"Remove Keyboard"} 131 ] 132 ], 133 "resize_keyboard":false, 134 "selective":false, 135 "one_time_keyboard":false 136 } 137 }`; 138 139 Asdf asdf = jsonWithNulls.parseJson(); 140 141 removeNulledNodes(asdf); 142 143 asdf.to!string 144 .assertEquals(cleanJson.parseJson.to!string); 145 } 146 147 struct JsonableAlgebraicProxy(Typelist ...) 148 { 149 import std.variant : Algebraic; 150 151 private Algebraic!Typelist value; 152 153 this(T)(T value) 154 if (staticIndexOf!(T, Typelist) >= 0) 155 { 156 opAssign(value); 157 } 158 159 void opAssign(T)(T value) 160 if (staticIndexOf!(T, Typelist) >= 0) 161 { 162 this.value = value; 163 } 164 165 static Algebraic!Typelist deserialize(Asdf data) 166 { 167 assert(false, "Deserialization of a value is not implemented."); 168 } 169 170 void serialize(S)(ref S serializer) 171 { 172 if (!value.hasValue) { 173 serializer.putValue(null); 174 175 return; 176 } 177 178 static foreach (T; Typelist) { 179 if (value.type == typeid(T)) { 180 T result = cast(T)value.get!T; 181 182 serializer.serializeValue(result); 183 } 184 } 185 } 186 } 187 188 unittest 189 { 190 struct A { 191 int val; 192 } 193 194 alias AProxy = JsonableAlgebraicProxy!A; 195 196 AProxy[] aelements; 197 198 assert(__traits(compiles, aelements ~= cast(AProxy)A(3))); 199 }