diff --git a/src/main/java/com/suse/saltstack/netapi/parser/JsonParser.java b/src/main/java/com/suse/saltstack/netapi/parser/JsonParser.java index 63ddcb3aa..8fb9d8f90 100644 --- a/src/main/java/com/suse/saltstack/netapi/parser/JsonParser.java +++ b/src/main/java/com/suse/saltstack/netapi/parser/JsonParser.java @@ -8,6 +8,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; @@ -31,9 +32,11 @@ import java.io.InputStreamReader; import java.io.Reader; import java.lang.reflect.Type; +import java.lang.reflect.ParameterizedType; import java.util.Date; import java.util.List; import java.util.Map; +import java.util.Optional; /** * Parser for Saltstack responses. @@ -79,6 +82,7 @@ public JsonParser(TypeToken type) { .registerTypeAdapter(StartTime.class, new StartTimeAdapter().nullSafe()) .registerTypeAdapter(Stats.class, new StatsDeserializer()) .registerTypeAdapter(Arguments.class, new ArgumentsDeserializer()) + .registerTypeAdapterFactory(new OptionalTypeAdapterFactory()) .create(); } @@ -126,6 +130,47 @@ public Date read(JsonReader jsonReader) throws IOException { } } + /** + * TypeAdaptorFactory creating TypeAdapters for Optional + */ + private class OptionalTypeAdapterFactory implements TypeAdapterFactory { + + @Override + @SuppressWarnings("unchecked") + public TypeAdapter create(Gson gson, TypeToken typeToken) { + Type type = typeToken.getType(); + boolean isOptional = typeToken.getRawType() == Optional.class; + boolean isParameterized = type instanceof ParameterizedType; + if (isOptional && isParameterized) { + Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0]; + TypeAdapter elementAdapter = gson.getAdapter(TypeToken.get(elementType)); + return (TypeAdapter) optionalAdapter(elementAdapter); + } else { + return null; + } + } + + private TypeAdapter> optionalAdapter(TypeAdapter innerAdapter) { + return new TypeAdapter>() { + @Override + public Optional read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return Optional.empty(); + } else { + A value = innerAdapter.read(in); + return Optional.of(value); + } + } + + @Override + public void write(JsonWriter out, Optional optional) throws IOException { + innerAdapter.write(out, optional.orElse(null)); + } + }; + } + } + /** * Deserializer for the Stats object received from the API. */ diff --git a/src/test/java/com/suse/saltstack/netapi/parser/JsonParserTest.java b/src/test/java/com/suse/saltstack/netapi/parser/JsonParserTest.java index 7cdf948fc..5ed9fd860 100644 --- a/src/test/java/com/suse/saltstack/netapi/parser/JsonParserTest.java +++ b/src/test/java/com/suse/saltstack/netapi/parser/JsonParserTest.java @@ -2,6 +2,7 @@ import com.google.gson.JsonParseException; import com.suse.saltstack.netapi.calls.wheel.Key; +import com.google.gson.reflect.TypeToken; import com.suse.saltstack.netapi.datatypes.Arguments; import com.suse.saltstack.netapi.datatypes.Job; import com.suse.saltstack.netapi.datatypes.ScheduledJob; @@ -14,7 +15,9 @@ import com.suse.saltstack.netapi.datatypes.Token; import java.util.Date; import java.util.Arrays; +import java.util.Optional; import java.util.LinkedHashMap; +import java.util.LinkedList; import java.util.Map; import org.junit.Test; @@ -24,6 +27,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertFalse; /** * Json parser unit tests. @@ -202,6 +206,39 @@ public void testSaltStackJobsWithKwargsParser() throws Exception { assertEquals("lucid", job.getUser()); } + @Test + public void testOptionalParser() { + InputStream is = this.getClass() + .getResourceAsStream("/optional_parser_test.json"); + JsonParser parser = new JsonParser<>(new TypeToken(){}); + OptionalTest result = parser.parse(is); + assertFalse(result.nullString.isPresent()); + assertFalse(result.absentString.isPresent()); + result.valueString.ifPresent((value) -> + assertEquals("string with value", value) + ); + List> expected = new LinkedList<>(); + expected.add(Optional.of(1)); + expected.add(Optional.of(2)); + expected.add(Optional.of(3)); + expected.add(Optional.empty()); + expected.add(Optional.of(5)); + assertEquals(expected, result.maybeInts); + } + + @Test + public void testOptionalSingleValue() { + JsonParser> parser = + new JsonParser<>(new TypeToken>(){}); + InputStream nullValue = this.getClass() + .getResourceAsStream("/single_null_value.json"); + assertFalse(parser.parse(nullValue).isPresent()); + + InputStream intValue = this.getClass() + .getResourceAsStream("/single_int_value.json"); + assertEquals(new Integer(123), parser.parse(intValue).get()); + } + @Test public void testSaltStackJobsWithArgsAsKwargsParser() throws Exception { InputStream is = this.getClass() diff --git a/src/test/java/com/suse/saltstack/netapi/parser/OptionalTest.java b/src/test/java/com/suse/saltstack/netapi/parser/OptionalTest.java new file mode 100644 index 000000000..94ba7d9a3 --- /dev/null +++ b/src/test/java/com/suse/saltstack/netapi/parser/OptionalTest.java @@ -0,0 +1,15 @@ +package com.suse.saltstack.netapi.parser; + +import java.util.Optional; +import java.util.List; + +/** + * Helper Type to test the Optional Parser + */ +public class OptionalTest { + + public Optional nullString = Optional.empty(); + public Optional valueString = Optional.empty(); + public Optional absentString = Optional.empty(); + public List> maybeInts; +} diff --git a/src/test/resources/optional_parser_test.json b/src/test/resources/optional_parser_test.json new file mode 100644 index 000000000..47118e887 --- /dev/null +++ b/src/test/resources/optional_parser_test.json @@ -0,0 +1,5 @@ +{ + "nullString": null, + "valueString": "string with value", + "maybeInts": [1, 2, 3, null, 5] +} diff --git a/src/test/resources/single_int_value.json b/src/test/resources/single_int_value.json new file mode 100644 index 000000000..d800886d9 --- /dev/null +++ b/src/test/resources/single_int_value.json @@ -0,0 +1 @@ +123 \ No newline at end of file diff --git a/src/test/resources/single_null_value.json b/src/test/resources/single_null_value.json new file mode 100644 index 000000000..ec747fa47 --- /dev/null +++ b/src/test/resources/single_null_value.json @@ -0,0 +1 @@ +null \ No newline at end of file