Skip to content

Commit 4d2eb7f

Browse files
authored
Merge pull request #57 from gulugulubing/officialPolicy
add some official policy
2 parents adb85e2 + a716bc8 commit 4d2eb7f

File tree

1 file changed

+90
-87
lines changed

1 file changed

+90
-87
lines changed

source/iopipe/json/serialize.d

Lines changed: 90 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -31,23 +31,44 @@ import std.typecons : Nullable;
3131
import std.conv;
3232
import std.format;
3333

34-
struct DefaultDeserializationPolicy {
34+
struct DefaultDeserializationPolicy(bool caseInsensitive = false) {
3535
ReleasePolicy relPol = ReleasePolicy.afterMembers; // default policy
3636

37-
// Handles a field (returns true if handled)
37+
this(ReleasePolicy relPol) {
38+
this.relPol = relPol;
39+
}
40+
3841
void onField(JT, T, C)(
3942
ref JT tokenizer,
4043
ref T item,
4144
JSONItem key,
4245
ref C context
4346
) {
44-
.onField(this, tokenizer, item, key, context);
47+
.onField!caseInsensitive(this, tokenizer, item, key, context);
4548
if(relPol == ReleasePolicy.afterMembers) {
4649
tokenizer.releaseParsed();
4750
}
4851
}
4952
}
5053

54+
55+
unittest
56+
{
57+
58+
static struct S {
59+
string userName;
60+
@alternateName("pet") string dogName;
61+
int age;
62+
}
63+
auto jsonStr = `{"username": "Alice", "PET": "Buddy", "AGE": 30}`;
64+
auto policy = DefaultDeserializationPolicy!true();
65+
auto s = deserialize!S(jsonStr, policy);
66+
assert(s.userName == "Alice");
67+
assert(s.dogName == "Buddy");
68+
assert(s.age == 30);
69+
}
70+
71+
5172
// shim for policies that do not specify a release policy
5273
private ReleasePolicy relPol(P)(ref P policy) => ReleasePolicy.afterMembers;
5374

@@ -85,7 +106,7 @@ auto onArrayBegin(P, JT, T)(ref P policy, ref JT tokenizer, ref T item)
85106
return ubyte.init;
86107
}
87108

