From a4fe6d9524918d6e1f96be87f1d70216e243de84 Mon Sep 17 00:00:00 2001 From: "Adam J. Weigold" Date: Fri, 4 Jan 2019 08:48:27 -0600 Subject: [PATCH] Adding Jackson databind module for simplified deserialization. This commit adds compile only dependencies to Jackson databind, to provide simplified deserialization of ua_parser models for consumers using the library. As this is compile only, no impact is expected to consumers not using Jackson. However by including this boilerplate code, Jackson now can deserialize directly into ua_parser models in projects that may have stored them previously. --- pom.xml | 24 +++++++ src/main/java/ua_parser/ClientModule.java | 62 +++++++++++++++++++ .../com.fasterxml.jackson.databind.Module | 1 + .../JacksonModuleDatabindingTest.java | 43 +++++++++++++ src/test/resources/ua_parser/testClient.json | 18 ++++++ 5 files changed, 148 insertions(+) create mode 100644 src/main/java/ua_parser/ClientModule.java create mode 100644 src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module create mode 100644 src/test/java/ua_parser/JacksonModuleDatabindingTest.java create mode 100644 src/test/resources/ua_parser/testClient.json diff --git a/pom.xml b/pom.xml index 9c083f5..c97dfa5 100644 --- a/pom.xml +++ b/pom.xml @@ -57,6 +57,9 @@ + + src/main/resources + ua_parser ${basedir}/uap-core @@ -67,6 +70,9 @@ + + src/test/resources + ua_parser ${basedir}/uap-core/test_resources @@ -159,6 +165,12 @@ commons-collections4 4.1 + + com.fasterxml.jackson.core + jackson-databind + 2.8.3 + true + junit junit @@ -171,5 +183,17 @@ 1.3 test + + org.skyscreamer + jsonassert + 1.5.0 + test + + + commons-io + commons-io + 2.6 + test + diff --git a/src/main/java/ua_parser/ClientModule.java b/src/main/java/ua_parser/ClientModule.java new file mode 100644 index 0000000..ef13303 --- /dev/null +++ b/src/main/java/ua_parser/ClientModule.java @@ -0,0 +1,62 @@ +package ua_parser; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.module.SimpleModule; + +/** + * Class that registers capability of serializing objects with Jackson core. + * + * This module can be manually registered with Jackson: + *
+ *     ObjectMapper mapper = new ObjectMapper();
+ *     mapper.registerModule(new ClientModule());
+ * 
+ * + * This module also supports auto configuration as an SPI via the Java ServiceLoader api: + *
+ *     ObjectMapper mapper = new ObjectMapper();
+ *     mapper.findAndRegisterModules();
+ *
+ * 
+ * + * @author Adam J. Weigold + */ +public class ClientModule extends SimpleModule { + + public ClientModule() { + setMixInAnnotation(UserAgent.class, UserAgentMixin.class); + setMixInAnnotation(OS.class, OSMixin.class); + setMixInAnnotation(Device.class, DeviceMixin.class); + setMixInAnnotation(Client.class, ClientMixin.class); + } + + abstract static class UserAgentMixin { + @JsonCreator + public UserAgentMixin(@JsonProperty("family") String family, + @JsonProperty("major") String major, + @JsonProperty("minor") String minor, + @JsonProperty("patch") String patch) {} + } + + abstract static class OSMixin { + @JsonCreator + public OSMixin(@JsonProperty("family") String family, + @JsonProperty("major") String major, + @JsonProperty("minor") String minor, + @JsonProperty("patch") String patch, + @JsonProperty("patchMinor") String patchMinor) {} + } + + abstract static class DeviceMixin { + @JsonCreator + public DeviceMixin(@JsonProperty("family") String family) {} + } + + abstract static class ClientMixin { + @JsonCreator + public ClientMixin(@JsonProperty("userAgent") UserAgent userAgent, + @JsonProperty("os") OS os, + @JsonProperty("device") Device device) {} + } +} diff --git a/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module b/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module new file mode 100644 index 0000000..5601c50 --- /dev/null +++ b/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module @@ -0,0 +1 @@ +ua_parser.ClientModule \ No newline at end of file diff --git a/src/test/java/ua_parser/JacksonModuleDatabindingTest.java b/src/test/java/ua_parser/JacksonModuleDatabindingTest.java new file mode 100644 index 0000000..3e4dfe6 --- /dev/null +++ b/src/test/java/ua_parser/JacksonModuleDatabindingTest.java @@ -0,0 +1,43 @@ +package ua_parser; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.io.IOUtils; +import org.json.JSONException; +import org.junit.Test; +import org.skyscreamer.jsonassert.JSONAssert; + +import java.io.IOException; +import java.nio.charset.Charset; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class JacksonModuleDatabindingTest { + + @Test + public void testJacksonModuleSpiRegistration() { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.findAndRegisterModules(); + Class clientMixin = objectMapper.findMixInClassFor(Client.class); + assertTrue(clientMixin.isAssignableFrom(ClientModule.ClientMixin.class)); + } + + @Test + public void testJacksonSerializationAndDeserialization() throws IOException, JSONException { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.findAndRegisterModules(); + + Client testClient = new Client(new UserAgent("Firefox", "3", "5", "5"), + new OS("Mac OS X", "10", "4", null, null), + new Device("Other")); + + String expectedSerialization = IOUtils.resourceToString( + "ua_parser/testClient.json", Charset.defaultCharset(), getClass().getClassLoader()); + + String actualSerialization = objectMapper.writeValueAsString(testClient); + JSONAssert.assertEquals(expectedSerialization, actualSerialization, true); + + Client deserializedClient = objectMapper.readValue(actualSerialization, Client.class); + assertEquals(testClient, deserializedClient); + } +} diff --git a/src/test/resources/ua_parser/testClient.json b/src/test/resources/ua_parser/testClient.json new file mode 100644 index 0000000..0c5e1d1 --- /dev/null +++ b/src/test/resources/ua_parser/testClient.json @@ -0,0 +1,18 @@ +{ + "userAgent": { + "family": "Firefox", + "major": "3", + "minor": "5", + "patch": "5" + }, + "os": { + "family": "Mac OS X", + "major": "10", + "minor": "4", + "patch": null, + "patchMinor": null + }, + "device": { + "family": "Other" + } +} \ No newline at end of file