Skip to content

Commit

Permalink
Adding matcher behavior option to support recursion (#44)
Browse files Browse the repository at this point in the history
  • Loading branch information
monitorjbl committed Jun 20, 2017
1 parent da6b2dd commit eb46730
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 12 deletions.
6 changes: 6 additions & 0 deletions json-view/src/main/java/com/monitorjbl/json/JsonView.java
Expand Up @@ -9,6 +9,7 @@
public class JsonView<T> {
protected final T value;
protected final Map<Class<?>, Match> matches = new HashMap<>();
protected MatcherBehavior matcherBehavior;

protected JsonView(T value) {
this.value = value;
Expand All @@ -27,6 +28,11 @@ public JsonView<T> onClass(Class<?> cls, Match match) {
return this;
}

public JsonView<T> withMatcherBehavior(MatcherBehavior matcherBehavior) {
this.matcherBehavior = matcherBehavior;
return this;
}

public static <E> JsonView<E> with(E value) {
return new JsonView<>(value);
}
Expand Down
Expand Up @@ -18,8 +18,14 @@ public JsonViewModule(JsonViewSerializer jsonView) {
this.jsonView = jsonView;
}

public JsonViewModule withDefaultMatcherBehavior(MatcherBehavior matcherBehavior){
this.jsonView.setDefaultMatcherBehavior(matcherBehavior);
return this;
}

public <E> JsonViewModule registerSerializer(Class<E> cls, JsonSerializer<E> serializer) {
jsonView.registerCustomSerializer(cls, serializer);
return this;
}

}
60 changes: 48 additions & 12 deletions json-view/src/main/java/com/monitorjbl/json/JsonViewSerializer.java
Expand Up @@ -33,6 +33,9 @@
import java.util.UUID;
import java.util.regex.Pattern;

import static com.monitorjbl.json.MatcherBehavior.CLASS_FIRST;
import static com.monitorjbl.json.MatcherBehavior.PATH_FIRST;

public class JsonViewSerializer extends JsonSerializer<JsonView> {

/**
Expand All @@ -45,6 +48,8 @@ public class JsonViewSerializer extends JsonSerializer<JsonView> {
*/
private Map<Class<?>, JsonSerializer<Object>> customSerializersMap = null;

private MatcherBehavior defaultMatcherBehavior = CLASS_FIRST;

public JsonViewSerializer() {
this(1024);
}
Expand Down Expand Up @@ -95,6 +100,15 @@ public void unregisterCustomSerializer(Class<?> cls) {
}
}

/**
* Set the default matcher behavior to be used if the {@link JsonView} object to
* be serialized does not specify one.
*
* @param defaultMatcherBehavior The default behavior to use
*/
public void setDefaultMatcherBehavior(MatcherBehavior defaultMatcherBehavior) {
this.defaultMatcherBehavior = defaultMatcherBehavior;
}

@Override
public void serialize(JsonView result, JsonGenerator jgen, SerializerProvider serializers) throws IOException {
Expand Down Expand Up @@ -339,14 +353,7 @@ && getAnnotation(cls, JsonSerialize.class) == null)
}

@SuppressWarnings("unchecked")
boolean fieldAllowed(Field field, Class declaringClass) {
String name = field.getName();
String prefix = currentPath.length() > 0 ? currentPath + "." : "";
if(Modifier.isStatic(field.getModifiers())) {
return false;
}

//search for matching class
private Match classMatchSearch(Class declaringClass) {
Match match = null;
Class cls = declaringClass;
while(!cls.equals(Object.class) && match == null) {
Expand All @@ -363,10 +370,39 @@ boolean fieldAllowed(Field field, Class declaringClass) {
}
cls = cls.getSuperclass();
}
if(match == null) {
match = currentMatch;
} else {
prefix = "";
return match;
}

@SuppressWarnings("unchecked")
boolean fieldAllowed(Field field, Class declaringClass) {
String name = field.getName();
String prefix = currentPath.length() > 0 ? currentPath + "." : "";
if(Modifier.isStatic(field.getModifiers())) {
return false;
}

// Determine matcher behavior
MatcherBehavior currentBehavior = result.matcherBehavior;
if(currentBehavior == null) {
currentBehavior = JsonViewSerializer.this.defaultMatcherBehavior;
}

//search for matching class
Match match = null;
if(currentBehavior == CLASS_FIRST) {
match = classMatchSearch(declaringClass);
if(match == null) {
match = currentMatch;
} else {
prefix = "";
}
} else if(currentBehavior == PATH_FIRST) {
if(currentMatch != null) {
match = currentMatch;
} else {
match = classMatchSearch(declaringClass);
prefix = "";
}
}

//if there is a match, respect it
Expand Down
18 changes: 18 additions & 0 deletions json-view/src/main/java/com/monitorjbl/json/MatcherBehavior.java
@@ -0,0 +1,18 @@
package com.monitorjbl.json;

/**
* Dictates the order in which to search for matches when serializing objects.
*/
public enum MatcherBehavior {
/**
* Check for matches on an class first before using the
* current path matcher
*/
CLASS_FIRST,

/**
* Check for matches against the current path first before
* looking for a matcher on the class
*/
PATH_FIRST
}
Expand Up @@ -37,6 +37,7 @@
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.function.Consumer;

import static com.monitorjbl.json.Match.match;
import static java.util.Arrays.asList;
Expand Down Expand Up @@ -862,4 +863,52 @@ public void testJsonSerializeAnnotation() throws Exception {
assertTrue(obj.get("customFieldSerializer") instanceof String);
assertEquals(obj.get("customFieldSerializer"), "5[hello]");
}

@Test
public void testPathFirstMatch_defaultBehavior() throws Exception {
TestObject ref = new TestObject();
ref.setInt1(1);
ref.setStr1("sdfdsf");
ref.setStr2("erer");
ref.setRecursion(ref);

Consumer<Map<String, Object>> doTest = obj->{
assertEquals(ref.getInt1(), obj.get("int1"));
assertEquals(ref.getStr1(), obj.get("str1"));
assertEquals(ref.getStr2(), obj.get("str2"));
assertNotNull(obj.get("recursion"));

Map<String, Object> rec1 = (Map<String, Object>) obj.get("recursion");
assertNotNull(rec1.get("recursion"));
assertEquals(ref.getStr1(), rec1.get("str1"));
assertNull(rec1.get("str2"));
assertNull(rec1.get("int1"));

Map<String, Object> rec2 = (Map<String, Object>) rec1.get("recursion");
assertEquals(ref.getStr2(), rec2.get("str2"));
assertNull(rec2.get("str1"));
assertNull(rec2.get("int1"));

assertNull(rec2.get("recursion"));
};


// Perform test with behavior set at default level
sut = new ObjectMapper().registerModule(new JsonViewModule(serializer)
.withDefaultMatcherBehavior(MatcherBehavior.PATH_FIRST));
String serialized = sut.writeValueAsString(JsonView.with(ref)
.onClass(TestObject.class, match()
.include("recursion", "recursion.str1", "recursion.recursion.str2", "str1", "str2", "int1")
.exclude("*")));
doTest.accept(sut.readValue(serialized, HashMap.class));

// Perform test with behavior set at the JsonView level
sut = new ObjectMapper().registerModule(new JsonViewModule(serializer));
serialized = sut.writeValueAsString(JsonView.with(ref)
.withMatcherBehavior(MatcherBehavior.PATH_FIRST)
.onClass(TestObject.class, match()
.include("recursion", "recursion.str1", "recursion.recursion.str2", "str1", "str2", "int1")
.exclude("*")));
doTest.accept(sut.readValue(serialized, HashMap.class));
}
}
Expand Up @@ -57,6 +57,7 @@ public enum TestEnum {VALUE_A, VALUE_B}
@JsonProperty
private String jsonPropNoValue;
private UUID uuid;
private TestObject recursion;

public String getStr1() {
return str1;
Expand Down Expand Up @@ -289,4 +290,12 @@ public UUID getUuid() {
public void setUuid(UUID uuid) {
this.uuid = uuid;
}

public TestObject getRecursion() {
return recursion;
}

public void setRecursion(TestObject recursion) {
this.recursion = recursion;
}
}

0 comments on commit eb46730

Please sign in to comment.