• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.core.JsonGenerator;
8 import com.fasterxml.jackson.databind.*;
9 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
10 import com.fasterxml.jackson.databind.module.SimpleModule;
11 import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer;
12 import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
13 
14 /**
15  * This unit test suite tests functioning of {@link JsonValue}
16  * annotation with bean serialization.
17  */
18 @SuppressWarnings("serial")
19 public class JsonValueTest
20     extends BaseMapTest
21 {
22     static class ValueClass<T>
23     {
24         final T _value;
25 
ValueClass(T v)26         public ValueClass(T v) { _value = v; }
27 
value()28         @JsonValue T value() { return _value; }
29     }
30 
31     static class FieldValueClass<T>
32     {
33         @JsonValue(true)
34         final T _value;
35 
FieldValueClass(T v)36         public FieldValueClass(T v) { _value = v; }
37     }
38 
39     /**
40      * Another test class to check that it is also possible to
41      * force specific serializer to use with @JsonValue annotated
42      * method. Difference is between Integer serialization, and
43      * conversion to a Json String.
44      */
45     final static class ToStringValueClass<T>
46         extends ValueClass<T>
47     {
ToStringValueClass(T value)48         public ToStringValueClass(T value) { super(value); }
49 
50         // Also, need to use this annotation to help
51         @JsonSerialize(using=ToStringSerializer.class)
52         @Override
value()53         @JsonValue T value() { return super.value(); }
54     }
55 
56     final static class ToStringValueClass2
57         extends ValueClass<String>
58     {
ToStringValueClass2(String value)59         public ToStringValueClass2(String value) { super(value); }
60 
61         // Simple as well, but let's ensure that other getters won't matter...
62 
getFoobar()63         @JsonProperty int getFoobar() { return 4; }
64 
getSomethingElse()65         public String[] getSomethingElse() { return new String[] { "1", "a" }; }
66     }
67 
68     static class ValueBase {
69         public String a = "a";
70     }
71 
72     static class ValueType extends ValueBase {
73         public String b = "b";
74     }
75 
76     // Finally, let's also test static vs dynamic type
77     static class ValueWrapper {
78         @JsonValue
getX()79         public ValueBase getX() { return new ValueType(); }
80     }
81 
82     static class MapBean
83     {
84         @JsonValue
toMap()85         public Map<String,String> toMap()
86         {
87             HashMap<String,String> map = new HashMap<String,String>();
88             map.put("a", "1");
89             return map;
90         }
91     }
92 
93     static class MapFieldBean
94     {
95         @JsonValue
96         Map<String,String> stuff = new HashMap<>();
97         {
98             stuff.put("b", "2");
99         }
100     }
101 
102     static class MapAsNumber extends HashMap<String,String>
103     {
104         @JsonValue
value()105         public int value() { return 42; }
106     }
107 
108     static class ListAsNumber extends ArrayList<Integer>
109     {
110         @JsonValue
value()111         public int value() { return 13; }
112     }
113 
114     // Just to ensure it's possible to disable annotation (usually
115     // via mix-ins, but here directly)
116     @JsonPropertyOrder({ "x", "y" })
117     static class DisabledJsonValue {
118         @JsonValue(false)
119         public int x = 1;
120 
121         @JsonValue(false)
getY()122         public int getY() { return 2; }
123     }
124 
125     static class IntExtBean {
126         public List<Internal> values = new ArrayList<Internal>();
127 
add(int v)128         public void add(int v) { values.add(new Internal(v)); }
129     }
130 
131     static class Internal {
132         public int value;
133 
Internal(int v)134         public Internal(int v) { value = v; }
135 
136         @JsonValue
asExternal()137         public External asExternal() { return new External(this); }
138     }
139 
140     static class External {
141         public int i;
142 
External(Internal e)143         External(Internal e) { i = e.value; }
144     }
145 
146     // [databind#167]
147 
148     @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "boingo")
149     @JsonSubTypes(value = {@JsonSubTypes.Type(name = "boopsy", value = AdditionInterfaceImpl.class)
150     })
151     static interface AdditionInterface
152     {
add(int in)153         public int add(int in);
154     }
155 
156     public static class AdditionInterfaceImpl implements AdditionInterface
157     {
158 	    private final int toAdd;
159 
160 	    @JsonCreator
AdditionInterfaceImpl(@sonProperty"toAdd") int toAdd)161 	    public AdditionInterfaceImpl(@JsonProperty("toAdd") int toAdd) {
162 	      this.toAdd = toAdd;
163 	    }
164 
165 	    @JsonProperty
getToAdd()166 	    public int getToAdd() {
167 	      return toAdd;
168 	    }
169 
170 	    @Override
add(int in)171 	    public int add(int in) {
172 	      return in + toAdd;
173 	    }
174     }
175 
176     static class Bean838 {
177         @JsonValue
value()178         public String value() {
179             return "value";
180         }
181     }
182 
183     static class Bean838Serializer extends StdScalarSerializer<Bean838>
184     {
Bean838Serializer()185         public Bean838Serializer() {
186             super(Bean838.class);
187         }
188 
189         @Override
serialize(Bean838 value, JsonGenerator gen, SerializerProvider provider)190         public void serialize(Bean838 value, JsonGenerator gen,
191                 SerializerProvider provider) throws IOException {
192             gen.writeNumber(42);
193         }
194     }
195 
196     /*
197     /*********************************************************
198     /* Test cases
199     /*********************************************************
200      */
201 
202     private final ObjectMapper MAPPER = new ObjectMapper();
203 
testSimpleMethodJsonValue()204     public void testSimpleMethodJsonValue() throws Exception
205     {
206         assertEquals("\"abc\"", MAPPER.writeValueAsString(new ValueClass<String>("abc")));
207         assertEquals("null", MAPPER.writeValueAsString(new ValueClass<String>(null)));
208     }
209 
testSimpleFieldJsonValue()210     public void testSimpleFieldJsonValue() throws Exception
211     {
212         assertEquals("\"abc\"", MAPPER.writeValueAsString(new FieldValueClass<String>("abc")));
213         assertEquals("null", MAPPER.writeValueAsString(new FieldValueClass<String>(null)));
214     }
215 
testJsonValueWithUseSerializer()216     public void testJsonValueWithUseSerializer() throws Exception
217     {
218         String result = serializeAsString(MAPPER, new ToStringValueClass<Integer>(Integer.valueOf(123)));
219         assertEquals("\"123\"", result);
220     }
221 
222     /**
223      * Test for verifying that additional getters won't confuse serializer.
224      */
testMixedJsonValue()225     public void testMixedJsonValue() throws Exception
226     {
227         String result = serializeAsString(MAPPER, new ToStringValueClass2("xyz"));
228         assertEquals("\"xyz\"", result);
229     }
230 
testDisabling()231     public void testDisabling() throws Exception
232     {
233         assertEquals(aposToQuotes("{'x':1,'y':2}"),
234                 MAPPER.writeValueAsString(new DisabledJsonValue()));
235     }
236 
testValueWithStaticType()237     public void testValueWithStaticType() throws Exception
238     {
239         // Ok; first, with dynamic type:
240         assertEquals("{\"a\":\"a\",\"b\":\"b\"}", MAPPER.writeValueAsString(new ValueWrapper()));
241 
242         // then static
243         ObjectMapper staticMapper = jsonMapperBuilder()
244                 .configure(MapperFeature.USE_STATIC_TYPING, true)
245                 .build();
246         assertEquals("{\"a\":\"a\"}", staticMapper.writeValueAsString(new ValueWrapper()));
247     }
248 
testMapWithJsonValue()249     public void testMapWithJsonValue() throws Exception {
250         // First via method
251         assertEquals("{\"a\":\"1\"}", MAPPER.writeValueAsString(new MapBean()));
252 
253         // then field
254         assertEquals("{\"b\":\"2\"}", MAPPER.writeValueAsString(new MapFieldBean()));
255     }
256 
testWithMap()257     public void testWithMap() throws Exception {
258         assertEquals("42", MAPPER.writeValueAsString(new MapAsNumber()));
259     }
260 
testWithList()261     public void testWithList() throws Exception {
262         assertEquals("13", MAPPER.writeValueAsString(new ListAsNumber()));
263     }
264 
testInList()265     public void testInList() throws Exception {
266         IntExtBean bean = new IntExtBean();
267         bean.add(1);
268         bean.add(2);
269         String json = MAPPER.writeValueAsString(bean);
270         assertEquals(json, "{\"values\":[{\"i\":1},{\"i\":2}]}");
271     }
272 
273     // [databind#167]
testPolymorphicSerdeWithDelegate()274     public void testPolymorphicSerdeWithDelegate() throws Exception
275     {
276 	    AdditionInterface adder = new AdditionInterfaceImpl(1);
277 
278 	    assertEquals(2, adder.add(1));
279 	    String json = MAPPER.writeValueAsString(adder);
280 	    assertEquals("{\"boingo\":\"boopsy\",\"toAdd\":1}", json);
281 	    assertEquals(2, MAPPER.readValue(json, AdditionInterface.class).add(1));
282     }
283 
testJsonValueWithCustomOverride()284     public void testJsonValueWithCustomOverride() throws Exception
285     {
286         final Bean838 INPUT = new Bean838();
287 
288         // by default, @JsonValue should be used
289         assertEquals(quote("value"), MAPPER.writeValueAsString(INPUT));
290 
291         // but custom serializer should override it
292         ObjectMapper mapper = new ObjectMapper();
293         mapper.registerModule(new SimpleModule()
294             .addSerializer(Bean838.class, new Bean838Serializer())
295             );
296         assertEquals("42", mapper.writeValueAsString(INPUT));
297     }
298 }
299