Skip to content

Commit f33e798

Browse files
committed
Many improvements, deserialization now is able to ensure all required fields were present.
1 parent df441fd commit f33e798

File tree

4 files changed

+175
-35
lines changed

4 files changed

+175
-35
lines changed

JSONSerialization/JSONSerialization.dproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
<Compile Include="std\collections.d" />
5050
<Compile Include="std\performance\conv.d" />
5151
<Compile Include="std\performance\array.d" />
52+
<Compile Include="std\performance\bitmanip.d" />
5253
</ItemGroup>
5354
<ItemGroup>
5455
<Folder Include="std\" />

JSONSerialization/std/json.d

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -72,20 +72,17 @@ final class JSONSerializationFormat : SerializationFormat
7272
ensurePublicConstructor!T();
7373
output.put('{');
7474
size_t i = 0;
75-
foreach (member; __traits(allMembers, T))
75+
foreach (member; membersToSerialize!T)
7676
{
77-
static if (shouldSerializeMember!(T, member))
78-
{
79-
import std.traitsExt : getMemberValue;
80-
81-
if (!shouldSerializeValue!(T, member)(val))
82-
continue;
83-
if (i != 0)
84-
output.put(',');
85-
output.put(`"` ~ getFinalMemberName!(T, member) ~ `":`);
86-
serialize(output, getMemberValue!member(val));
87-
i++;
88-
}
77+
import std.traitsExt : getMemberValue;
78+
79+
if (!shouldSerializeValue!(T, member)(val))
80+
continue;
81+
if (i != 0)
82+
output.put(',');
83+
output.put(`"` ~ getFinalMemberName!(T, member) ~ `":`);
84+
serialize(output, getMemberValue!member(val));
85+
i++;
8986
}
9087
output.put('}');
9188
}
@@ -592,31 +589,31 @@ final class JSONSerializationFormat : SerializationFormat
592589
}
593590
ensurePublicConstructor!T();
594591
T parsedValue = constructDefault!T();
592+
auto serializedFields = SerializedFieldSet!T();
595593
bool first = true;
596594
parser.expect!(TokenType.LCurl);
597595
parser.consume();
598596
// TODO: Deal with Optional fields, and ensure all required fields
599597
// have been deserialized.
600-
do if (parser.current.type != TokenType.RCurl)
598+
if (parser.current.type != TokenType.RCurl) do
601599
{
602600
if (!first && parser.current.type == TokenType.Comma)
603601
parser.consume();
604-
602+
603+
parser.expect!(TokenType.String);
605604
switch (parser.current.stringValue)
606605
{
607-
foreach (member; __traits(allMembers, T))
606+
foreach (member; membersToSerialize!T)
608607
{
609-
static if (shouldSerializeMember!(T, member))
610-
{
611-
import std.traitsExt : MemberType, setMemberValue;
612-
613-
case getFinalMemberName!(T, member):
614-
parser.consume();
615-
parser.expect!(TokenType.Colon);
616-
parser.consume();
617-
setMemberValue!member(parsedValue, deserializeValue!(MemberType!(T, member))(parser));
618-
goto ExitSwitch;
619-
}
608+
import std.traitsExt : MemberType, setMemberValue;
609+
610+
case getFinalMemberName!(T, member):
611+
parser.consume();
612+
parser.expect!(TokenType.Colon);
613+
parser.consume();
614+
setMemberValue!member(parsedValue, deserializeValue!(MemberType!(T, member))(parser));
615+
serializedFields.markSerialized!(member);
616+
goto ExitSwitch;
620617
}
621618

622619
default:
@@ -629,6 +626,8 @@ final class JSONSerializationFormat : SerializationFormat
629626
} while (parser.current.type == TokenType.Comma);
630627
parser.expect!(TokenType.RCurl);
631628
parser.consume();
629+
630+
serializedFields.ensureFullySerialized();
632631
return parsedValue;
633632
}
634633

@@ -730,8 +729,8 @@ final class JSONSerializationFormat : SerializationFormat
730729

