Skip to content

Commit 88faa6b

Browse files
committed
Add support to deserialize enum values with numbers as Json values
1 parent f1d7be6 commit 88faa6b

File tree

4 files changed

+181
-9
lines changed

4 files changed

+181
-9
lines changed

api-client/src/main/kotlin/de/gesellix/docker/remote/api/core/NullIfEmptyEnumAdapterFactory.kt

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ import java.lang.reflect.Type
1111
class NullIfEmptyEnumAdapterFactory : JsonAdapter.Factory {
1212

1313
override fun create(
14-
type: Type,
15-
annotations: MutableSet<out Annotation>,
16-
moshi: Moshi
14+
type: Type,
15+
annotations: MutableSet<out Annotation>,
16+
moshi: Moshi
1717
): JsonAdapter<*>? {
1818
// if (!Types.getRawType(type).isAnnotationPresent(
1919
// DefaultIfEmpty::class.java)) {
@@ -27,12 +27,29 @@ class NullIfEmptyEnumAdapterFactory : JsonAdapter.Factory {
2727

2828
return object : JsonAdapter<Any>() {
2929
override fun fromJson(reader: JsonReader): Any? {
30-
// @Suppress("UNCHECKED_CAST")
31-
val blob = reader.readJsonValue() as String?
32-
// val blob = reader.readJsonValue() as Map<String, Any?>
33-
val nullOrValue = when {
34-
(blob.isNullOrEmpty()) -> null
35-
else -> blob
30+
val nullOrValue = when (val peek = reader.peek()) {
31+
JsonReader.Token.NULL -> null
32+
33+
JsonReader.Token.STRING -> {
34+
val value = reader.readJsonValue() as String
35+
when {
36+
value.isEmpty() -> null
37+
else -> value
38+
}
39+
}
40+
41+
JsonReader.Token.NUMBER -> {
42+
val value = reader.readJsonValue() as Double
43+
when {
44+
value.toInt().compareTo(value) == 0 -> value.toInt().toString()
45+
value.toLong().compareTo(value) == 0 -> value.toLong().toString()
46+
else -> value
47+
}
48+
}
49+
50+
JsonReader.Token.BOOLEAN -> reader.readJsonValue() as Boolean
51+
52+
else -> throw IllegalArgumentException("Token type not supported: $peek")
3653
}
3754
return delegate.fromJsonValue(nullOrValue)
3855
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package de.gesellix.docker.remote.api.core;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertNull;
5+
import static org.junit.jupiter.api.Assertions.assertThrows;
6+
7+
import java.io.IOException;
8+
9+
import org.junit.jupiter.api.Disabled;
10+
import org.junit.jupiter.api.Test;
11+
12+
import com.squareup.moshi.Json;
13+
import com.squareup.moshi.JsonAdapter;
14+
import com.squareup.moshi.JsonDataException;
15+
import com.squareup.moshi.Moshi;
16+
import com.squareup.moshi.Types;
17+
18+
import de.gesellix.docker.remote.api.ChangeType;
19+
import de.gesellix.docker.remote.api.ContainerState;
20+
21+
class NullIfEmptyEnumAdapterFactoryTest {
22+
23+
@Test
24+
@Disabled("not sure if this should work as expected by the test")
25+
public void shouldReadNullEnumValues() throws IOException {
26+
TestWrapper<ContainerState.Status> result = deserialize(
27+
ContainerState.Status.class,
28+
null);
29+
assertNull(result.getProperty());
30+
}
31+
32+
@Test
33+
public void shouldReadEmptyStringEnumValuesAsNull() throws IOException {
34+
TestWrapper<ContainerState.Status> result = deserialize(
35+
ContainerState.Status.class,
36+
"\"\"");
37+
assertNull(result.getProperty());
38+
}
39+
40+
@Test
41+
public void shouldFailWhenReadingInvalidEnumValues() {
42+
JsonDataException thrown = assertThrows(JsonDataException.class,
43+
() -> deserialize(ContainerState.Status.class, "\"foobar\""));
44+
assertEquals("Expected one of [created, running, paused, restarting, removing, exited, dead] but was foobar at path $", thrown.getMessage());
45+
}
46+
47+
@Test
48+
public void shouldReadStringEnumValues() throws IOException {
49+
TestWrapper<ContainerState.Status> result = deserialize(
50+
ContainerState.Status.class,
51+
"\"" + ContainerState.Status.Dead.getValue() + "\"");
52+
assertEquals(ContainerState.Status.Dead, result.getProperty());
53+
}
54+
55+
@Test
56+
public void shouldReadIntegerEnumValuesFromString() throws IOException {
57+
TestWrapper<ChangeType> result = deserialize(
58+
ChangeType.class,
59+
"\"" + ChangeType.T1.getValue() + "\"");
60+
assertEquals(ChangeType.T1, result.getProperty());
61+
}
62+
63+
@Test
64+
public void shouldReadIntegerEnumValuesFromNumber() throws IOException {
65+
TestWrapper<ChangeType> result = deserialize(
66+
ChangeType.class,
67+
ChangeType.T1.getValue());
68+
assertEquals(ChangeType.T1, result.getProperty());
69+
}
70+
71+
@Test
72+
public void shouldReadDoubleEnumValuesFromString() throws IOException {
73+
TestWrapper<TestEnum> result = deserialize(
74+
TestEnum.class,
75+
"\"" + TestEnum.V1_2.getValue() + "\"");
76+
assertEquals(TestEnum.V1_2, result.getProperty());
77+
}
78+
79+
@Test
80+
@Disabled("not sure if this should work as expected by the test")
81+
public void shouldReadDoubleEnumValuesFromNumber() throws IOException {
82+
TestWrapper<TestEnum> result = deserialize(
83+
TestEnum.class,
84+
TestEnum.V1_2.getValue());
85+
assertEquals(TestEnum.V1_2, result.getProperty());
86+
}
87+
88+
private <T> TestWrapper<T> deserialize(Class<T> enumType, Object serializedValue) throws IOException {
89+
JsonAdapter<TestWrapper<T>> moshi = new Moshi.Builder()
90+
.add(new NullIfEmptyEnumAdapterFactory())
91+
.add(new TestWrapperAdapter<T>())
92+
.build()
93+
.adapter(Types.newParameterizedType(TestWrapper.class, enumType));
94+
95+
String serialized = "{ \"property\" : " + serializedValue + " }";
96+
System.out.println("Deserializing '" + serialized + "'");
97+
return moshi.fromJson(serialized);
98+
}
99+
100+
enum TestEnum {
101+
@Json(name = "1.1")
102+
V1_1("1.1"),
103+
@Json(name = "1.2")
104+
V1_2("1.2");
105+
106+
private final String value;
107+
108+
TestEnum(String value) {
109+
this.value = value;
110+
}
111+
112+
public String getValue() {
113+
return value;
114+
}
115+
}
116+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package de.gesellix.docker.remote.api.core;
2+
3+
public class TestWrapper<T> {
4+
public TestWrapper() {
5+
}
6+
7+
private T property;
8+
9+
public T getProperty() {
10+
return property;
11+
}
12+
13+
public void setProperty(T property) {
14+
this.property = property;
15+
}
16+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package de.gesellix.docker.remote.api.core;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
6+
import com.squareup.moshi.FromJson;
7+
import com.squareup.moshi.ToJson;
8+
9+
public class TestWrapperAdapter<T> {
10+
@ToJson
11+
public Map<String, T> toJson(TestWrapper<T> from) {
12+
Map<String, T> result = new HashMap<>();
13+
result.put("property", from.getProperty());
14+
return result;
15+
}
16+
17+
@FromJson
18+
public TestWrapper<T> fromJson(Map<String, T> delegate) {
19+
TestWrapper<T> result = new TestWrapper<>();
20+
result.setProperty(delegate.get("property"));
21+
return result;
22+
}
23+
}

0 commit comments

Comments
 (0)