Skip to content

Commit e969215

Browse files
feat: refactor to allow for parsing of any single leaf or array of leafs (#235)
* refactor to allow for parsing of any single leaf or array of leafs * remove unused import * add parsing for audience.conditions leaf nodes
1 parent ded7fc9 commit e969215

File tree

5 files changed

+112
-93
lines changed

5 files changed

+112
-93
lines changed

core-api/src/main/java/com/optimizely/ab/config/parser/AudienceGsonDeserializer.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,15 @@ public Audience deserialize(JsonElement json, Type typeOfT, JsonDeserializationC
4949
if (!typeOfT.toString().contains("TypedAudience")) {
5050
conditionsElement = parser.parse(jsonObject.get("conditions").getAsString());
5151
}
52-
List<Object> rawObjectList = gson.fromJson(conditionsElement, List.class);
53-
Condition conditions = ConditionUtils.<UserAttribute>parseConditions(UserAttribute.class, rawObjectList);
52+
Condition conditions = null;
53+
if (conditionsElement.isJsonArray()) {
54+
List<Object> rawObjectList = gson.fromJson(conditionsElement, List.class);
55+
conditions = ConditionUtils.parseConditions(UserAttribute.class, rawObjectList);
56+
}
57+
else if (conditionsElement.isJsonObject()) {
58+
Object object = gson.fromJson(conditionsElement,Object.class);
59+
conditions = ConditionUtils.parseConditions(UserAttribute.class, object);
60+
}
5461

5562
return new Audience(id, name, conditions);
5663
}

core-api/src/main/java/com/optimizely/ab/config/parser/GsonHelpers.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.google.gson.JsonObject;
2424
import com.google.gson.JsonParseException;
2525
import com.google.gson.JsonParser;
26+
import com.google.gson.internal.LinkedTreeMap;
2627
import com.google.gson.reflect.TypeToken;
2728
import com.optimizely.ab.bucketing.DecisionService;
2829
import com.optimizely.ab.config.Experiment;
@@ -108,10 +109,16 @@ static Condition parseAudienceConditions(JsonObject experimentJson) {
108109

109110
JsonElement conditionsElement = experimentJson.get("audienceConditions");
110111

111-
List<Object> rawObjectList = gson.fromJson(conditionsElement, List.class);
112-
Condition conditions = ConditionUtils.<AudienceIdCondition>parseConditions(AudienceIdCondition.class, rawObjectList);
112+
if (conditionsElement.isJsonArray()) {
113+
List<Object> rawObjectList = gson.fromJson(conditionsElement, List.class);
114+
return ConditionUtils.<AudienceIdCondition>parseConditions(AudienceIdCondition.class, rawObjectList);
115+
}
116+
else if (conditionsElement.isJsonObject()) {
117+
Object jsonObject = gson.fromJson(conditionsElement,Object.class);
118+
return ConditionUtils.<AudienceIdCondition>parseConditions(AudienceIdCondition.class, jsonObject);
119+
}
113120

114-
return conditions;
121+
return null;
115122
}
116123

117124
static Experiment parseExperiment(JsonObject experimentJson, String groupId, JsonDeserializationContext context) {

core-api/src/main/java/com/optimizely/ab/config/parser/JsonConfigParser.java

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import com.optimizely.ab.internal.ConditionUtils;
3838
import org.json.JSONArray;
3939
import org.json.JSONObject;
40+
import org.json.JSONTokener;
4041

4142
import javax.annotation.Nonnull;
4243
import java.util.ArrayList;
@@ -148,9 +149,7 @@ private List<Experiment> parseExperiments(JSONArray experimentJson, String group
148149
Condition conditions = null;
149150
if (experimentObject.has("audienceConditions")) {
150151
Object jsonCondition = experimentObject.get("audienceConditions");
151-
if (jsonCondition instanceof JSONArray) {
152-
conditions = ConditionUtils.<AudienceIdCondition>parseConditions(AudienceIdCondition.class, (JSONArray) jsonCondition);
153-
}
152+
conditions = ConditionUtils.<AudienceIdCondition>parseConditions(AudienceIdCondition.class, jsonCondition);
154153
}
155154

156155
// parse the child objects
@@ -289,9 +288,19 @@ private List<Audience> parseAudiences(JSONArray audienceJson) {
289288
String id = audienceObject.getString("id");
290289
String key = audienceObject.getString("name");
291290
Object conditionsObject = audienceObject.get("conditions");
292-
JSONArray conditionJson = new JSONArray((String)conditionsObject);
291+
if (conditionsObject instanceof String) { // should always be true
292+
JSONTokener tokener = new JSONTokener((String)conditionsObject);
293+
char token = tokener.nextClean();
294+
if (token =='[') {
295+
// must be an array
296+
conditionsObject = new JSONArray((String)conditionsObject);
297+
}
298+
else if (token =='{') {
299+
conditionsObject = new JSONObject((String)conditionsObject);
300+
}
301+
}
293302

294-
Condition conditions = ConditionUtils.<UserAttribute>parseConditions(UserAttribute.class, conditionJson);
303+
Condition conditions = ConditionUtils.<UserAttribute>parseConditions(UserAttribute.class, conditionsObject);
295304
audiences.add(new Audience(id, key, conditions));
296305
}
297306

@@ -306,9 +315,8 @@ private List<Audience> parseTypedAudiences(JSONArray audienceJson) {
306315
String id = audienceObject.getString("id");
307316
String key = audienceObject.getString("name");
308317
Object conditionsObject = audienceObject.get("conditions");
309-
JSONArray conditionJson = (JSONArray)conditionsObject;
310318

311-
Condition conditions = ConditionUtils.<UserAttribute>parseConditions(UserAttribute.class, conditionJson);
319+
Condition conditions = ConditionUtils.<UserAttribute>parseConditions(UserAttribute.class, conditionsObject);
312320
audiences.add(new Audience(id, key, conditions));
313321
}
314322

core-api/src/main/java/com/optimizely/ab/config/parser/JsonSimpleConfigParser.java

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -156,13 +156,11 @@ private List<Experiment> parseExperiments(JSONArray experimentJson, String group
156156
Condition conditions = null;
157157
if (experimentObject.containsKey("audienceConditions")) {
158158
Object jsonCondition = experimentObject.get("audienceConditions");
159-
if (jsonCondition instanceof JSONArray) {
160-
try {
161-
conditions = ConditionUtils.<AudienceIdCondition>parseConditions(AudienceIdCondition.class, (JSONArray)jsonCondition);
162-
} catch (Exception e) {
163-
// unable to parse conditions.
164-
Logger.getAnonymousLogger().log(Level.ALL, "problem parsing audience conditions", e);
165-
}
159+
try {
160+
conditions = ConditionUtils.<AudienceIdCondition>parseConditions(AudienceIdCondition.class, jsonCondition);
161+
} catch (Exception e) {
162+
// unable to parse conditions.
163+
Logger.getAnonymousLogger().log(Level.ALL, "problem parsing audience conditions", e);
166164
}
167165
}
168166
// parse the child objects
@@ -303,7 +301,7 @@ private List<Audience> parseAudiences(JSONArray audienceJson) throws ParseExcept
303301
String id = (String)audienceObject.get("id");
304302
String key = (String)audienceObject.get("name");
305303
Object conditionObject = audienceObject.get("conditions");
306-
JSONArray conditionJson = (JSONArray)parser.parse((String)conditionObject);
304+
Object conditionJson = parser.parse((String)conditionObject);
307305
Condition conditions = ConditionUtils.<UserAttribute>parseConditions(UserAttribute.class, conditionJson);
308306
audiences.add(new Audience(id, key, conditions));
309307
}
@@ -319,8 +317,7 @@ private List<Audience> parseTypedAudiences(JSONArray audienceJson) throws ParseE
319317
String id = (String)audienceObject.get("id");
320318
String key = (String)audienceObject.get("name");
321319
Object conditionObject = audienceObject.get("conditions");
322-
JSONArray conditionJson = (JSONArray)conditionObject;
323-
Condition conditions = ConditionUtils.<UserAttribute>parseConditions(UserAttribute.class, conditionJson);
320+
Condition conditions = ConditionUtils.<UserAttribute>parseConditions(UserAttribute.class, conditionObject);
324321
audiences.add(new Audience(id, key, conditions));
325322
}
326323

core-api/src/main/java/com/optimizely/ab/internal/ConditionUtils.java

Lines changed: 71 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,75 @@
3232
import java.util.Map;
3333

3434
public class ConditionUtils {
35+
36+
static public <T> Condition parseConditions(Class<T> clazz, Object object) throws InvalidAudienceCondition {
37+
if (object instanceof List) {
38+
List<Object> objectList = (List<Object>)object;
39+
return ConditionUtils.<T>parseConditions(clazz, objectList);
40+
}
41+
else if (object instanceof String) { // looking for audience conditions in experiment
42+
AudienceIdCondition audienceIdCondition = new AudienceIdCondition<T>((String)object);
43+
if (clazz.isInstance(audienceIdCondition)) {
44+
return audienceIdCondition;
45+
}
46+
else {
47+
throw new InvalidAudienceCondition(String.format("Expected AudienceIdCondition got %s", clazz.getCanonicalName()));
48+
}
49+
}
50+
else if (object instanceof LinkedTreeMap) { // gson
51+
if (clazz != UserAttribute.class) {
52+
throw new InvalidAudienceCondition(String.format("Expected UserAttributes got %s", clazz.getCanonicalName()));
53+
54+
}
55+
56+
LinkedTreeMap<String, ?> conditionMap = (LinkedTreeMap<String, ?>)object;
57+
return new UserAttribute((String)conditionMap.get("name"), (String)conditionMap.get("type"),
58+
(String)conditionMap.get("match"), conditionMap.get("value"));
59+
}
60+
else if (object instanceof JSONObject) {
61+
if (clazz != UserAttribute.class) {
62+
throw new InvalidAudienceCondition(String.format("Expected UserAttributes got %s", clazz.getCanonicalName()));
63+
64+
}
65+
66+
JSONObject conditionMap = (JSONObject)object;
67+
return new UserAttribute((String)conditionMap.get("name"), (String)conditionMap.get("type"),
68+
(String)conditionMap.get("match"), conditionMap.get("value"));
69+
}
70+
else if (object instanceof org.json.JSONArray) {
71+
return ConditionUtils.<T>parseConditions(clazz, (org.json.JSONArray) object);
72+
}
73+
else if (object instanceof org.json.JSONObject){
74+
if (clazz != UserAttribute.class) {
75+
throw new InvalidAudienceCondition(String.format("Expected UserAttributes got %s", clazz.getCanonicalName()));
76+
77+
}
78+
org.json.JSONObject conditionMap = (org.json.JSONObject)object;
79+
String match = null;
80+
Object value = null;
81+
if (conditionMap.has("match")) {
82+
match = (String) conditionMap.get("match");
83+
}
84+
if (conditionMap.has("value")) {
85+
value = conditionMap.get("value");
86+
}
87+
return new UserAttribute((String)conditionMap.get("name"), (String)conditionMap.get("type"),
88+
match, value);
89+
}
90+
91+
else { // looking for audience conditions in audience
92+
if (clazz != UserAttribute.class) {
93+
throw new InvalidAudienceCondition(String.format("Expected UserAttributes got %s", clazz.getCanonicalName()));
94+
95+
}
96+
97+
Map<String, ?> conditionMap = (Map<String, ?>)object;
98+
return new UserAttribute((String)conditionMap.get("name"), (String)conditionMap.get("type"),
99+
(String)conditionMap.get("match"), conditionMap.get("value"));
100+
}
101+
102+
}
103+
35104
/**
36105
* parse conditions using List and Map
37106
* @param rawObjectList list of conditions
@@ -55,49 +124,7 @@ static public <T> Condition parseConditions(Class<T> clazz, List<Object> rawObje
55124

56125
for (int i = startingParseIndex; i < rawObjectList.size(); i++) {
57126
Object obj = rawObjectList.get(i);
58-
if (obj instanceof List) {
59-
List<Object> objectList = (List<Object>)rawObjectList.get(i);
60-
conditions.add(ConditionUtils.<T>parseConditions(clazz, objectList));
61-
}
62-
else if (obj instanceof String) { // looking for audience conditions in experiment
63-
AudienceIdCondition audienceIdCondition = new AudienceIdCondition<T>((String)obj);
64-
if (clazz.isInstance(audienceIdCondition)) {
65-
conditions.add(new AudienceIdCondition((String) obj));
66-
}
67-
else {
68-
throw new InvalidAudienceCondition(String.format("Expected AudienceIdCondition got %s", clazz.getCanonicalName()));
69-
}
70-
}
71-
else if (obj instanceof LinkedTreeMap) { // gson
72-
if (clazz != UserAttribute.class) {
73-
throw new InvalidAudienceCondition(String.format("Expected UserAttributes got %s", clazz.getCanonicalName()));
74-
75-
}
76-
77-
LinkedTreeMap<String, ?> conditionMap = (LinkedTreeMap<String, ?>)rawObjectList.get(i);
78-
conditions.add(new UserAttribute((String)conditionMap.get("name"), (String)conditionMap.get("type"),
79-
(String)conditionMap.get("match"), conditionMap.get("value")));
80-
}
81-
else if (obj instanceof JSONObject) {
82-
if (clazz != UserAttribute.class) {
83-
throw new InvalidAudienceCondition(String.format("Expected UserAttributes got %s", clazz.getCanonicalName()));
84-
85-
}
86-
87-
JSONObject conditionMap = (JSONObject)obj;
88-
conditions.add(new UserAttribute((String)conditionMap.get("name"), (String)conditionMap.get("type"),
89-
(String)conditionMap.get("match"), conditionMap.get("value")));
90-
}
91-
else { // looking for audience conditions in audience
92-
if (clazz != UserAttribute.class) {
93-
throw new InvalidAudienceCondition(String.format("Expected UserAttributes got %s", clazz.getCanonicalName()));
94-
95-
}
96-
97-
Map<String, ?> conditionMap = (Map<String, ?>)rawObjectList.get(i);
98-
conditions.add(new UserAttribute((String)conditionMap.get("name"), (String)conditionMap.get("type"),
99-
(String)conditionMap.get("match"), conditionMap.get("value")));
100-
}
127+
conditions.add(parseConditions(clazz, obj));
101128
}
102129

103130
Condition condition;
@@ -158,33 +185,7 @@ static public <T> Condition parseConditions(Class<T> clazz, org.json.JSONArray c
158185

159186
for (int i = startingParseIndex; i < conditionJson.length(); i++) {
160187
Object obj = conditionJson.get(i);
161-
if (obj instanceof org.json.JSONArray) {
162-
conditions.add(ConditionUtils.<T>parseConditions(clazz, (org.json.JSONArray) conditionJson.get(i)));
163-
} else if (obj instanceof String) {
164-
AudienceIdCondition<T> audiencCondition = new AudienceIdCondition<T>((String)obj);
165-
if (clazz.isInstance(audiencCondition)) {
166-
conditions.add(audiencCondition);
167-
}
168-
else {
169-
throw new InvalidAudienceCondition(String.format("Expected AudienceIdCondition got %s", clazz.getCanonicalName()));
170-
}
171-
} else {
172-
if (clazz != UserAttribute.class) {
173-
throw new InvalidAudienceCondition(String.format("Expected UserAttributes got %s", clazz.getCanonicalName()));
174-
175-
}
176-
org.json.JSONObject conditionMap = (org.json.JSONObject)obj;
177-
String match = null;
178-
Object value = null;
179-
if (conditionMap.has("match")) {
180-
match = (String) conditionMap.get("match");
181-
}
182-
if (conditionMap.has("value")) {
183-
value = conditionMap.get("value");
184-
}
185-
conditions.add(new UserAttribute((String)conditionMap.get("name"), (String)conditionMap.get("type"),
186-
match, value));
187-
}
188+
conditions.add(parseConditions(clazz, obj));
188189
}
189190

190191
Condition condition;
@@ -205,5 +206,4 @@ static public <T> Condition parseConditions(Class<T> clazz, org.json.JSONArray c
205206

206207
return condition;
207208
}
208-
209209
}

0 commit comments

Comments
 (0)