Skip to content
This repository has been archived by the owner on Sep 29, 2020. It is now read-only.

Commit

Permalink
Allow exclusion cancellation and option to just use the serializer or…
Browse files Browse the repository at this point in the history
… deserializer
  • Loading branch information
kashike committed Jan 14, 2018
1 parent e4876c6 commit 53ffab8
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 17 deletions.
7 changes: 6 additions & 1 deletion src/main/java/net/kyori/cereal/Document.java
Expand Up @@ -32,12 +32,17 @@
* A document is something that can be serialized.
*/
public interface Document {

/**
* Marks a method for exclusion from serialization.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Exclude {
/**
* Tests if the method is excluded.
*
* @return {@code true} if the method is excluded
*/
boolean value() default true;
}
}
8 changes: 7 additions & 1 deletion src/main/java/net/kyori/cereal/DocumentRegistry.java
Expand Up @@ -26,6 +26,7 @@
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import net.kyori.blizzard.NonNull;
import net.kyori.blizzard.Nullable;

import java.lang.reflect.Method;
import java.util.LinkedHashMap;
Expand All @@ -43,7 +44,12 @@ final class DocumentRegistry {
if(!Document.class.isAssignableFrom(method.getDeclaringClass())) {
continue;
}
if(method.isAnnotationPresent(Document.Exclude.class)) {
boolean excluded = method.isDefault();
@Nullable final Document.Exclude exclude = method.getAnnotation(Document.Exclude.class);
if(exclude != null) {
excluded = exclude.value();
}
if(excluded) {
continue;
}
fields.put(method.getName(), DocumentMeta.Field.create(method));
Expand Down
74 changes: 61 additions & 13 deletions src/main/java/net/kyori/cereal/DocumentSerializer.java
Expand Up @@ -37,20 +37,68 @@
/**
* A serializer used for all {@link Document}s.
*/
public final class DocumentSerializer implements JsonDeserializer<Document>, JsonSerializer<Document> {
private final DocumentRegistry registry = new DocumentRegistry();
private final DocumentGenerator generator = new DocumentGenerator(this.registry);

@Override
public Document deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException {
final DocumentMeta<? extends Document> meta = this.registry.meta(TypeToken.of(typeOfT).getRawType().asSubclass(Document.class));
final Object[] fields = meta.deserialize((JsonObject) json, context);
return this.generator.create(meta.type, fields);
public final class DocumentSerializer {
public static JsonSerializer<Document> serializerOnly() {
return new Serializer(new DocumentRegistry());
}

@Override
public JsonElement serialize(final Document src, final Type typeOfSrc, final JsonSerializationContext context) {
final DocumentMeta<? extends Document> meta = this.registry.meta(src.getClass());
return meta.serialize(src, context);
public static JsonDeserializer<Document> deserializerOnly() {
return new Deserializer(new DocumentRegistry());
}

public static <S extends JsonDeserializer<Document> & JsonSerializer<Document>> S both() {
return (S) new Both();
}

private static final class Serializer implements JsonSerializer<Document> {
private final DocumentRegistry registry;

private Serializer(final DocumentRegistry registry) {
this.registry = registry;
}

@Override
public JsonElement serialize(final Document src, final Type typeOfSrc, final JsonSerializationContext context) {
final DocumentMeta<? extends Document> meta = this.registry.meta(src.getClass());
return meta.serialize(src, context);
}
}

private static final class Deserializer implements JsonDeserializer<Document> {
private final DocumentRegistry registry;
private final DocumentGenerator generator;

private Deserializer(final DocumentRegistry registry) {
this.registry = registry;
this.generator = new DocumentGenerator(registry);
}

@Override
public Document deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException {
final DocumentMeta<? extends Document> meta = this.registry.meta(TypeToken.of(typeOfT).getRawType().asSubclass(Document.class));
final Object[] fields = meta.deserialize((JsonObject) json, context);
return this.generator.create(meta.type, fields);
}
}

private static final class Both implements JsonDeserializer<Document>, JsonSerializer<Document> {
private final Serializer serializer;
private final Deserializer deserializer;

private Both() {
final DocumentRegistry registry = new DocumentRegistry();
this.serializer = new Serializer(registry);
this.deserializer = new Deserializer(registry);
}

@Override
public Document deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException {
return this.deserializer.deserialize(json, typeOfT, context);
}

@Override
public JsonElement serialize(final Document src, final Type type, final JsonSerializationContext context) {
return this.serializer.serialize(src, type, context);
}
}
}
25 changes: 23 additions & 2 deletions src/test/java/net/kyori/cereal/CerealTest.java
Expand Up @@ -34,14 +34,16 @@
import java.util.Optional;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

public class CerealTest {
private static final Gson GSON = new GsonBuilder()
.registerTypeHierarchyAdapter(Document.class, new DocumentSerializer())
.registerTypeHierarchyAdapter(Document.class, DocumentSerializer.both())
.create();

@Test
public void test() {
public void testStandard() {
final Entity source = new Entity() {
@Override
public int id() {
Expand Down Expand Up @@ -72,10 +74,29 @@ public Map<String, List<String>> strings() {
assertEquals(source.strings(), target.strings());
}

@Test
public void testWithExclude() {
final ThingWithDefault source = () -> 42;
final String json = GSON.toJson(source);
assertFalse(json.contains("bar"));
assertTrue(json.contains("razz"));
final ThingWithDefault target = GSON.fromJson(json, ThingWithDefault.class);

assertEquals(source.thing(), target.thing());
assertEquals(source.foo(), target.foo());
assertEquals(source.baz(), target.baz());
}

public interface Entity extends Document {
int id();
String name();
Optional<String> value();
Map<String, List<String>> strings();
}

public interface ThingWithDefault extends Document {
int thing();
/* default is excluded by default */ default String foo() { return "bar"; }
@Exclude(false) default String baz() { return "razz"; }
}
}

0 comments on commit 53ffab8

Please sign in to comment.