88-
void onField(P, JT, T, C)(ref P policy, ref JT tokenizer, ref T item, JSONItem key, ref C context) {
109+
void onField(bool caseInsensitive = false, P, JT, T, C)(ref P policy, ref JT tokenizer, ref T item, JSONItem key, ref C context) {
89110
static if(is(T == V[K], V, K)) {
90111
// convert key into K, forcing a copy. We must copy because this key needs to exist forever.
91112
auto k = extractString!(K, true)(key, tokenizer.chain);
@@ -152,6 +173,25 @@ void onField(P, JT, T, C)(ref P policy, ref JT tokenizer, ref T item, JSONItem k
152173
}
153174

154175
default:
176+
static if(caseInsensitive)
177+
{
178+
// Case-insensitive comparison
179+
import std.string : icmp;
180+
auto keyStr = key.data(tokenizer.chain);
181+
static foreach(i, memberName; members) {
182+
{
183+
static if(hasUDA!(__traits(getMember, T, memberName), alternateName)) {
184+
enum jsonName = getUDAs!(__traits(getMember, T, memberName), alternateName)[0].name;
185+
}
186+
else {
187+
enum jsonName = memberName;
188+
}
189+
if (icmp(keyStr, jsonName) == 0) {
190+
goto case jsonName;
191+
}
192+
}
193+
}
194+
}
155195
static if(ignoreExtras)
156196
{
157197
tokenizer.skipItem();
@@ -163,7 +203,7 @@ void onField(P, JT, T, C)(ref P policy, ref JT tokenizer, ref T item, JSONItem k
163203
// recurse with the JSONValue as the object. Note the change in
164204
// context, as the AA field deserializer takes a ubyte.
165205
ubyte _fakeContext;
166-
onField(policy, tokenizer, __traits(getMember, item, extrasMember).object, key, _fakeContext);
206+
onField!false(policy, tokenizer, __traits(getMember, item, extrasMember).object, key, _fakeContext);
167207
} else {
168208
// If we get here, it's truly an unknown field
169209
throw new JSONIopipeException(format("No member named '%s' in type `%s`", key.data(tokenizer.chain), T.stringof));
@@ -653,7 +693,7 @@ OBJ_MEMBER_SWITCH:
653693
else
654694
enum jsonName = m;
655695
case jsonName:
656-
auto policy = DefaultDeserializationPolicy(relPol);
696+
auto policy = DefaultDeserializationPolicy!false(relPol);
657697
policy.deserializeImpl(tokenizer, __traits(getMember, item, m));
658698
visited[i] = true;
659699
break OBJ_MEMBER_SWITCH;
@@ -685,7 +725,7 @@ OBJ_MEMBER_SWITCH:
685725
JSONValue!SType newItem;
686726
// Need to save name() before deserializeImpl potentially calls release and invalidates the window
687727
auto key = name().to!(immutable(SType));
688-
auto policy = DefaultDeserializationPolicy(relPol);
728+
auto policy = DefaultDeserializationPolicy!false(relPol);
689729
policy.deserializeImpl(tokenizer, newItem);
690730
__traits(getMember, item, extrasMember).object[key] = newItem;
691731
break OBJ_MEMBER_SWITCH;
@@ -1279,6 +1319,47 @@ unittest
12791319
assert(trailingCommaArray[2] == 3);
12801320
}
12811321

1322+
unittest
1323+
{
1324+
import std.datetime.date;
1325+
// Test custom handling of a type with a policy.
1326+
1327+
static struct DTStringPolicy {
1328+
void deserializeImpl(JT, T)(ref JT tokenizer, ref T item) {
1329+
static if (is(T == DateTime))
1330+
{
1331+
// Deserialize DateTime from a string
1332+
auto jsonItem = tokenizer.nextSignificant
1333+
.jsonExpect(JSONToken.String, "Parsing DateTime");
1334+
item = DateTime.fromSimpleString(extractString!string(jsonItem, tokenizer.chain));
1335+
}
1336+
else {
1337+
// default to module behavior
1338+
.deserializeImpl(this, tokenizer, item);
1339+
};
1340+
}
1341+
}
1342+
1343+
auto pol = DTStringPolicy();
1344+
auto dt = deserialize!DateTime(`"2001-Jan-01 12:00:00"`, pol);
1345+
assert(dt == DateTime(2001, 1, 1, 12, 0, 0));
1346+
1347+
static struct Person {
1348+
string name;
1349+
int age;
1350+
DateTime dob;
1351+
}
1352+
1353+
auto jsonStr = `{
1354+
"name": "John Doe",
1355+
"age": 30,
1356+
"dob" : "2001-Jan-01 12:00:00"
1357+
}`;
1358+
auto p = deserialize!Person(jsonStr, pol);
1359+
assert(p.name == "John Doe");
1360+
assert(p.age == 30);
1361+
assert(p.dob == DateTime(2001, 1, 1, 12, 0, 0));
1362+
}
12821363

12831364
unittest
12841365
{
@@ -1334,42 +1415,6 @@ unittest
13341415
assert(worker.workSchedule[2] == DateTime(2023, 6, 17, 8, 45, 0));
13351416
}
13361417

1337-
unittest
1338-
{
1339-
static struct CaseInsensitivePolicy {
1340-
1341-
void onField(JT, T, C)(ref JT tokenizer, ref T item, JSONItem key, ref C context) {
1342-
// Case-insensitive comparison
1343-
import std.string : icmp;
1344-
alias members = SerializableMembers!T;
1345-
1346-
auto keyStr = key.data(tokenizer.chain);
1347-
1348-
static foreach(i, memberName; members) {
1349-
{
1350-
enum jsonName = memberName;
1351-
if (icmp(keyStr, jsonName) == 0) {
1352-
deserializeImpl(this, tokenizer, __traits(getMember, item, memberName));
1353-
context[i] = true;
1354-
return;
1355-
}
1356-
}
1357-
}
1358-
throw new JSONIopipeException(format("No member named '%s' in type `%s`", keyStr, T.stringof));
1359-
}
1360-
}
1361-
1362-
static struct S {
1363-
string userName;
1364-
int age;
1365-
}
1366-
auto jsonStr = `{"username": "Alice", "AGE": 30}`;
1367-
auto policy = CaseInsensitivePolicy();
1368-
auto s = deserialize!S(jsonStr, policy);
1369-
assert(s.userName == "Alice");
1370-
assert(s.age == 30);
1371-
}
1372-
13731418
unittest
13741419
{
13751420
// simple dynamic array test with policy
@@ -1416,7 +1461,7 @@ T deserialize(T, JT)(
14161461
ref JT tokenizer,
14171462
) if (isInstanceOf!(JSONTokenizer, JT))
14181463
{
1419-
auto policy = DefaultDeserializationPolicy();
1464+
auto policy = DefaultDeserializationPolicy!false();
14201465
return deserialize!T(tokenizer, policy);
14211466
}
14221467

@@ -1425,7 +1470,7 @@ void deserialize(T, JT)(
14251470
ref T item
14261471
) if (isInstanceOf!(JSONTokenizer, JT))
14271472
{
1428-
auto policy = DefaultDeserializationPolicy();
1473+
auto policy = DefaultDeserializationPolicy!false();
14291474
deserialize(tokenizer, item, policy);
14301475
}
14311476

@@ -1510,48 +1555,6 @@ unittest
15101555
assert(person.pet.age == 5);
15111556
}
15121557

1513-
unittest
1514-
{
1515-
import std.datetime.date;
1516-
// Test custom handling of a type with a policy.
1517-
1518-
static struct DTStringPolicy {
1519-
void deserializeImpl(JT, T)(ref JT tokenizer, ref T item) {
1520-
static if (is(T == DateTime))
1521-
{
1522-
// Deserialize DateTime from a string
1523-
auto jsonItem = tokenizer.nextSignificant
1524-
.jsonExpect(JSONToken.String, "Parsing DateTime");
1525-
item = DateTime.fromSimpleString(extractString!string(jsonItem, tokenizer.chain));
1526-
}
1527-
else {
1528-
// default to module behavior
1529-
.deserializeImpl(this, tokenizer, item);
1530-
};
1531-
}
1532-
}
1533-
1534-
auto pol = DTStringPolicy();
1535-
auto dt = deserialize!DateTime(`"2001-Jan-01 12:00:00"`, pol);
1536-
assert(dt == DateTime(2001, 1, 1, 12, 0, 0));
1537-
1538-
static struct Person {
1539-
string name;
1540-
int age;
1541-
DateTime dob;
1542-
}
1543-
1544-
auto jsonStr = `{
1545-
"name": "John Doe",
1546-
"age": 30,
1547-
"dob" : "2001-Jan-01 12:00:00"
1548-
}`;
1549-
auto p = deserialize!Person(jsonStr, pol);
1550-
assert(p.name == "John Doe");
1551-
assert(p.age == 30);
1552-
assert(p.dob == DateTime(2001, 1, 1, 12, 0, 0));
1553-
}
1554-
15551558
unittest
15561559
{
15571560
// Test on new design policy in extra members.

0 commit comments

Comments
 (0)