1 package com.fasterxml.jackson.databind; 2 3 import com.fasterxml.jackson.annotation.*; 4 5 import com.fasterxml.jackson.databind.ObjectMapper; 6 import com.fasterxml.jackson.databind.exc.MismatchedInputException; 7 import com.fasterxml.jackson.databind.json.JsonMapper; 8 9 /** 10 * Unit tests dealing with handling of "root element wrapping", 11 * including configuration of root name to use. 12 */ 13 public class TestRootName extends BaseMapTest 14 { 15 @JsonRootName("rudy") 16 static class Bean { 17 public int a = 3; 18 } 19 20 @JsonRootName("") 21 static class RootBeanWithEmpty { 22 public int a = 2; 23 } 24 25 /* 26 /********************************************************** 27 /* Unit tests 28 /********************************************************** 29 */ 30 testRootViaMapper()31 public void testRootViaMapper() throws Exception 32 { 33 ObjectMapper mapper = rootMapper(); 34 String json = mapper.writeValueAsString(new Bean()); 35 assertEquals("{\"rudy\":{\"a\":3}}", json); 36 Bean bean = mapper.readValue(json, Bean.class); 37 assertNotNull(bean); 38 39 // also same with explicitly "not defined"... 40 json = mapper.writeValueAsString(new RootBeanWithEmpty()); 41 assertEquals("{\"RootBeanWithEmpty\":{\"a\":2}}", json); 42 RootBeanWithEmpty bean2 = mapper.readValue(json, RootBeanWithEmpty.class); 43 assertNotNull(bean2); 44 assertEquals(2, bean2.a); 45 } 46 testRootViaMapperFails()47 public void testRootViaMapperFails() throws Exception 48 { 49 final ObjectMapper mapper = rootMapper(); 50 // First kind of fail, wrong name 51 try { 52 mapper.readValue(aposToQuotes("{'notRudy':{'a':3}}"), Bean.class); 53 fail("Should not pass"); 54 } catch (MismatchedInputException e) { 55 verifyException(e, "Root name 'notRudy' does not match expected ('rudy')"); 56 } 57 58 // second: non-Object 59 try { 60 mapper.readValue(aposToQuotes("[{'rudy':{'a':3}}]"), Bean.class); 61 fail("Should not pass"); 62 } catch (MismatchedInputException e) { 63 verifyException(e, "Unexpected token (START_ARRAY"); 64 } 65 66 // Third: empty Object 67 try { 68 mapper.readValue(aposToQuotes("{}]"), Bean.class); 69 fail("Should not pass"); 70 } catch (MismatchedInputException e) { 71 verifyException(e, "Current token not FIELD_NAME"); 72 } 73 74 // Fourth, stuff after wrapped 75 try { 76 mapper.readValue(aposToQuotes("{'rudy':{'a':3}, 'extra':3}"), Bean.class); 77 fail("Should not pass"); 78 } catch (MismatchedInputException e) { 79 verifyException(e, "Unexpected token"); 80 verifyException(e, "Current token not END_OBJECT (to match wrapper"); 81 } 82 } 83 testRootViaReaderFails()84 public void testRootViaReaderFails() throws Exception 85 { 86 final ObjectReader reader = rootMapper().readerFor(Bean.class); 87 // First kind of fail, wrong name 88 try { 89 reader.readValue(aposToQuotes("{'notRudy':{'a':3}}")); 90 fail("Should not pass"); 91 } catch (MismatchedInputException e) { 92 verifyException(e, "Root name 'notRudy' does not match expected ('rudy')"); 93 } 94 95 // second: non-Object 96 try { 97 reader.readValue(aposToQuotes("[{'rudy':{'a':3}}]")); 98 fail("Should not pass"); 99 } catch (MismatchedInputException e) { 100 verifyException(e, "Unexpected token (START_ARRAY"); 101 } 102 103 // Third: empty Object 104 try { 105 reader.readValue(aposToQuotes("{}]")); 106 fail("Should not pass"); 107 } catch (MismatchedInputException e) { 108 verifyException(e, "Current token not FIELD_NAME"); 109 } 110 111 // Fourth, stuff after wrapped 112 try { 113 reader.readValue(aposToQuotes("{'rudy':{'a':3}, 'extra':3}")); 114 fail("Should not pass"); 115 } catch (MismatchedInputException e) { 116 verifyException(e, "Unexpected token"); 117 verifyException(e, "Current token not END_OBJECT (to match wrapper"); 118 } 119 } 120 testRootViaWriterAndReader()121 public void testRootViaWriterAndReader() throws Exception 122 { 123 ObjectMapper mapper = rootMapper(); 124 String json = mapper.writer().writeValueAsString(new Bean()); 125 assertEquals("{\"rudy\":{\"a\":3}}", json); 126 Bean bean = mapper.readerFor(Bean.class).readValue(json); 127 assertNotNull(bean); 128 } 129 testReconfiguringOfWrapping()130 public void testReconfiguringOfWrapping() throws Exception 131 { 132 ObjectMapper mapper = new ObjectMapper(); 133 // default: no wrapping 134 final Bean input = new Bean(); 135 String jsonUnwrapped = mapper.writeValueAsString(input); 136 assertEquals("{\"a\":3}", jsonUnwrapped); 137 // secondary: wrapping 138 String jsonWrapped = mapper.writer(SerializationFeature.WRAP_ROOT_VALUE) 139 .writeValueAsString(input); 140 assertEquals("{\"rudy\":{\"a\":3}}", jsonWrapped); 141 142 // and then similarly for readers: 143 Bean result = mapper.readValue(jsonUnwrapped, Bean.class); 144 assertNotNull(result); 145 try { // must not have extra wrapping 146 result = mapper.readerFor(Bean.class).with(DeserializationFeature.UNWRAP_ROOT_VALUE) 147 .readValue(jsonUnwrapped); 148 fail("Should have failed"); 149 } catch (JsonMappingException e) { 150 verifyException(e, "Root name 'a'"); 151 } 152 // except wrapping may be expected: 153 result = mapper.readerFor(Bean.class).with(DeserializationFeature.UNWRAP_ROOT_VALUE) 154 .readValue(jsonWrapped); 155 assertNotNull(result); 156 } 157 158 // [JACKSON-764] testRootUsingExplicitConfig()159 public void testRootUsingExplicitConfig() throws Exception 160 { 161 ObjectMapper mapper = new ObjectMapper(); 162 ObjectWriter writer = mapper.writer().withRootName("wrapper"); 163 String json = writer.writeValueAsString(new Bean()); 164 assertEquals("{\"wrapper\":{\"a\":3}}", json); 165 166 ObjectReader reader = mapper.readerFor(Bean.class).withRootName("wrapper"); 167 Bean bean = reader.readValue(json); 168 assertNotNull(bean); 169 170 // also: verify that we can override SerializationFeature as well: 171 ObjectMapper wrapping = rootMapper(); 172 json = wrapping.writer().withRootName("something").writeValueAsString(new Bean()); 173 assertEquals("{\"something\":{\"a\":3}}", json); 174 json = wrapping.writer().withRootName("").writeValueAsString(new Bean()); 175 assertEquals("{\"a\":3}", json); 176 177 // 21-Apr-2015, tatu: Alternative available with 2.6 as well: 178 json = wrapping.writer().withoutRootName().writeValueAsString(new Bean()); 179 assertEquals("{\"a\":3}", json); 180 181 bean = wrapping.readerFor(Bean.class).withRootName("").readValue(json); 182 assertNotNull(bean); 183 assertEquals(3, bean.a); 184 185 bean = wrapping.readerFor(Bean.class).withoutRootName().readValue("{\"a\":4}"); 186 assertNotNull(bean); 187 assertEquals(4, bean.a); 188 189 // and back to defaults 190 bean = wrapping.readerFor(Bean.class).readValue("{\"rudy\":{\"a\":7}}"); 191 assertNotNull(bean); 192 assertEquals(7, bean.a); 193 } 194 195 /* 196 /********************************************************** 197 /* Helper methods 198 /********************************************************** 199 */ 200 201 private final ObjectMapper ROOT_MAPPER = JsonMapper.builder() 202 .enable(SerializationFeature.WRAP_ROOT_VALUE) 203 .enable(DeserializationFeature.UNWRAP_ROOT_VALUE) 204 .build(); 205 rootMapper()206 private ObjectMapper rootMapper() { 207 return ROOT_MAPPER; 208 } 209 } 210