Extend Serializer abstraction, allow GsonIgnore on classes
This commit is contained in:
parent
adad20900d
commit
982fc0b9c6
|
@ -24,6 +24,10 @@ test {
|
|||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
java {
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
|
|
|
@ -31,6 +31,10 @@ test {
|
|||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
java {
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
|
|
|
@ -5,6 +5,10 @@ import io.gitlab.jfronny.gson.*;
|
|||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* A json serializer and deserializer for {@code ComparableVersion}
|
||||
* The json representation is a string
|
||||
*/
|
||||
public class ComparableVersionAdapter implements JsonDeserializer<ComparableVersion>, JsonSerializer<ComparableVersion> {
|
||||
@Override
|
||||
public ComparableVersion deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
|
|
|
@ -3,15 +3,17 @@ package io.gitlab.jfronny.commons.serialize.gson;
|
|||
import io.gitlab.jfronny.commons.ComparableVersion;
|
||||
import io.gitlab.jfronny.commons.serialize.Serializer;
|
||||
import io.gitlab.jfronny.commons.serialize.SerializerHolder;
|
||||
import io.gitlab.jfronny.gson.Gson;
|
||||
import io.gitlab.jfronny.gson.GsonBuilder;
|
||||
import io.gitlab.jfronny.gson.TypeAdapterFactory;
|
||||
import io.gitlab.jfronny.gson.*;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Holds a common instance of the Gson object.
|
||||
* Supports registering type adapters/etc. as needed
|
||||
*/
|
||||
public class GsonHolder {
|
||||
private static final GsonBuilder builder = new GsonBuilder()
|
||||
.registerTypeAdapter(ComparableVersion.class, new ComparableVersionAdapter())
|
||||
|
@ -22,6 +24,11 @@ public class GsonHolder {
|
|||
|
||||
private static boolean clean = false;
|
||||
private static Gson gson;
|
||||
|
||||
/**
|
||||
* Get the current gson instance or build it if needed
|
||||
* @return The Gson instance
|
||||
*/
|
||||
public static Gson getGson() {
|
||||
if (!clean) {
|
||||
gson = builder.create();
|
||||
|
@ -30,16 +37,29 @@ public class GsonHolder {
|
|||
return gson;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a type adapter and mark the gson instance as unclean
|
||||
* @param type The type for which to register the adapter
|
||||
* @param typeAdapter The adapter type
|
||||
*/
|
||||
public static void registerTypeAdapter(Type type, Object typeAdapter) {
|
||||
builder.registerTypeAdapter(type, typeAdapter);
|
||||
clean = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a type adapter factory and mark the gson instance as unclean
|
||||
* @param factory The factory to register
|
||||
*/
|
||||
public static void registerTypeAdapterFactory(TypeAdapterFactory factory) {
|
||||
builder.registerTypeAdapterFactory(factory);
|
||||
clean = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a function on the builder for modifying it and mark the gson instance as unclean
|
||||
* @param func The function to run
|
||||
*/
|
||||
public static void modifyBuilder(Consumer<GsonBuilder> func) {
|
||||
func.accept(builder);
|
||||
clean = false;
|
||||
|
@ -56,8 +76,21 @@ public class GsonHolder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public <T> T deserialize(Reader reader, Type type) throws SerializeException {
|
||||
return getGson().fromJson(reader, type);
|
||||
public <T> T deserialize(Reader source, Type typeOfT) throws SerializeException {
|
||||
try {
|
||||
return getGson().fromJson(source, typeOfT);
|
||||
} catch (JsonIOException | JsonSyntaxException e) {
|
||||
throw new SerializeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T deserialize(String source, Type typeOfT) throws SerializeException {
|
||||
try {
|
||||
return getGson().fromJson(source, typeOfT);
|
||||
} catch (JsonIOException | JsonSyntaxException e) {
|
||||
throw new SerializeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -5,7 +5,11 @@ import java.lang.annotation.Retention;
|
|||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Mark a class/field to be ignored by Gson.
|
||||
* May be used for metadata in serialized classes
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
@Target({ElementType.FIELD, ElementType.TYPE})
|
||||
public @interface GsonIgnore {
|
||||
}
|
||||
|
|
|
@ -3,14 +3,18 @@ package io.gitlab.jfronny.commons.serialize.gson;
|
|||
import io.gitlab.jfronny.gson.ExclusionStrategy;
|
||||
import io.gitlab.jfronny.gson.FieldAttributes;
|
||||
|
||||
/**
|
||||
* An exclusion strategy that ignores fields with the GsonIgnore attribute
|
||||
*/
|
||||
public class GsonIgnoreExclusionStrategy implements ExclusionStrategy {
|
||||
@Override
|
||||
public boolean shouldSkipClass(Class<?> clazz) {
|
||||
return false;
|
||||
return clazz.isAnnotationPresent(GsonIgnore.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldSkipField(FieldAttributes f) {
|
||||
return f.getAnnotation(GsonIgnore.class) != null;
|
||||
return f.getAnnotation(GsonIgnore.class) != null
|
||||
|| f.getDeclaringClass().isAnnotationPresent(GsonIgnore.class);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
package io.gitlab.jfronny.commons.test;
|
||||
|
||||
import io.gitlab.jfronny.commons.ComparableVersion;
|
||||
import io.gitlab.jfronny.commons.serialize.SerializerHolder;
|
||||
import io.gitlab.jfronny.commons.serialize.gson.GsonHolder;
|
||||
import io.gitlab.jfronny.commons.serialize.gson.GsonIgnore;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class GsonTest {
|
||||
@BeforeAll
|
||||
static void setup() {
|
||||
GsonHolder.register();
|
||||
}
|
||||
|
||||
@Test
|
||||
void gsonIgnore() {
|
||||
assertEquals("null", SerializerHolder.getInstance().serialize(new ExampleTypeHidden()));
|
||||
assertEquals("""
|
||||
{
|
||||
"shouldShow": "Yes"
|
||||
}""", SerializerHolder.getInstance().serialize(new TransitiveHiddenType()));
|
||||
assertEquals("""
|
||||
{
|
||||
"id": "Yes",
|
||||
"one": 1,
|
||||
"shown": {
|
||||
"shouldShow": "Yes"
|
||||
}
|
||||
}""", SerializerHolder.getInstance().serialize(new ExampleType()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void comparableVersionAdapter() {
|
||||
assertEquals("\"1.0.0\"", SerializerHolder.getInstance().serialize(new ComparableVersion("1.0.0")));
|
||||
assertDoesNotThrow(() -> assertEquals(new ComparableVersion("1.0.0"), SerializerHolder.getInstance().deserialize("\"1.0.0\"", ComparableVersion.class)));
|
||||
}
|
||||
|
||||
public static class ExampleType {
|
||||
String id = "Yes";
|
||||
@GsonIgnore
|
||||
String other = "No";
|
||||
Integer one = 1;
|
||||
ExampleTypeHidden type = new ExampleTypeHidden();
|
||||
TransitiveHiddenType shown = new TransitiveHiddenType();
|
||||
}
|
||||
|
||||
@GsonIgnore
|
||||
public static class ExampleTypeHidden {
|
||||
String someMetadata = "No";
|
||||
}
|
||||
|
||||
public static class TransitiveHiddenType extends ExampleTypeHidden {
|
||||
String shouldShow = "Yes";
|
||||
}
|
||||
}
|
|
@ -24,6 +24,16 @@ public interface Serializer {
|
|||
*/
|
||||
<T> T deserialize(Reader source, Type typeOfT) throws SerializeException;
|
||||
|
||||
/**
|
||||
* Deserialize data from a reader to an object
|
||||
* @param source The source from which the object should be deserialized
|
||||
* @param typeOfT The type of the desired object
|
||||
* @param <T> The type of the desired object
|
||||
* @return The deserialized object
|
||||
* @throws SerializeException if there was a problem during serialization
|
||||
*/
|
||||
<T> T deserialize(String source, Type typeOfT) throws SerializeException;
|
||||
|
||||
/**
|
||||
* The MIME type for serialized data, for example application/json
|
||||
* @return The MIME type
|
||||
|
|
|
@ -114,4 +114,15 @@ public class Coerce {
|
|||
public static <TIn, TOut, TEx extends Throwable> TOut pin(@Nullable TIn value, @NotNull ThrowingFunction<TIn, TOut, TEx> func) throws TEx {
|
||||
return Objects.requireNonNull(func).apply(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value The value to pin
|
||||
* @param func The function to apply to the pinned value
|
||||
* @param <TIn> The type of the value to pin
|
||||
* @param <TEx> An exception type (if needed)
|
||||
* @throws TEx If the function throws, nothing is handled here
|
||||
*/
|
||||
public static <TIn, TEx extends Throwable> void pin(@Nullable TIn value, @NotNull ThrowingConsumer<TIn, TEx> func) throws TEx {
|
||||
Objects.requireNonNull(func).accept(value);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue