1 package com.fasterxml.jackson.databind.struct; 2 3 import java.util.HashMap; 4 import java.util.Map; 5 6 import com.fasterxml.jackson.annotation.*; 7 import com.fasterxml.jackson.annotation.JsonFormat.Shape; 8 9 import com.fasterxml.jackson.databind.*; 10 import com.fasterxml.jackson.databind.exc.MismatchedInputException; 11 import com.fasterxml.jackson.databind.introspect.Annotated; 12 import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; 13 14 public class TestPOJOAsArray extends BaseMapTest 15 { 16 static class PojoAsArrayWrapper 17 { 18 @JsonFormat(shape=JsonFormat.Shape.ARRAY) 19 public PojoAsArray value; 20 PojoAsArrayWrapper()21 public PojoAsArrayWrapper() { } PojoAsArrayWrapper(String name, int x, int y, boolean c)22 public PojoAsArrayWrapper(String name, int x, int y, boolean c) { 23 value = new PojoAsArray(name, x, y, c); 24 } 25 } 26 27 @JsonPropertyOrder(alphabetic=true) 28 static class NonAnnotatedXY { 29 public int x, y; 30 NonAnnotatedXY()31 public NonAnnotatedXY() { } NonAnnotatedXY(int x0, int y0)32 public NonAnnotatedXY(int x0, int y0) { 33 x = x0; 34 y = y0; 35 } 36 } 37 38 // note: must be serialized/deserialized alphabetically; fields NOT declared in that order 39 @JsonPropertyOrder(alphabetic=true) 40 static class PojoAsArray 41 { 42 public int x, y; 43 public String name; 44 public boolean complete; 45 PojoAsArray()46 public PojoAsArray() { } PojoAsArray(String name, int x, int y, boolean c)47 public PojoAsArray(String name, int x, int y, boolean c) { 48 this.name = name; 49 this.x = x; 50 this.y = y; 51 this.complete = c; 52 } 53 } 54 55 @JsonPropertyOrder(alphabetic=true) 56 @JsonFormat(shape=JsonFormat.Shape.ARRAY) 57 static class FlatPojo 58 { 59 public int x, y; 60 public String name; 61 public boolean complete; 62 FlatPojo()63 public FlatPojo() { } FlatPojo(String name, int x, int y, boolean c)64 public FlatPojo(String name, int x, int y, boolean c) { 65 this.name = name; 66 this.x = x; 67 this.y = y; 68 this.complete = c; 69 } 70 } 71 72 static class ForceArraysIntrospector extends JacksonAnnotationIntrospector 73 { 74 private static final long serialVersionUID = 1L; 75 76 @Override findFormat(Annotated a)77 public JsonFormat.Value findFormat(Annotated a) { 78 return new JsonFormat.Value().withShape(JsonFormat.Shape.ARRAY); 79 } 80 } 81 82 static class A { 83 public B value = new B(); 84 } 85 86 @JsonPropertyOrder(alphabetic=true) 87 static class B { 88 public int x = 1; 89 public int y = 2; 90 } 91 92 @JsonFormat(shape=Shape.ARRAY) 93 static class SingleBean { 94 public String name = "foo"; 95 } 96 97 @JsonPropertyOrder(alphabetic=true) 98 @JsonFormat(shape=Shape.ARRAY) 99 static class TwoStringsBean { 100 public String bar = null; 101 public String foo = "bar"; 102 } 103 104 @JsonFormat(shape=JsonFormat.Shape.ARRAY) 105 @JsonPropertyOrder(alphabetic=true) 106 static class AsArrayWithMap 107 { 108 public Map<Integer,Integer> attrs; 109 AsArrayWithMap()110 public AsArrayWithMap() { } AsArrayWithMap(int x, int y)111 public AsArrayWithMap(int x, int y) { 112 attrs = new HashMap<Integer,Integer>(); 113 attrs.put(x, y); 114 } 115 } 116 117 @JsonFormat(shape=JsonFormat.Shape.ARRAY) 118 static class CreatorWithIndex { 119 protected int _a, _b; 120 121 @JsonCreator CreatorWithIndex(@sonPropertyindex=0, value="a") int a, @JsonProperty(index=1, value="b") int b)122 public CreatorWithIndex(@JsonProperty(index=0, value="a") int a, 123 @JsonProperty(index=1, value="b") int b) { 124 this._a = a; 125 this._b = b; 126 } 127 } 128 129 /* 130 /***************************************************** 131 /* Basic tests 132 /***************************************************** 133 */ 134 135 private final static ObjectMapper MAPPER = new ObjectMapper(); 136 137 /** 138 * Test that verifies that property annotation works 139 */ testReadSimplePropertyValue()140 public void testReadSimplePropertyValue() throws Exception 141 { 142 String json = "{\"value\":[true,\"Foobar\",42,13]}"; 143 PojoAsArrayWrapper p = MAPPER.readValue(json, PojoAsArrayWrapper.class); 144 assertNotNull(p.value); 145 assertTrue(p.value.complete); 146 assertEquals("Foobar", p.value.name); 147 assertEquals(42, p.value.x); 148 assertEquals(13, p.value.y); 149 } 150 151 /** 152 * Test that verifies that Class annotation works 153 */ testReadSimpleRootValue()154 public void testReadSimpleRootValue() throws Exception 155 { 156 String json = "[false,\"Bubba\",1,2]"; 157 FlatPojo p = MAPPER.readValue(json, FlatPojo.class); 158 assertFalse(p.complete); 159 assertEquals("Bubba", p.name); 160 assertEquals(1, p.x); 161 assertEquals(2, p.y); 162 } 163 164 /** 165 * Test that verifies that property annotation works 166 */ testWriteSimplePropertyValue()167 public void testWriteSimplePropertyValue() throws Exception 168 { 169 String json = MAPPER.writeValueAsString(new PojoAsArrayWrapper("Foobar", 42, 13, true)); 170 // will have wrapper POJO, then POJO-as-array.. 171 assertEquals("{\"value\":[true,\"Foobar\",42,13]}", json); 172 } 173 174 /** 175 * Test that verifies that Class annotation works 176 */ testWriteSimpleRootValue()177 public void testWriteSimpleRootValue() throws Exception 178 { 179 String json = MAPPER.writeValueAsString(new FlatPojo("Bubba", 1, 2, false)); 180 // will have wrapper POJO, then POJO-as-array.. 181 assertEquals("[false,\"Bubba\",1,2]", json); 182 } 183 184 // [Issue#223] testNullColumn()185 public void testNullColumn() throws Exception 186 { 187 assertEquals("[null,\"bar\"]", MAPPER.writeValueAsString(new TwoStringsBean())); 188 } 189 190 /* 191 /***************************************************** 192 /* Compatibility with "single-elem as array" feature 193 /***************************************************** 194 */ 195 testSerializeAsArrayWithSingleProperty()196 public void testSerializeAsArrayWithSingleProperty() throws Exception { 197 ObjectMapper mapper = new ObjectMapper(); 198 mapper.enable(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED); 199 String json = mapper.writeValueAsString(new SingleBean()); 200 assertEquals("\"foo\"", json); 201 } 202 testBeanAsArrayUnwrapped()203 public void testBeanAsArrayUnwrapped() throws Exception 204 { 205 ObjectMapper mapper = new ObjectMapper(); 206 mapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY); 207 SingleBean result = mapper.readValue("[\"foobar\"]", SingleBean.class); 208 assertNotNull(result); 209 assertEquals("foobar", result.name); 210 } 211 212 /* 213 /***************************************************** 214 /* Round-trip tests 215 /***************************************************** 216 */ 217 testAnnotationOverride()218 public void testAnnotationOverride() throws Exception 219 { 220 // by default, POJOs become JSON Objects; 221 assertEquals("{\"value\":{\"x\":1,\"y\":2}}", MAPPER.writeValueAsString(new A())); 222 223 // but override should change it: 224 ObjectMapper mapper2 = new ObjectMapper(); 225 mapper2.setAnnotationIntrospector(new ForceArraysIntrospector()); 226 assertEquals("[[1,2]]", mapper2.writeValueAsString(new A())); 227 228 // and allow reading back, too 229 } 230 testWithMaps()231 public void testWithMaps() throws Exception 232 { 233 AsArrayWithMap input = new AsArrayWithMap(1, 2); 234 String json = MAPPER.writeValueAsString(input); 235 AsArrayWithMap output = MAPPER.readValue(json, AsArrayWithMap.class); 236 assertNotNull(output); 237 assertNotNull(output.attrs); 238 assertEquals(1, output.attrs.size()); 239 assertEquals(Integer.valueOf(2), output.attrs.get(1)); 240 } 241 testSimpleWithIndex()242 public void testSimpleWithIndex() throws Exception 243 { 244 // as POJO: 245 // CreatorWithIndex value = MAPPER.readValue(aposToQuotes("{'b':1,'a':2}"), 246 CreatorWithIndex value = MAPPER.readValue(aposToQuotes("[2,1]"), 247 CreatorWithIndex.class); 248 assertEquals(2, value._a); 249 assertEquals(1, value._b); 250 } 251 testWithConfigOverrides()252 public void testWithConfigOverrides() throws Exception 253 { 254 ObjectMapper mapper = new ObjectMapper(); 255 mapper.configOverride(NonAnnotatedXY.class) 256 .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.ARRAY)); 257 String json = mapper.writeValueAsString(new NonAnnotatedXY(2, 3)); 258 assertEquals("[2,3]", json); 259 260 // also, read it back 261 NonAnnotatedXY result = mapper.readValue(json, NonAnnotatedXY.class); 262 assertNotNull(result); 263 assertEquals(3, result.y); 264 } 265 266 /* 267 /***************************************************** 268 /* Failure tests 269 /***************************************************** 270 */ 271 testUnknownExtraProp()272 public void testUnknownExtraProp() throws Exception 273 { 274 String json = "{\"value\":[true,\"Foobar\",42,13, false]}"; 275 try { 276 MAPPER.readValue(json, PojoAsArrayWrapper.class); 277 fail("should not pass with extra element"); 278 } catch (MismatchedInputException e) { 279 verifyException(e, "Unexpected JSON values"); 280 } 281 282 // but actually fine if skip-unknown set 283 PojoAsArrayWrapper v = MAPPER.readerFor(PojoAsArrayWrapper.class) 284 .without(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) 285 .readValue(json); 286 assertNotNull(v); 287 // note: +1 for both so 288 assertEquals(v.value.x, 42); 289 assertEquals(v.value.y, 13); 290 assertTrue(v.value.complete); 291 assertEquals("Foobar", v.value.name); 292 } 293 } 294