mirror of https://github.com/jwtk/jjwt.git
Merge pull request #414 from patton73/master
Added Gson serialization/deserialization Extension
This commit is contained in:
commit
ff8a6bfe58
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2018 JWTK
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-root</artifactId>
|
||||
<version>0.10.8-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>jjwt-gson</artifactId>
|
||||
<name>JJWT :: Extensions :: Gson</name>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<jjwt.root>${basedir}/../..</jjwt.root>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,44 @@
|
|||
package io.jsonwebtoken.gson.io;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import io.jsonwebtoken.io.DeserializationException;
|
||||
import io.jsonwebtoken.io.Deserializer;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import java.io.IOException;
|
||||
|
||||
public class GsonDeserializer<T> implements Deserializer<T> {
|
||||
|
||||
private final Class<T> returnType;
|
||||
private final Gson gson;
|
||||
|
||||
@SuppressWarnings("unused") //used via reflection by RuntimeClasspathDeserializerLocator
|
||||
public GsonDeserializer() {
|
||||
this(GsonSerializer.DEFAULT_GSON);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "WeakerAccess", "unused"}) // for end-users providing a custom gson
|
||||
public GsonDeserializer(Gson gson) {
|
||||
this(gson, (Class<T>) Object.class);
|
||||
}
|
||||
|
||||
private GsonDeserializer(Gson gson, Class<T> returnType) {
|
||||
Assert.notNull(gson, "gson cannot be null.");
|
||||
Assert.notNull(returnType, "Return type cannot be null.");
|
||||
this.gson = gson;
|
||||
this.returnType = returnType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T deserialize(byte[] bytes) throws DeserializationException {
|
||||
try {
|
||||
return readValue(bytes);
|
||||
} catch (IOException e) {
|
||||
String msg = "Unable to deserialize bytes into a " + returnType.getName() + " instance: " + e.getMessage();
|
||||
throw new DeserializationException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
protected T readValue(byte[] bytes) throws IOException {
|
||||
return gson.fromJson(new String(bytes), returnType);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package io.jsonwebtoken.gson.io;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import io.jsonwebtoken.io.Encoders;
|
||||
import io.jsonwebtoken.io.SerializationException;
|
||||
import io.jsonwebtoken.io.Serializer;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.Strings;
|
||||
|
||||
public class GsonSerializer<T> implements Serializer<T> {
|
||||
|
||||
static final Gson DEFAULT_GSON = new GsonBuilder().disableHtmlEscaping().create();
|
||||
private Gson gson;
|
||||
|
||||
@SuppressWarnings("unused") //used via reflection by RuntimeClasspathDeserializerLocator
|
||||
public GsonSerializer() {
|
||||
this(DEFAULT_GSON);
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess") //intended for end-users to use when providing a custom gson
|
||||
public GsonSerializer(Gson gson) {
|
||||
Assert.notNull(gson, "gson cannot be null.");
|
||||
this.gson = gson;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serialize(T t) throws SerializationException {
|
||||
Assert.notNull(t, "Object to serialize cannot be null.");
|
||||
try {
|
||||
return writeValueAsBytes(t);
|
||||
} catch (Exception e) {
|
||||
String msg = "Unable to serialize object: " + e.getMessage();
|
||||
throw new SerializationException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess") //for testing
|
||||
protected byte[] writeValueAsBytes(T t) {
|
||||
Object o;
|
||||
if (t instanceof byte[]) {
|
||||
o = Encoders.BASE64.encode((byte[]) t);
|
||||
} else if (t instanceof char[]) {
|
||||
o = new String((char[]) t);
|
||||
} else {
|
||||
o = t;
|
||||
}
|
||||
return this.gson.toJson(o).getBytes(Strings.UTF_8);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package io.jsonwebtoken.gson.io
|
||||
|
||||
import com.google.gson.Gson
|
||||
import io.jsonwebtoken.io.DeserializationException
|
||||
import io.jsonwebtoken.lang.Strings
|
||||
import org.junit.Test
|
||||
|
||||
import static org.easymock.EasyMock.*
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class GsonDeserializerTest {
|
||||
|
||||
@Test
|
||||
void testDefaultConstructor() {
|
||||
def deserializer = new GsonDeserializer()
|
||||
assertNotNull deserializer.gson
|
||||
}
|
||||
|
||||
@Test
|
||||
void testObjectMapperConstructor() {
|
||||
def customGSON = new Gson()
|
||||
def deserializer = new GsonDeserializer(customGSON)
|
||||
assertSame customGSON, deserializer.gson
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException)
|
||||
void testObjectMapperConstructorWithNullArgument() {
|
||||
new GsonDeserializer<>(null)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeserialize() {
|
||||
byte[] serialized = '{"hello":"世界"}'.getBytes(Strings.UTF_8)
|
||||
def expected = [hello: '世界']
|
||||
def result = new GsonDeserializer().deserialize(serialized)
|
||||
assertEquals expected, result
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeserializeFailsWithJsonProcessingException() {
|
||||
|
||||
def ex = createMock(java.io.IOException)
|
||||
|
||||
expect(ex.getMessage()).andReturn('foo')
|
||||
|
||||
def deserializer = new GsonDeserializer() {
|
||||
@Override
|
||||
protected Object readValue(byte[] bytes) throws java.io.IOException {
|
||||
throw ex
|
||||
}
|
||||
}
|
||||
|
||||
replay ex
|
||||
|
||||
try {
|
||||
deserializer.deserialize('{"hello":"世界"}'.getBytes(Strings.UTF_8))
|
||||
fail()
|
||||
} catch (DeserializationException se) {
|
||||
assertEquals 'Unable to deserialize bytes into a java.lang.Object instance: foo', se.getMessage()
|
||||
assertSame ex, se.getCause()
|
||||
}
|
||||
|
||||
verify ex
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
package io.jsonwebtoken.gson.io
|
||||
|
||||
import io.jsonwebtoken.lang.Strings
|
||||
import org.junit.Test
|
||||
|
||||
import static org.easymock.EasyMock.*
|
||||
import static org.junit.Assert.*
|
||||
import com.google.gson.Gson
|
||||
import io.jsonwebtoken.io.SerializationException
|
||||
|
||||
class GsonSerializerTest {
|
||||
|
||||
@Test
|
||||
void testDefaultConstructor() {
|
||||
def serializer = new GsonSerializer()
|
||||
assertNotNull serializer.gson
|
||||
}
|
||||
|
||||
@Test
|
||||
void testObjectMapperConstructor() {
|
||||
def customGSON = new Gson()
|
||||
def serializer = new GsonSerializer<>(customGSON)
|
||||
assertSame customGSON, serializer.gson
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException)
|
||||
void testObjectMapperConstructorWithNullArgument() {
|
||||
new GsonSerializer<>(null)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testByte() {
|
||||
byte[] expected = "120".getBytes(Strings.UTF_8) //ascii("x") = 120
|
||||
byte[] bytes = "x".getBytes(Strings.UTF_8)
|
||||
byte[] result = new GsonSerializer().serialize(bytes[0]) //single byte
|
||||
assertTrue Arrays.equals(expected, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testByteArray() { //expect Base64 string by default:
|
||||
byte[] bytes = "hi".getBytes(Strings.UTF_8)
|
||||
String expected = '"aGk="' as String //base64(hi) --> aGk=
|
||||
byte[] result = new GsonSerializer().serialize(bytes)
|
||||
assertEquals expected, new String(result, Strings.UTF_8)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmptyByteArray() { //expect Base64 string by default:
|
||||
byte[] bytes = new byte[0]
|
||||
byte[] result = new GsonSerializer().serialize(bytes)
|
||||
assertEquals '""', new String(result, Strings.UTF_8)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testChar() { //expect Base64 string by default:
|
||||
byte[] result = new GsonSerializer().serialize('h' as char)
|
||||
assertEquals "\"h\"", new String(result, Strings.UTF_8)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCharArray() { //expect Base64 string by default:
|
||||
byte[] result = new GsonSerializer().serialize("hi".toCharArray())
|
||||
assertEquals "\"hi\"", new String(result, Strings.UTF_8)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSerialize() {
|
||||
byte[] expected = '{"hello":"世界"}'.getBytes(Strings.UTF_8)
|
||||
byte[] result = new GsonSerializer().serialize([hello: '世界'])
|
||||
assertTrue Arrays.equals(expected, result)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void testSerializeFailsWithJsonProcessingException() {
|
||||
|
||||
def ex = createMock(SerializationException)
|
||||
|
||||
expect(ex.getMessage()).andReturn('foo')
|
||||
|
||||
def serializer = new GsonSerializer() {
|
||||
@Override
|
||||
protected byte[] writeValueAsBytes(Object o) throws SerializationException {
|
||||
throw ex
|
||||
}
|
||||
}
|
||||
|
||||
replay ex
|
||||
|
||||
try {
|
||||
serializer.serialize([hello: 'world'])
|
||||
fail()
|
||||
} catch (SerializationException se) {
|
||||
assertEquals 'Unable to serialize object: foo', se.getMessage()
|
||||
assertSame ex, se.getCause()
|
||||
}
|
||||
|
||||
verify ex
|
||||
}
|
||||
}
|
|
@ -36,6 +36,7 @@
|
|||
<modules>
|
||||
<module>jackson</module>
|
||||
<module>orgjson</module>
|
||||
<module>gson</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
|
@ -54,6 +54,11 @@
|
|||
<artifactId>jjwt-orgjson</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-gson</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -34,6 +34,8 @@ public class RuntimeClasspathDeserializerLocator<T> implements InstanceLocator<D
|
|||
return Classes.newInstance("io.jsonwebtoken.io.JacksonDeserializer");
|
||||
} else if (isAvailable("io.jsonwebtoken.io.OrgJsonDeserializer")) {
|
||||
return Classes.newInstance("io.jsonwebtoken.io.OrgJsonDeserializer");
|
||||
} else if (isAvailable("io.jsonwebtoken.gson.io.GsonDeserializer")) {
|
||||
return Classes.newInstance("io.jsonwebtoken.gson.io.GsonDeserializer");
|
||||
} else {
|
||||
throw new IllegalStateException("Unable to discover any JSON Deserializer implementations on the classpath.");
|
||||
}
|
||||
|
|
|
@ -34,6 +34,8 @@ public class RuntimeClasspathSerializerLocator implements InstanceLocator<Serial
|
|||
return Classes.newInstance("io.jsonwebtoken.io.JacksonSerializer");
|
||||
} else if (isAvailable("io.jsonwebtoken.io.OrgJsonSerializer")) {
|
||||
return Classes.newInstance("io.jsonwebtoken.io.OrgJsonSerializer");
|
||||
} else if (isAvailable("io.jsonwebtoken.gson.io.GsonSerializer")) {
|
||||
return Classes.newInstance("io.jsonwebtoken.gson.io.GsonSerializer");
|
||||
} else {
|
||||
throw new IllegalStateException("Unable to discover any JSON Serializer implementations on the classpath.");
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
|
|||
import io.jsonwebtoken.io.Deserializer
|
||||
import io.jsonwebtoken.io.JacksonDeserializer
|
||||
import io.jsonwebtoken.io.OrgJsonDeserializer
|
||||
import io.jsonwebtoken.gson.io.GsonDeserializer
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
@ -96,4 +97,23 @@ class RuntimeClasspathDeserializerLocatorTest {
|
|||
def deserializer = locator.getInstance()
|
||||
assertTrue deserializer instanceof OrgJsonDeserializer
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGson() {
|
||||
def locator = new RuntimeClasspathDeserializerLocator() {
|
||||
@Override
|
||||
protected boolean isAvailable(String fqcn) {
|
||||
if (JacksonDeserializer.class.getName().equals(fqcn)) {
|
||||
return false; //skip it to allow the Gson impl to be created
|
||||
}
|
||||
if (OrgJsonDeserializer.class.getName().equals(fqcn)) {
|
||||
return false; //skip it to allow the Gson impl to be created
|
||||
}
|
||||
return super.isAvailable(fqcn)
|
||||
}
|
||||
}
|
||||
|
||||
def deserializer = locator.getInstance()
|
||||
assertTrue deserializer instanceof GsonDeserializer
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package io.jsonwebtoken.impl.io
|
|||
import io.jsonwebtoken.io.Serializer
|
||||
import io.jsonwebtoken.io.JacksonSerializer
|
||||
import io.jsonwebtoken.io.OrgJsonSerializer
|
||||
import io.jsonwebtoken.gson.io.GsonSerializer
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
@ -95,4 +96,23 @@ class RuntimeClasspathSerializerLocatorTest {
|
|||
def serializer = locator.getInstance()
|
||||
assertTrue serializer instanceof OrgJsonSerializer
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGson() {
|
||||
def locator = new RuntimeClasspathSerializerLocator() {
|
||||
@Override
|
||||
protected boolean isAvailable(String fqcn) {
|
||||
if (JacksonSerializer.class.getName().equals(fqcn)) {
|
||||
return false //skip it to allow the Gson impl to be created
|
||||
}
|
||||
if (OrgJsonSerializer.class.getName().equals(fqcn)) {
|
||||
return false //skip it to allow the Gson impl to be created
|
||||
}
|
||||
return super.isAvailable(fqcn)
|
||||
}
|
||||
}
|
||||
|
||||
def serializer = locator.getInstance()
|
||||
assertTrue serializer instanceof GsonSerializer
|
||||
}
|
||||
}
|
||||
|
|
11
pom.xml
11
pom.xml
|
@ -90,6 +90,7 @@
|
|||
|
||||
<jackson.version>2.9.9.1</jackson.version>
|
||||
<orgjson.version>20180130</orgjson.version>
|
||||
<gson.version>2.8.5</gson.version>
|
||||
|
||||
<!-- Optional Runtime Dependencies: -->
|
||||
<bouncycastle.version>1.60</bouncycastle.version>
|
||||
|
@ -131,6 +132,11 @@
|
|||
<artifactId>jjwt-orgjson</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-gson</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
|
@ -141,6 +147,11 @@
|
|||
<artifactId>json</artifactId>
|
||||
<version>${orgjson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>${gson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Used only during testing for PS256, PS384 and PS512 since JDK <= 10 doesn't support them: -->
|
||||
<dependency>
|
||||
|
|
Loading…
Reference in New Issue