Skip to content

Commit ae64508

Browse files
committed
Add CaseInsensitiveEnumTypeAdapterFactory
1 parent 0ea7ec8 commit ae64508

File tree

3 files changed

+137
-0
lines changed

3 files changed

+137
-0
lines changed

src/AndroidClient/client/src/main/java/net/servicestack/client/JsonSerializers.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,27 @@
33

44
package net.servicestack.client;
55

6+
import com.google.gson.Gson;
67
import com.google.gson.JsonDeserializationContext;
78
import com.google.gson.JsonDeserializer;
89
import com.google.gson.JsonElement;
910
import com.google.gson.JsonParseException;
1011
import com.google.gson.JsonPrimitive;
1112
import com.google.gson.JsonSerializationContext;
1213
import com.google.gson.JsonSerializer;
14+
import com.google.gson.TypeAdapter;
15+
import com.google.gson.TypeAdapterFactory;
16+
import com.google.gson.reflect.TypeToken;
17+
import com.google.gson.stream.JsonReader;
18+
import com.google.gson.stream.JsonToken;
19+
import com.google.gson.stream.JsonWriter;
1320

21+
import java.io.IOException;
1422
import java.lang.reflect.Type;
1523
import java.util.Date;
24+
import java.util.HashMap;
25+
import java.util.Locale;
26+
import java.util.Map;
1627
import java.util.UUID;
1728

1829
public class JsonSerializers {
@@ -69,4 +80,45 @@ public UUID deserialize(JsonElement json, Type typeOfT, JsonDeserializationConte
6980
}
7081
};
7182
}
83+
84+
public static class CaseInsensitiveEnumTypeAdapterFactory implements TypeAdapterFactory {
85+
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
86+
Class<T> rawType = (Class<T>) type.getRawType();
87+
if (!rawType.isEnum()) {
88+
return null;
89+
}
90+
91+
final Map<String, T> lowercaseToConstant = new HashMap<String, T>();
92+
for (T constant : rawType.getEnumConstants()) {
93+
lowercaseToConstant.put(toLowercase(constant), constant);
94+
}
95+
96+
return new TypeAdapter<T>() {
97+
public void write(JsonWriter out, T value) throws IOException {
98+
if (value == null) {
99+
out.nullValue();
100+
} else {
101+
out.value(value.toString());
102+
}
103+
}
104+
105+
public T read(JsonReader reader) throws IOException {
106+
if (reader.peek() == JsonToken.NULL) {
107+
reader.nextNull();
108+
return null;
109+
} else {
110+
return lowercaseToConstant.get(toLowercase(reader.nextString()));
111+
}
112+
}
113+
};
114+
}
115+
116+
private String toLowercase(Object o) {
117+
return o.toString().toLowerCase(Locale.US);
118+
}
119+
}
120+
121+
public static TypeAdapterFactory getCaseInsensitiveEnumTypeAdapterFactory() {
122+
return new CaseInsensitiveEnumTypeAdapterFactory();
123+
}
72124
}

src/AndroidClient/client/src/main/java/net/servicestack/client/JsonServiceClient.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ public void setTimeout(int timeoutMs) {
6666

6767
public GsonBuilder getGsonBuilder() {
6868
return new GsonBuilder()
69+
.registerTypeAdapterFactory(JsonSerializers.getCaseInsensitiveEnumTypeAdapterFactory())
6970
.registerTypeAdapter(Date.class, JsonSerializers.getDateSerializer())
7071
.registerTypeAdapter(Date.class, JsonSerializers.getDateDeserializer())
7172
.registerTypeAdapter(TimeSpan.class, JsonSerializers.getTimeSpanSerializer())
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package utils;
2+
3+
import com.google.gson.Gson;
4+
import com.google.gson.GsonBuilder;
5+
import junitparams.JUnitParamsRunner;
6+
import junitparams.Parameters;
7+
import org.junit.Before;
8+
import org.junit.Rule;
9+
import org.junit.Test;
10+
import org.junit.rules.ExpectedException;
11+
import org.junit.runner.RunWith;
12+
import junit.framework.TestCase;
13+
14+
import net.servicestack.client.JsonSerializers;
15+
16+
import static org.junit.Assert.assertEquals;
17+
18+
enum SodaEnum
19+
{
20+
Coke,
21+
Pepsi,
22+
}
23+
24+
@RunWith(JUnitParamsRunner.class)
25+
public class CaseInsensitiveTypeAdapterFactoryTest {
26+
27+
private Gson gson;
28+
29+
@Before
30+
public void forEachTest() {
31+
gson = new GsonBuilder()
32+
.registerTypeAdapterFactory(new JsonSerializers.CaseInsensitiveEnumTypeAdapterFactory())
33+
.create();
34+
}
35+
36+
@Rule
37+
public ExpectedException thrown = ExpectedException.none();
38+
39+
private Object[] validDeserializationTests() {
40+
return new Object[]{
41+
new Object[]{"Pascal case", "Coke", SodaEnum.Coke},
42+
new Object[]{"Weird mixed case", "cOKe", SodaEnum.Coke},
43+
new Object[]{"lowercase", "pepsi", SodaEnum.Pepsi},
44+
new Object[]{"UPPERCASE", "PEPSI", SodaEnum.Pepsi},
45+
};
46+
}
47+
48+
@Test
49+
@Parameters(method = "validDeserializationTests")
50+
public void fromJson_withValidText_ConvertsAsExpected(String reason, String jsonText, SodaEnum expected) {
51+
SodaEnum actual = gson.fromJson(jsonText, SodaEnum.class);
52+
53+
assertEquals(reason, expected, actual);
54+
}
55+
56+
private Object[] invalidDeserializationTests() {
57+
return new Object[]{
58+
new Object[]{"Null should not deserialize", null},
59+
new Object[]{"Unknown type", "SevenUp"},
60+
};
61+
}
62+
63+
@Test
64+
@Parameters(method = "invalidDeserializationTests")
65+
public void fromJson_withInvalidText_ReturnsNull(String reason, String jsonText){
66+
SodaEnum actual = gson.fromJson(jsonText, SodaEnum.class);
67+
assertEquals(reason, null, actual);
68+
}
69+
70+
private Object[] validSerializationTests() {
71+
return new Object[]{
72+
new Object[]{SodaEnum.Coke, "\"Coke\""},
73+
new Object[]{SodaEnum.Pepsi, "\"Pepsi\""},
74+
};
75+
}
76+
77+
@Test
78+
@Parameters(method = "validSerializationTests")
79+
public void toJson_ConvertsAsExpected(SodaEnum value, String expected) {
80+
String actual = gson.toJson(value);
81+
82+
assertEquals("Serialized to json", expected, actual);
83+
}
84+
}

0 commit comments

Comments
 (0)