1 package com.fasterxml.jackson.databind.ser;
2 
3 import java.io.IOException;
4 import java.util.*;
5 
6 import com.fasterxml.jackson.annotation.*;
7 import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
8 import com.fasterxml.jackson.core.JsonGenerator;
9 import com.fasterxml.jackson.databind.*;
10 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
11 import com.fasterxml.jackson.databind.json.JsonMapper;
12 
13 /**
14  * This unit test suite tests use of @JsonClass Annotation
15  * with bean serialization.
16  */
17 public class TestJsonSerialize
18     extends BaseMapTest
19 {
20     /*
21     /**********************************************************
22     /* Annotated helper classes
23     /**********************************************************
24      */
25 
26     interface ValueInterface {
getX()27         public int getX();
28     }
29 
30     static class ValueClass
31         implements ValueInterface
32     {
33         @Override
getX()34         public int getX() { return 3; }
getY()35         public int getY() { return 5; }
36     }
37 
38     /**
39      * Test class to verify that <code>JsonSerialize.as</code>
40      * works as expected
41      */
42     static class WrapperClassForAs
43     {
44         @JsonSerialize(as=ValueInterface.class)
getValue()45         public ValueClass getValue() {
46             return new ValueClass();
47         }
48     }
49 
50     // This should indicate that static type be used for all fields
51     @JsonSerialize(typing=JsonSerialize.Typing.STATIC)
52     static class WrapperClassForStaticTyping
53     {
getValue()54         public ValueInterface getValue() {
55             return new ValueClass();
56         }
57     }
58 
59     static class WrapperClassForStaticTyping2
60     {
61         @JsonSerialize(typing=JsonSerialize.Typing.STATIC)
getStaticValue()62         public ValueInterface getStaticValue() {
63             return new ValueClass();
64         }
65 
66         @JsonSerialize(typing=JsonSerialize.Typing.DYNAMIC)
getDynamicValue()67         public ValueInterface getDynamicValue() {
68             return new ValueClass();
69         }
70     }
71 
72     /**
73      * Test bean that has an invalid {@link JsonSerialize} annotation.
74      */
75     static class BrokenClass
76     {
77         // invalid annotation: String not a supertype of Long
78         @JsonSerialize(as=String.class)
getValue()79         public Long getValue() {
80             return Long.valueOf(4L);
81         }
82     }
83 
84     @SuppressWarnings("serial")
85     static class ValueMap extends HashMap<String,ValueInterface> { }
86     @SuppressWarnings("serial")
87     static class ValueList extends ArrayList<ValueInterface> { }
88     @SuppressWarnings("serial")
89     static class ValueLinkedList extends LinkedList<ValueInterface> { }
90 
91     // Classes for [JACKSON-294]
92     static class Foo294
93     {
94         @JsonProperty private String id;
95         @JsonSerialize(using = Bar294Serializer.class)
96         private Bar294 bar;
97 
Foo294()98         public Foo294() { }
Foo294(String id, String id2)99         public Foo294(String id, String id2) {
100             this.id = id;
101             bar = new Bar294(id2);
102         }
103     }
104 
105     static class Bar294{
106         @JsonProperty protected String id;
107         @JsonProperty protected String name;
108 
Bar294()109         public Bar294() { }
Bar294(String id)110         public Bar294(String id) {
111             this.id = id;
112         }
113 
getId()114         public String getId() { return id; }
getName()115         public String getName() { return name; }
116     }
117 
118     static class Bar294Serializer extends JsonSerializer<Bar294>
119     {
120         @Override
serialize(Bar294 bar, JsonGenerator jgen, SerializerProvider provider)121         public void serialize(Bar294 bar, JsonGenerator jgen,
122             SerializerProvider provider) throws IOException
123         {
124             jgen.writeString(bar.id);
125         }
126     }
127 
128     /*
129     /**********************************************************
130     /* Main tests
131     /**********************************************************
132      */
133 
134     final ObjectMapper MAPPER = objectMapper();
135 
136     @SuppressWarnings("unchecked")
testSimpleValueDefinition()137     public void testSimpleValueDefinition() throws Exception
138     {
139         Map<String,Object> result = writeAndMap(MAPPER, new WrapperClassForAs());
140         assertEquals(1, result.size());
141         Object ob = result.get("value");
142         // Should see only "x", not "y"
143         result = (Map<String,Object>) ob;
144         assertEquals(1, result.size());
145         assertEquals(Integer.valueOf(3), result.get("x"));
146     }
147 
testBrokenAnnotation()148     public void testBrokenAnnotation() throws Exception
149     {
150         try {
151             serializeAsString(MAPPER, new BrokenClass());
152             fail("Should not succeed");
153         } catch (Exception e) {
154             verifyException(e, "types not related");
155         }
156     }
157 
158     @SuppressWarnings("unchecked")
testStaticTypingForClass()159     public void testStaticTypingForClass() throws Exception
160     {
161         Map<String,Object> result = writeAndMap(MAPPER, new WrapperClassForStaticTyping());
162         assertEquals(1, result.size());
163         Object ob = result.get("value");
164         // Should see only "x", not "y"
165         result = (Map<String,Object>) ob;
166         assertEquals(1, result.size());
167         assertEquals(Integer.valueOf(3), result.get("x"));
168     }
169 
170     @SuppressWarnings("unchecked")
testMixedTypingForClass()171     public void testMixedTypingForClass() throws Exception
172     {
173         Map<String,Object> result = writeAndMap(MAPPER, new WrapperClassForStaticTyping2());
174         assertEquals(2, result.size());
175 
176         Object obStatic = result.get("staticValue");
177         // Should see only "x", not "y"
178         Map<String,Object> stat = (Map<String,Object>) obStatic;
179         assertEquals(1, stat.size());
180         assertEquals(Integer.valueOf(3), stat.get("x"));
181 
182         Object obDynamic = result.get("dynamicValue");
183         // Should see both
184         Map<String,Object> dyn = (Map<String,Object>) obDynamic;
185         assertEquals(2, dyn.size());
186         assertEquals(Integer.valueOf(3), dyn.get("x"));
187         assertEquals(Integer.valueOf(5), dyn.get("y"));
188     }
189 
testStaticTypingWithMap()190     public void testStaticTypingWithMap() throws Exception
191     {
192         ObjectMapper m = jsonMapperBuilder()
193                 .configure(MapperFeature.USE_STATIC_TYPING, true)
194                 .build();
195         ValueMap map = new ValueMap();
196         map.put("a", new ValueClass());
197         assertEquals("{\"a\":{\"x\":3}}", serializeAsString(m, map));
198     }
199 
testStaticTypingWithArrayList()200     public void testStaticTypingWithArrayList() throws Exception
201     {
202         ObjectMapper m = jsonMapperBuilder()
203                 .configure(MapperFeature.USE_STATIC_TYPING, true)
204                 .build();
205         ValueList list = new ValueList();
206         list.add(new ValueClass());
207         assertEquals("[{\"x\":3}]", m.writeValueAsString(list));
208     }
209 
testStaticTypingWithLinkedList()210     public void testStaticTypingWithLinkedList() throws Exception
211     {
212         ObjectMapper m = jsonMapperBuilder()
213                 .configure(MapperFeature.USE_STATIC_TYPING, true)
214                 .build();
215         ValueLinkedList list = new ValueLinkedList();
216         list.add(new ValueClass());
217         assertEquals("[{\"x\":3}]", serializeAsString(m, list));
218     }
219 
testStaticTypingWithArray()220     public void testStaticTypingWithArray() throws Exception
221     {
222         ObjectMapper m = jsonMapperBuilder()
223                 .configure(MapperFeature.USE_STATIC_TYPING, true)
224                 .build();
225         ValueInterface[] array = new ValueInterface[] { new ValueClass() };
226         assertEquals("[{\"x\":3}]", serializeAsString(m, array));
227     }
228 
testIssue294()229     public void testIssue294() throws Exception
230     {
231         JsonMapper mapper = JsonMapper.builder().enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY).build();
232         assertEquals("{\"bar\":\"barId\",\"id\":\"fooId\"}",
233                 mapper.writeValueAsString(new Foo294("fooId", "barId")));
234     }
235 
236     @JsonPropertyOrder({ "a", "something" })
237     static class Response {
238         public String a = "x";
239 
240         @JsonProperty   //does not show up
isSomething()241         public boolean isSomething() { return true; }
242     }
243 
testWithIsGetter()244     public void testWithIsGetter() throws Exception
245     {
246         ObjectMapper m = new ObjectMapper();
247         m.setVisibility(PropertyAccessor.GETTER, Visibility.NONE)
248         .setVisibility(PropertyAccessor.FIELD, Visibility.ANY)
249         .setVisibility(PropertyAccessor.CREATOR, Visibility.NONE)
250         .setVisibility(PropertyAccessor.IS_GETTER, Visibility.NONE)
251         .setVisibility(PropertyAccessor.SETTER, Visibility.NONE);
252         final String JSON = m.writeValueAsString(new Response());
253         assertEquals(aposToQuotes("{'a':'x','something':true}"), JSON);
254     }
255 }
256