731730
T arrVal;// = Appender!T();
732731
bool first = true;
733-
734-
do if (parser.current.type != TokenType.RSquare)
732+
733+
if (parser.current.type != TokenType.RSquare) do
735734
{
736735
if (!first && parser.current.type == TokenType.Comma)
737736
parser.consume();
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
module std.performance.bitmanip;
2+
3+
import core.bitop : bt, bts, btr;
4+
5+
struct BitArray(int length)
6+
{
7+
enum bitsPerSizeT = size_t.sizeof * 8;
8+
enum dataLength = (length + (bitsPerSizeT - 1)) / bitsPerSizeT;
9+
size_t[dataLength] data;
10+
11+
this(size_t[dataLength] initialData)
12+
{
13+
this.data = initialData;
14+
}
15+
16+
/**********************************************
17+
* Gets the $(D i)'th bit in the $(D BitArray).
18+
*/
19+
bool opIndex(size_t i) const @trusted pure nothrow
20+
in
21+
{
22+
assert(i < length);
23+
}
24+
body
25+
{
26+
return cast(bool)bt(data.ptr, i);
27+
}
28+
29+
/**********************************************
30+
* Sets the $(D i)'th bit in the $(D BitArray).
31+
*/
32+
bool opIndexAssign(bool b, size_t i) @trusted pure nothrow
33+
in
34+
{
35+
assert(i < length);
36+
}
37+
body
38+
{
39+
if (__ctfe)
40+
{
41+
if (b)
42+
data[i / bitsPerSizeT] |= (1 << (i & (bitsPerSizeT - 1)));
43+
else
44+
data[i / bitsPerSizeT] &= ~(1 << (i & (bitsPerSizeT - 1)));
45+
}
46+
else
47+
{
48+
if (b)
49+
bts(data.ptr, i);
50+
else
51+
btr(data.ptr, i);
52+
}
53+
return b;
54+
}
55+
56+
BitArray!length opOpAssign(string op : "&")(BitArray!length a2)
57+
{
58+
this.data[] &= a2.data[];
59+
return this;
60+
}
61+
}

JSONSerialization/std/serialization.d

Lines changed: 85 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,63 @@ abstract class SerializationFormat
2121
;
2222

2323
protected:
24-
enum deserializationContext;
24+
static struct SerializedFieldSet(T)
25+
{
26+
import std.performance.bitmanip : BitArray;
27+
28+
alias members = membersToSerialize!T;
29+
// BUG: Required due to a bug that causes compilation to fail
30+
enum membersLength = members.length;
31+
32+
mixin(genDeclarations());
33+
private static string genDeclarations()
34+
{
35+
import std.conv : to;
36+
import std.performance.array : Appender;
37+
38+
auto ret = Appender!string();
39+
40+
ret.put(`BitArray!`);
41+
ret.put(to!string(membersLength));
42+
ret.put(` fieldMarkers;`);
43+
44+
BitArray!membersLength expectedArr;
45+
foreach (i, m; members)
46+
{
47+
if (!isMemberOptional!(T, m))
48+
expectedArr[i] = true;
49+
}
50+
ret.put(`enum expectedFields = BitArray!`);
51+
ret.put(to!string(membersLength));
52+
ret.put(`([`);
53+
foreach (i, d; expectedArr.data)
54+
{
55+
if (i != 0)
56+
ret.put(',');
57+
ret.put(`0x`);
58+
ret.put(to!string(d, 16));
59+
}
60+
ret.put(`]);`);
61+
62+
return ret.data;
63+
}
64+
65+
@property void markSerialized(string member)() @safe pure nothrow
66+
{
67+
import std.typecons : staticIndexOf;
68+
69+
fieldMarkers[staticIndexOf!(member, members)] = true;
70+
}
2571

72+
void ensureFullySerialized() @safe pure
73+
{
74+
fieldMarkers &= expectedFields;
75+
if (fieldMarkers != expectedFields)
76+
throw new Exception("A required field was not deserialized!");
77+
}
78+
}
79+
80+
enum deserializationContext;
2681
enum isDeserializationContext(T) = hasAttribute!(T, deserializationContext);
2782

2883
template isSerializable(T)
@@ -46,12 +101,38 @@ protected:
46101
else
47102
static assert(0, "Not yet implemented!");
48103
}
104+
105+
template membersToSerialize(T)
106+
{
107+
alias CTValSet(E...) = E;
108+
enum shouldSerializeMember(string member) = member != "this" && isMemberField!(T, member) && !memberHasAttribute!(T, member, nonSerialized);
109+
110+
// TODO: This needs to deal with inherited members that have the same name.
111+
template membersToSerializeImpl(T, Members...)
112+
{
113+
static if (Members.length > 1)
114+
{
115+
static if (shouldSerializeMember!(Members[0]))
116+
alias membersToSerializeImpl = CTValSet!(Members[0], membersToSerializeImpl!(T, Members[1..$]));
117+
else
118+
alias membersToSerializeImpl = membersToSerializeImpl!(T, Members[1..$]);
119+
}
120+
else
121+
{
122+
static if (shouldSerializeMember!(Members[0]))
123+
alias membersToSerializeImpl = CTValSet!(Members[0]);
124+
else
125+
alias membersToSerializeImpl = CTValSet!();
126+
}
127+
}
128+
alias membersToSerialize = membersToSerializeImpl!(T, __traits(allMembers, T));
129+
}
49130

50-
enum shouldSerializeMember(T, string member) = member != "this" && isMemberField!(T, member) && !memberHasAttribute!(T, member, nonSerialized);
131+
enum isMemberOptional(T, string member) = memberHasAttribute!(T, member, optional);
51132

52133
static bool shouldSerializeValue(T, string member)(T val) @safe pure nothrow
53134
{
54-
static if (memberHasAttribute!(T, member, optional))
135+
static if (isMemberOptional!(T, member))
55136
{
56137
if (getDefaultMemberValue!(T, member) == getMemberValue!member(val))
57138
return false;
@@ -77,9 +158,7 @@ protected:
77158
static if (__traits(compiles, (cast(T)T.init).toString()) && __traits(compiles, T.parse("")))
78159
{
79160
// TODO: Support using an output range here as well.
80-
output.put('"');
81-
output.put(val.toString());
82-
output.put('"');
161+
serialize(output, val.toString());
83162
}
84163
else
85164
static assert(0, typeof(this).stringof ~ " does not support serializing " ~ T.stringof ~ "s!");

0 commit comments

Comments
 (0)