INT-3122 Return String Properties as String

Make converted object toString() behave as expected
This commit is contained in:
Eric Bottard
2013-09-04 18:34:58 +02:00
committed by Gary Russell
parent 93f61160e2
commit e24d683478
2 changed files with 55 additions and 24 deletions

View File

@@ -41,8 +41,8 @@ public class JsonPropertyAccessor implements PropertyAccessor {
/**
* The kind of types this can work with.
*/
private static final Class<?>[] SUPPORTED_CLASSES =
new Class[] { String.class, ObjectNode.class, ArrayNode.class };
private static final Class<?>[] SUPPORTED_CLASSES = new Class[] { String.class, ToStringFriendlyJsonNode.class,
ObjectNode.class, ArrayNode.class };
// Note: ObjectMapper is thread-safe
private ObjectMapper objectMapper = new ObjectMapper();
@@ -59,20 +59,28 @@ public class JsonPropertyAccessor implements PropertyAccessor {
return ((index != null && container.has(index)) || container.has(name));
}
private ContainerNode<?> assertContainerNode(JsonNode json) throws AccessException {
if (json instanceof ContainerNode) {
return (ContainerNode<?>) json;
}
else {
throw new AccessException("Can not act on json that is not a ContainerNode: "
+ json.getClass().getSimpleName());
}
}
private ContainerNode<?> asJson(Object target) throws AccessException {
if (target instanceof ContainerNode) {
return (ContainerNode<?>) target;
}
else if (target instanceof ToStringFriendlyJsonNode) {
ToStringFriendlyJsonNode wrapper = (ToStringFriendlyJsonNode) target;
return assertContainerNode(wrapper.node);
}
else if (target instanceof String) {
try {
JsonNode json = this.objectMapper.readTree((String) target);
if (json instanceof ContainerNode) {
return (ContainerNode<?>) json;
}
else {
throw new AccessException("Can not act on json that is not a ContainerNode: "
+ json.getClass().getSimpleName());
}
return assertContainerNode(json);
}
catch (JsonProcessingException e) {
throw new AccessException("Exception while trying to deserialize String", e);
@@ -103,10 +111,10 @@ public class JsonPropertyAccessor implements PropertyAccessor {
ContainerNode<?> container = asJson(target);
Integer index = maybeIndex(name);
if (index != null && container.has(index)) {
return new TypedValue(container.get(index));
return new TypedValue(wrap(container.get(index)));
}
else {
return new TypedValue(container.get(name));
return new TypedValue(wrap(container.get(name)));
}
}
@@ -125,4 +133,28 @@ public class JsonPropertyAccessor implements PropertyAccessor {
this.objectMapper = objectMapper;
}
private ToStringFriendlyJsonNode wrap(JsonNode json) {
return new ToStringFriendlyJsonNode(json);
}
public static class ToStringFriendlyJsonNode {
private final JsonNode node;
public ToStringFriendlyJsonNode(JsonNode node) {
this.node = node;
}
@Override
public String toString() {
if (node.isValueNode()) {
// This is to avoid quotes around a TextNode for example
return node.asText();
}
else {
return node.toString();
}
}
}
}

View File

@@ -25,7 +25,6 @@ import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
@@ -49,20 +48,20 @@ public class JsonPropertyAccessorTests {
@Test
public void testSimpleLookup() throws Exception {
Object json = mapper.readTree("{\"foo\": \"bar\"}");
JsonNode actual = evaluate(json, "foo", JsonNode.class);
assertEquals("bar", actual.asText());
Object actual = evaluate(json, "foo", Object.class);
assertEquals("bar", actual.toString());
}
@Test(expected = SpelEvaluationException.class)
public void testUnsupportedJsonConstruct() throws Exception {
Object json = mapper.readTree("\"foo\"");
evaluate(json, "fizz", JsonNode.class);
evaluate(json, "fizz", Object.class);
}
@Test(expected = SpelEvaluationException.class)
public void testMissingProperty() throws Exception {
Object json = mapper.readTree("{\"foo\": \"bar\"}");
evaluate(json, "fizz", JsonNode.class);
evaluate(json, "fizz", Object.class);
}
@Test
@@ -70,8 +69,8 @@ public class JsonPropertyAccessorTests {
Object json = mapper.readTree("[3, 4, 5]");
// JsonNode actual = evaluate("1", json, JsonNode.class); // Does not work
// JsonNode actual = evaluate("'1'", json, JsonNode.class); // Does not work
JsonNode actual = evaluate(json, "['1']", JsonNode.class);
assertEquals(4, actual.asInt());
Object actual = evaluate(json, "['1']", Object.class);
assertEquals("4", actual.toString());
}
@Test
@@ -79,22 +78,22 @@ public class JsonPropertyAccessorTests {
Object json = mapper.readTree("[[3], [4, 5], []]");
// JsonNode actual = evaluate("1.1", json, JsonNode.class); // Does not work
// JsonNode actual = evaluate("[1][1]", json, JsonNode.class); // Does not work
JsonNode actual = evaluate(json, "['1']['1']", JsonNode.class);
assertEquals(5, actual.asInt());
Object actual = evaluate(json, "['1']['1']", Object.class);
assertEquals("5", actual.toString());
}
@Test
public void testNestedHashConstruct() throws Exception {
Object json = mapper.readTree("{\"foo\": {\"bar\": 4, \"fizz\": 5} }");
JsonNode actual = evaluate(json, "foo.fizz", JsonNode.class);
assertEquals(5, actual.asInt());
Object actual = evaluate(json, "foo.fizz", Object.class);
assertEquals("5", actual.toString());
}
@Test
public void testImplicitStringConversion() throws Exception {
String json = "{\"foo\": {\"bar\": 4, \"fizz\": 5} }";
JsonNode actual = evaluate(json, "foo.fizz", JsonNode.class);
assertEquals(5, actual.asInt());
Object actual = evaluate(json, "foo.fizz", Object.class);
assertEquals("5", actual.toString());
}
@Test(expected = SpelEvaluationException.class)