Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 6, MultiMap deserializer fix #8

Merged
merged 2 commits into from
May 29, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.KeyDeserializer;
Expand All @@ -19,6 +20,7 @@
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;

/**
Expand Down Expand Up @@ -124,6 +126,20 @@ protected abstract JsonDeserializer<?> _createContextual(MapLikeType t,
public T deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException,
JsonProcessingException {

//check if ACCEPT_SINGLE_VALUE_AS_ARRAY feature is enabled
if (ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)) {
return deserializeFromSingleValue(jp, ctxt);
}
// if not deserialize the normal way
else {
return deserializeContents(jp, ctxt);
}

}

private T deserializeContents(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {

T multimap = createMultimap();

expect(jp, JsonToken.START_OBJECT);
Expand All @@ -144,8 +160,7 @@ public T deserialize(JsonParser jp, DeserializationContext ctxt) throws IOExcept
if (jp.getCurrentToken() == JsonToken.VALUE_NULL) {
value = null;
} else if (elementTypeDeserializer != null) {
value = elementDeserializer.deserializeWithType(jp, ctxt,
elementTypeDeserializer);
value = elementDeserializer.deserializeWithType(jp, ctxt, elementTypeDeserializer);
} else {
value = elementDeserializer.deserialize(jp, ctxt);
}
Expand All @@ -156,7 +171,59 @@ public T deserialize(JsonParser jp, DeserializationContext ctxt) throws IOExcept
return multimap;
}
try {
@SuppressWarnings("unchecked") T map = (T) creatorMethod.invoke(null, multimap);
@SuppressWarnings("unchecked")
T map = (T) creatorMethod.invoke(null, multimap);
return map;
} catch (InvocationTargetException e) {
throw new JsonMappingException(jp, "Could not map to " + type, _peel(e));
} catch (IllegalArgumentException e) {
throw new JsonMappingException(jp, "Could not map to " + type, _peel(e));
} catch (IllegalAccessException e) {
throw new JsonMappingException(jp, "Could not map to " + type, _peel(e));
}
}

private T deserializeFromSingleValue(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {

T multimap = createMultimap();

expect(jp, JsonToken.START_OBJECT);

while (jp.nextToken() != JsonToken.END_OBJECT) {
final Object key;
if (keyDeserializer != null) {
key = keyDeserializer.deserializeKey(jp.getCurrentName(), ctxt);
} else {
key = jp.getCurrentName();
}

jp.nextToken();

// if there is an array, parse the array and add the elements
if (jp.currentToken() == JsonToken.START_ARRAY) {

while (jp.nextToken() != JsonToken.END_ARRAY) {
// get the current token value
final Object value = getCurrentTokenValue(jp, ctxt);
// add the token value to the map
multimap.put(key, value);
}
}
// if the element is a String, then add it as a List
else {
// get the current token value
final Object value = getCurrentTokenValue(jp, ctxt);
// add the single value as a list
multimap.put(key, Collections.singletonList(value));
}
}
if (creatorMethod == null) {
return multimap;
}
try {
@SuppressWarnings("unchecked")
T map = (T) creatorMethod.invoke(null, multimap);
return map;
} catch (InvocationTargetException e) {
throw new JsonMappingException(jp, "Could not map to " + type, _peel(e));
Expand All @@ -167,6 +234,20 @@ public T deserialize(JsonParser jp, DeserializationContext ctxt) throws IOExcept
}
}

private Object getCurrentTokenValue(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {

final Object value;
if (jp.getCurrentToken() == JsonToken.VALUE_NULL) {
value = null;
} else if (elementTypeDeserializer != null) {
value = elementDeserializer.deserializeWithType(jp, ctxt, elementTypeDeserializer);
} else {
value = elementDeserializer.deserialize(jp, ctxt);
}
return value;
}

private void expect(JsonParser jp, JsonToken token) throws IOException {
if (jp.getCurrentToken() != token) {
throw new JsonMappingException(jp, "Expecting " + token + ", found " + jp.getCurrentToken(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.guava.pojo.AddOp;
import com.fasterxml.jackson.datatype.guava.pojo.MathOp;
Expand Down Expand Up @@ -309,4 +310,44 @@ public void testPolymorphicValue() throws IOException {
ImmutableMultimapWrapper output = MAPPER.readValue(json, ImmutableMultimapWrapper.class);
assertEquals(input, output);
}

public void testFromSingleValue() throws Exception
{
ObjectMapper mapper = mapperWithModule()
.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
SampleMultiMapTest sampleTest = mapper.readValue("{\"map\":{\"test\":\"value\"}}",
new TypeReference<SampleMultiMapTest>() { });

assertEquals(1, sampleTest.map.get("test").size());
}

public void testFromMultiValueWithSingleValueOptionEnabled() throws Exception
{
ObjectMapper mapper = mapperWithModule()
.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);

SampleMultiMapTest sampleTest = mapper.readValue("{\"map\":{\"test\":\"val\",\"test1\":[\"val1\",\"val2\"]}}",
new TypeReference<SampleMultiMapTest>() { });

assertEquals(1, sampleTest.map.get("test").size());
assertEquals(2, sampleTest.map.get("test1").size());

}

public void testFromMultiValueWithNoSingleValueOptionEnabled() throws Exception
{
ObjectMapper mapper = mapperWithModule();

SampleMultiMapTest sampleTest = mapper.readValue("{\"map\":{\"test\":[\"val\"],\"test1\":[\"val1\",\"val2\"]}}",
new TypeReference<SampleMultiMapTest>() { });

assertEquals(1, sampleTest.map.get("test").size());
assertEquals(2, sampleTest.map.get("test1").size());

}

//Sample class for testing multimaps single value option
static class SampleMultiMapTest {
public HashMultimap<String, String> map;
}
}