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