1 module yamlserialized.deserialization; 2 3 import std.conv; 4 import std.traits; 5 6 import yamlserialized.types; 7 8 import dyaml; 9 10 /// Deserialize a D-YAML Node into an array T 11 void deserializeInto(T)(Node yamlNode, ref T array) if (isArray!T) { 12 alias ElementType = ForeachType!T; 13 14 // Iterate each item in the array of nodes and add them to values, converting them to the actual type 15 foreach(item; yamlNode.as!(Node[])) { 16 static if (is(ElementType == struct)) { 17 // This item is a struct - instantiate it 18 ElementType newStruct; 19 20 // ...deserialize into the new instance 21 item.deserializeInto(newStruct); 22 23 // ...and add it to the array 24 array ~= newStruct; 25 } 26 else static if (is(ElementType == class)) { 27 // The item type is class - create a new instance 28 auto newClass = new ElementType(); 29 30 // ...deserialize into the new instance 31 item.deserializeInto(newClass); 32 33 // ...and add it to the array 34 array ~= newClass; 35 } 36 else static if (isSomeString!ElementType) { 37 array ~= item.as!string.to!ElementType; 38 } 39 else static if (isArray!ElementType) { 40 // An array of arrays. Recursion time! 41 ElementType subArray; 42 43 item.deserializeInto(subArray); 44 array ~= subArray; 45 } 46 else { 47 array ~= item.as!ElementType; 48 } 49 } 50 } 51 52 /// Deserialize a D-YAML Node into an associative array T 53 void deserializeInto(T)(Node yamlNode, ref T associativeArray) if (isAssociativeArray!T) { 54 alias VType = ValueType!T; 55 56 // Iterate each Pair in the Node 57 foreach(pair; yamlNode.as!(Node.Pair[])) { 58 auto key = pair.key.as!string.to!(KeyType!T); 59 auto value = pair.value; 60 61 static if (isAssociativeArray!VType) { 62 /* The associative array's value type is another associative array type. 63 It's recursion time. */ 64 65 if (key in associativeArray) { 66 value.deserializeInto(associativeArray[key]); 67 } 68 else { 69 VType subAssocArray; 70 71 value.deserializeInto(subAssocArray); 72 associativeArray[key] = subAssocArray; 73 } 74 } 75 else static if (is(VType == struct)) { 76 // The value type is a struct - instantiate it 77 VType newStruct; 78 79 // ...deserialize into the new instance 80 value.deserializeInto(newStruct); 81 82 // ...and add it to the associative array 83 associativeArray[key] = newStruct; 84 } 85 else static if (is(VType == class)) { 86 // The value type is class - create a new instance 87 auto newClass = new VType(); 88 89 // ...deserialize into the new instance 90 value.deserializeInto(newClass); 91 92 // ...and add it to the associative array 93 associativeArray[key] = newClass; 94 } 95 else { 96 associativeArray[key] = value.as!VType; 97 } 98 } 99 } 100 101 /// Deserialize a D-YAML Node into a struct or class of type T 102 void deserializeInto(T)(Node yamlNode, ref T obj) if (is(T == struct) || is(T == class)) { 103 enum fieldNames = FieldNameTuple!T; 104 105 foreach(fieldName; fieldNames) { 106 alias FieldType = typeof(__traits(getMember, obj, fieldName)); 107 108 auto yamlFieldName = fieldName; 109 110 static if (hasUDA!(__traits(getMember, obj, fieldName), YamlField)) { 111 yamlFieldName = getUDAs!(__traits(getMember, obj, fieldName), YamlField)[0].name; 112 } 113 114 if (!yamlNode.containsKey(yamlFieldName)) { 115 continue; 116 } 117 118 static if (is(FieldType == struct)) { 119 // This field is a struct - recurse into it 120 yamlNode[yamlFieldName].deserializeInto(__traits(getMember, obj, fieldName)); 121 } 122 else static if (is(FieldType == class)) { 123 // This field is a class - recurse into it unless it is null 124 if (__traits(getMember, obj, fieldName) !is null) { 125 yamlNode[yamlFieldName].deserializeInto(__traits(getMember, obj, fieldName)); 126 } 127 } 128 else static if (isSomeChar!FieldType) { 129 // Field is a char 130 // Node.as!char fails for some reason, so we have to retrieve it as a string first 131 // and then convert it to the correct type. 132 __traits(getMember, obj, fieldName) = yamlNode[yamlFieldName].as!string.to!FieldType; 133 } 134 else static if (isSomeString!FieldType) { 135 // Field is a string 136 __traits(getMember, obj, fieldName) = yamlNode[yamlFieldName].as!string.to!FieldType; 137 } 138 else static if (isArray!FieldType) { 139 // Field is an array 140 yamlNode[yamlFieldName].deserializeInto(__traits(getMember, obj, fieldName)); 141 } 142 else static if (isAssociativeArray!FieldType) { 143 // Field is an associative array 144 yamlNode[yamlFieldName].deserializeInto(__traits(getMember, obj, fieldName)); 145 } 146 else static if (isIntegral!FieldType) { 147 // Field is an integer 148 if (yamlNode[yamlFieldName].convertsTo!FieldType) { 149 // If node contains an integer value, get it directly 150 __traits(getMember, obj, fieldName) = yamlNode[yamlFieldName].as!FieldType; 151 } 152 else { 153 // If node contains a non-integer value, convert it to a string first and then to the correct type 154 __traits(getMember, obj, fieldName) = yamlNode[yamlFieldName].as!string.to!FieldType; 155 } 156 } 157 else static if (isBoolean!FieldType) { 158 // Convert to string first, then to the correct boolean type. 159 __traits(getMember, obj, fieldName) = yamlNode[yamlFieldName].as!string.to!FieldType; 160 } 161 else { 162 __traits(getMember, obj, fieldName) = yamlNode[yamlFieldName].as!FieldType; 163 } 164 } 165 } 166 167 /// Deserialize a D-YAML Node into a struct of type T 168 T deserializeTo(T)(Node yamlNode) if (is(T == struct)) { 169 T obj; 170 171 yamlNode.deserializeInto(obj); 172 return obj; 173 }