RootNameTest.java
package com.fasterxml.jackson.databind;
import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import com.fasterxml.jackson.databind.json.JsonMapper;
import static org.junit.jupiter.api.Assertions.*;
import static com.fasterxml.jackson.databind.testutil.DatabindTestUtil.a2q;
import static com.fasterxml.jackson.databind.testutil.DatabindTestUtil.verifyException;
/**
* Unit tests dealing with handling of "root element wrapping",
* including configuration of root name to use.
*/
public class RootNameTest
{
@JsonRootName("rudy")
static class Bean {
public int a = 3;
}
@JsonRootName("")
static class RootBeanWithEmpty {
public int a = 2;
}
/*
/**********************************************************
/* Unit tests
/**********************************************************
*/
@Test
public void testRootViaMapper() throws Exception
{
ObjectMapper mapper = rootMapper();
String json = mapper.writeValueAsString(new Bean());
assertEquals("{\"rudy\":{\"a\":3}}", json);
Bean bean = mapper.readValue(json, Bean.class);
assertNotNull(bean);
// also same with explicitly "not defined"...
json = mapper.writeValueAsString(new RootBeanWithEmpty());
assertEquals("{\"RootBeanWithEmpty\":{\"a\":2}}", json);
RootBeanWithEmpty bean2 = mapper.readValue(json, RootBeanWithEmpty.class);
assertNotNull(bean2);
assertEquals(2, bean2.a);
}
@Test
public void testRootViaMapperFails() throws Exception
{
final ObjectMapper mapper = rootMapper();
// First kind of fail, wrong name
try {
mapper.readValue(a2q("{'notRudy':{'a':3}}"), Bean.class);
fail("Should not pass");
} catch (MismatchedInputException e) {
verifyException(e, "Root name ('notRudy') does not match expected ('rudy')");
}
// second: non-Object
try {
mapper.readValue(a2q("[{'rudy':{'a':3}}]"), Bean.class);
fail("Should not pass");
} catch (MismatchedInputException e) {
verifyException(e, "Unexpected token (START_ARRAY");
}
// Third: empty Object
try {
mapper.readValue(a2q("{}]"), Bean.class);
fail("Should not pass");
} catch (MismatchedInputException e) {
verifyException(e, "Current token not FIELD_NAME");
}
// Fourth, stuff after wrapped
try {
mapper.readValue(a2q("{'rudy':{'a':3}, 'extra':3}"), Bean.class);
fail("Should not pass");
} catch (MismatchedInputException e) {
verifyException(e, "Unexpected token");
verifyException(e, "Current token not END_OBJECT (to match wrapper");
}
}
@Test
public void testRootViaReaderFails() throws Exception
{
final ObjectReader reader = rootMapper().readerFor(Bean.class);
// First kind of fail, wrong name
try {
reader.readValue(a2q("{'notRudy':{'a':3}}"));
fail("Should not pass");
} catch (MismatchedInputException e) {
verifyException(e, "Root name ('notRudy') does not match expected ('rudy')");
}
// second: non-Object
try {
reader.readValue(a2q("[{'rudy':{'a':3}}]"));
fail("Should not pass");
} catch (MismatchedInputException e) {
verifyException(e, "Unexpected token (START_ARRAY");
}
// Third: empty Object
try {
reader.readValue(a2q("{}]"));
fail("Should not pass");
} catch (MismatchedInputException e) {
verifyException(e, "Current token not FIELD_NAME");
}
// Fourth, stuff after wrapped
try {
reader.readValue(a2q("{'rudy':{'a':3}, 'extra':3}"));
fail("Should not pass");
} catch (MismatchedInputException e) {
verifyException(e, "Unexpected token");
verifyException(e, "Current token not END_OBJECT (to match wrapper");
}
}
@Test
public void testRootViaWriterAndReader() throws Exception
{
ObjectMapper mapper = rootMapper();
String json = mapper.writer().writeValueAsString(new Bean());
assertEquals("{\"rudy\":{\"a\":3}}", json);
Bean bean = mapper.readerFor(Bean.class).readValue(json);
assertNotNull(bean);
}
@Test
public void testReconfiguringOfWrapping() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
// default: no wrapping
final Bean input = new Bean();
String jsonUnwrapped = mapper.writeValueAsString(input);
assertEquals("{\"a\":3}", jsonUnwrapped);
// secondary: wrapping
String jsonWrapped = mapper.writer(SerializationFeature.WRAP_ROOT_VALUE)
.writeValueAsString(input);
assertEquals("{\"rudy\":{\"a\":3}}", jsonWrapped);
// and then similarly for readers:
Bean result = mapper.readValue(jsonUnwrapped, Bean.class);
assertNotNull(result);
try { // must not have extra wrapping
result = mapper.readerFor(Bean.class).with(DeserializationFeature.UNWRAP_ROOT_VALUE)
.readValue(jsonUnwrapped);
fail("Should have failed");
} catch (MismatchedInputException e) {
verifyException(e, "Root name ('a')");
}
// except wrapping may be expected:
result = mapper.readerFor(Bean.class).with(DeserializationFeature.UNWRAP_ROOT_VALUE)
.readValue(jsonWrapped);
assertNotNull(result);
}
@Test
public void testRootUsingExplicitConfig() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
ObjectWriter writer = mapper.writer().withRootName("wrapper");
String json = writer.writeValueAsString(new Bean());
assertEquals("{\"wrapper\":{\"a\":3}}", json);
ObjectReader reader = mapper.readerFor(Bean.class).withRootName("wrapper");
Bean bean = reader.readValue(json);
assertNotNull(bean);
// also: verify that we can override SerializationFeature as well:
ObjectMapper wrapping = rootMapper();
json = wrapping.writer().withRootName("something").writeValueAsString(new Bean());
assertEquals("{\"something\":{\"a\":3}}", json);
json = wrapping.writer().withRootName("").writeValueAsString(new Bean());
assertEquals("{\"a\":3}", json);
// 21-Apr-2015, tatu: Alternative available with 2.6 as well:
json = wrapping.writer().withoutRootName().writeValueAsString(new Bean());
assertEquals("{\"a\":3}", json);
bean = wrapping.readerFor(Bean.class).withRootName("").readValue(json);
assertNotNull(bean);
assertEquals(3, bean.a);
bean = wrapping.readerFor(Bean.class).withoutRootName().readValue("{\"a\":4}");
assertNotNull(bean);
assertEquals(4, bean.a);
// and back to defaults
bean = wrapping.readerFor(Bean.class).readValue("{\"rudy\":{\"a\":7}}");
assertNotNull(bean);
assertEquals(7, bean.a);
}
/*
/**********************************************************
/* Helper methods
/**********************************************************
*/
private final ObjectMapper ROOT_MAPPER = JsonMapper.builder()
.enable(SerializationFeature.WRAP_ROOT_VALUE)
.enable(DeserializationFeature.UNWRAP_ROOT_VALUE)
.build();
private ObjectMapper rootMapper() {
return ROOT_MAPPER;
}
}