@@ -31,23 +31,44 @@ import std.typecons : Nullable;
3131import std.conv ;
3232import 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
5273private 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
12831364unittest
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-
13731418unittest
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-
15551558unittest
15561559{
15571560 // Test on new design policy in extra members.
0 commit comments