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.*; 8 import com.fasterxml.jackson.databind.*; 9 import com.fasterxml.jackson.databind.introspect.AnnotatedField; 10 import com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder; 11 import com.fasterxml.jackson.databind.module.SimpleModule; 12 import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; 13 import com.fasterxml.jackson.databind.ser.BeanSerializer; 14 import com.fasterxml.jackson.databind.ser.BeanSerializerBuilder; 15 import com.fasterxml.jackson.databind.ser.BeanSerializerModifier; 16 import com.fasterxml.jackson.databind.ser.std.StdSerializer; 17 import com.fasterxml.jackson.databind.type.ArrayType; 18 import com.fasterxml.jackson.databind.type.CollectionType; 19 import com.fasterxml.jackson.databind.type.MapType; 20 21 /** 22 * Unit tests for verifying that it is possible to configure 23 * construction of {@link BeanSerializer} instances. 24 */ 25 @SuppressWarnings("serial") 26 public class BeanSerializerModifierTest extends BaseMapTest 27 { 28 static class SerializerModifierModule extends SimpleModule 29 { 30 protected BeanSerializerModifier modifier; 31 SerializerModifierModule(BeanSerializerModifier modifier)32 public SerializerModifierModule(BeanSerializerModifier modifier) 33 { 34 super("test", Version.unknownVersion()); 35 this.modifier = modifier; 36 } 37 38 @Override setupModule(SetupContext context)39 public void setupModule(SetupContext context) 40 { 41 super.setupModule(context); 42 if (modifier != null) { 43 context.addBeanSerializerModifier(modifier); 44 } 45 } 46 } 47 48 @JsonPropertyOrder({"b", "a"}) 49 static class Bean { 50 public String b = "b"; 51 public String a = "a"; 52 } 53 54 static class RemovingModifier extends BeanSerializerModifier 55 { 56 private final String _removedProperty; 57 RemovingModifier(String remove)58 public RemovingModifier(String remove) { _removedProperty = remove; } 59 60 @Override changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties)61 public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, 62 List<BeanPropertyWriter> beanProperties) 63 { 64 Iterator<BeanPropertyWriter> it = beanProperties.iterator(); 65 while (it.hasNext()) { 66 BeanPropertyWriter bpw = it.next(); 67 if (bpw.getName().equals(_removedProperty)) { 68 it.remove(); 69 } 70 } 71 return beanProperties; 72 } 73 } 74 75 static class ReorderingModifier extends BeanSerializerModifier 76 { 77 @Override orderProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties)78 public List<BeanPropertyWriter> orderProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) 79 { 80 TreeMap<String,BeanPropertyWriter> props = new TreeMap<String,BeanPropertyWriter>(); 81 for (BeanPropertyWriter bpw : beanProperties) { 82 props.put(bpw.getName(), bpw); 83 } 84 return new ArrayList<BeanPropertyWriter>(props.values()); 85 } 86 } 87 88 static class ReplacingModifier extends BeanSerializerModifier 89 { 90 private final JsonSerializer<?> _serializer; 91 ReplacingModifier(JsonSerializer<?> s)92 public ReplacingModifier(JsonSerializer<?> s) { _serializer = s; } 93 94 @Override modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer)95 public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, 96 JsonSerializer<?> serializer) { 97 return _serializer; 98 } 99 } 100 101 static class BuilderModifier extends BeanSerializerModifier 102 { 103 private final JsonSerializer<?> _serializer; 104 BuilderModifier(JsonSerializer<?> ser)105 public BuilderModifier(JsonSerializer<?> ser) { 106 _serializer = ser; 107 } 108 109 @Override updateBuilder(SerializationConfig config, BeanDescription beanDesc, BeanSerializerBuilder builder)110 public BeanSerializerBuilder updateBuilder(SerializationConfig config, 111 BeanDescription beanDesc, BeanSerializerBuilder builder) { 112 return new BogusSerializerBuilder(builder, _serializer); 113 } 114 } 115 116 static class BogusSerializerBuilder extends BeanSerializerBuilder 117 { 118 private final JsonSerializer<?> _serializer; 119 BogusSerializerBuilder(BeanSerializerBuilder src, JsonSerializer<?> ser)120 public BogusSerializerBuilder(BeanSerializerBuilder src, 121 JsonSerializer<?> ser) { 122 super(src); 123 _serializer = ser; 124 } 125 126 @Override build()127 public JsonSerializer<?> build() { 128 return _serializer; 129 } 130 } 131 132 static class BogusBeanSerializer extends JsonSerializer<Object> 133 { 134 private final int _value; 135 BogusBeanSerializer(int v)136 public BogusBeanSerializer(int v) { _value = v; } 137 138 @Override serialize(Object value, JsonGenerator jgen, SerializerProvider provider)139 public void serialize(Object value, JsonGenerator jgen, 140 SerializerProvider provider) throws IOException { 141 jgen.writeNumber(_value); 142 } 143 } 144 145 static class EmptyBean { 146 @JsonIgnore 147 public String name = "foo"; 148 } 149 150 static class EmptyBeanModifier extends BeanSerializerModifier 151 { 152 @Override changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties)153 public List<BeanPropertyWriter> changeProperties(SerializationConfig config, 154 BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) 155 { 156 JavaType strType = config.constructType(String.class); 157 // we need a valid BeanPropertyDefinition; this will do (just need name to match) 158 POJOPropertyBuilder prop = new POJOPropertyBuilder(config, null, true, new PropertyName("bogus")); 159 try { 160 AnnotatedField f = new AnnotatedField(null, EmptyBean.class.getDeclaredField("name"), null); 161 beanProperties.add(new BeanPropertyWriter(prop, f, null, 162 strType, 163 null, null, strType, 164 false, null, 165 null)); 166 } catch (NoSuchFieldException e) { 167 throw new IllegalStateException(e.getMessage()); 168 } 169 return beanProperties; 170 } 171 } 172 173 // [Issue#539]: use post-modifier 174 static class EmptyBeanModifier539 extends BeanSerializerModifier 175 { 176 @Override changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties)177 public List<BeanPropertyWriter> changeProperties(SerializationConfig config, 178 BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) 179 { 180 return beanProperties; 181 } 182 183 @Override modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer)184 public JsonSerializer<?> modifySerializer(SerializationConfig config, 185 BeanDescription beanDesc, JsonSerializer<?> serializer) { 186 return new BogusBeanSerializer(42); 187 } 188 } 189 // [databind#120], arrays, collections, maps 190 191 static class ArraySerializerModifier extends BeanSerializerModifier { 192 @Override modifyArraySerializer(SerializationConfig config, ArrayType valueType, BeanDescription beanDesc, JsonSerializer<?> serializer)193 public JsonSerializer<?> modifyArraySerializer(SerializationConfig config, 194 ArrayType valueType, BeanDescription beanDesc, JsonSerializer<?> serializer) { 195 return new StdSerializer<Object>(Object.class) { 196 @Override public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException { 197 jgen.writeNumber(123); 198 } 199 }; 200 } 201 } 202 203 static class CollectionSerializerModifier extends BeanSerializerModifier { 204 @Override 205 public JsonSerializer<?> modifyCollectionSerializer(SerializationConfig config, 206 CollectionType valueType, BeanDescription beanDesc, JsonSerializer<?> serializer) { 207 return new StdSerializer<Object>(Object.class) { 208 @Override public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException { 209 jgen.writeNumber(123); 210 } 211 }; 212 } 213 } 214 215 static class MapSerializerModifier extends BeanSerializerModifier { 216 @Override 217 public JsonSerializer<?> modifyMapSerializer(SerializationConfig config, 218 MapType valueType, BeanDescription beanDesc, JsonSerializer<?> serializer) { 219 return new StdSerializer<Object>(Object.class) { 220 @Override public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException { 221 jgen.writeNumber(123); 222 } 223 }; 224 } 225 } 226 227 static class EnumSerializerModifier extends BeanSerializerModifier { 228 @Override 229 public JsonSerializer<?> modifyEnumSerializer(SerializationConfig config, 230 JavaType valueType, BeanDescription beanDesc, JsonSerializer<?> serializer) { 231 return new StdSerializer<Object>(Object.class) { 232 @Override public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException { 233 jgen.writeNumber(123); 234 } 235 }; 236 } 237 } 238 239 static class KeySerializerModifier extends BeanSerializerModifier { 240 @Override 241 public JsonSerializer<?> modifyKeySerializer(SerializationConfig config, 242 JavaType valueType, BeanDescription beanDesc, JsonSerializer<?> serializer) { 243 return new StdSerializer<Object>(Object.class) { 244 @Override public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException { 245 jgen.writeFieldName("foo"); 246 } 247 }; 248 } 249 } 250 251 /* 252 /******************************************************** 253 /* Unit tests: success 254 /******************************************************** 255 */ 256 257 public void testPropertyRemoval() throws Exception 258 { 259 ObjectMapper mapper = new ObjectMapper(); 260 mapper.registerModule(new SerializerModifierModule(new RemovingModifier("a"))); 261 Bean bean = new Bean(); 262 assertEquals("{\"b\":\"b\"}", mapper.writeValueAsString(bean)); 263 } 264 265 public void testPropertyReorder() throws Exception 266 { 267 ObjectMapper mapper = new ObjectMapper(); 268 mapper.registerModule(new SerializerModifierModule(new ReorderingModifier())); 269 Bean bean = new Bean(); 270 assertEquals("{\"a\":\"a\",\"b\":\"b\"}", mapper.writeValueAsString(bean)); 271 } 272 273 public void testBuilderReplacement() throws Exception 274 { 275 ObjectMapper mapper = new ObjectMapper(); 276 mapper.registerModule(new SerializerModifierModule(new BuilderModifier(new BogusBeanSerializer(17)))); 277 Bean bean = new Bean(); 278 assertEquals("17", mapper.writeValueAsString(bean)); 279 } 280 public void testSerializerReplacement() throws Exception 281 { 282 ObjectMapper mapper = new ObjectMapper(); 283 mapper.registerModule(new SerializerModifierModule(new ReplacingModifier(new BogusBeanSerializer(123)))); 284 Bean bean = new Bean(); 285 assertEquals("123", mapper.writeValueAsString(bean)); 286 } 287 288 // for [JACKSON-670] 289 public void testEmptyBean() throws Exception 290 { 291 ObjectMapper mapper = new ObjectMapper(); 292 mapper.registerModule(new SimpleModule("test", Version.unknownVersion()) { 293 @Override 294 public void setupModule(SetupContext context) 295 { 296 super.setupModule(context); 297 context.addBeanSerializerModifier(new EmptyBeanModifier()); 298 } 299 }); 300 String json = mapper.writeValueAsString(new EmptyBean()); 301 assertEquals("{\"bogus\":\"foo\"}", json); 302 } 303 304 public void testEmptyBean539() throws Exception 305 { 306 ObjectMapper mapper = new ObjectMapper(); 307 mapper.registerModule(new SimpleModule("test", Version.unknownVersion()) { 308 @Override 309 public void setupModule(SetupContext context) 310 { 311 super.setupModule(context); 312 context.addBeanSerializerModifier(new EmptyBeanModifier539()); 313 } 314 }); 315 String json = mapper.writeValueAsString(new EmptyBean()); 316 assertEquals("42", json); 317 } 318 319 // [databind#121] 320 321 public void testModifyArraySerializer() throws Exception 322 { 323 ObjectMapper mapper = new ObjectMapper(); 324 mapper.registerModule(new SimpleModule("test") 325 .setSerializerModifier(new ArraySerializerModifier())); 326 assertEquals("123", mapper.writeValueAsString(new Integer[] { 1, 2 })); 327 } 328 329 public void testModifyCollectionSerializer() throws Exception 330 { 331 ObjectMapper mapper = new ObjectMapper(); 332 mapper.registerModule(new SimpleModule("test") 333 .setSerializerModifier(new CollectionSerializerModifier())); 334 assertEquals("123", mapper.writeValueAsString(new ArrayList<Integer>())); 335 } 336 337 public void testModifyMapSerializer() throws Exception 338 { 339 ObjectMapper mapper = new ObjectMapper(); 340 mapper.registerModule(new SimpleModule("test") 341 .setSerializerModifier(new MapSerializerModifier())); 342 assertEquals("123", mapper.writeValueAsString(new HashMap<String,String>())); 343 } 344 345 public void testModifyEnumSerializer() throws Exception 346 { 347 ObjectMapper mapper = new ObjectMapper(); 348 mapper.registerModule(new SimpleModule("test") 349 .setSerializerModifier(new EnumSerializerModifier())); 350 assertEquals("123", mapper.writeValueAsString(ABC.C)); 351 } 352 353 public void testModifyKeySerializer() throws Exception 354 { 355 ObjectMapper mapper = new ObjectMapper(); 356 mapper.registerModule(new SimpleModule("test") 357 .setSerializerModifier(new KeySerializerModifier())); 358 Map<String,Integer> map = new HashMap<String,Integer>(); 359 map.put("x", 3); 360 assertEquals("{\"foo\":3}", mapper.writeValueAsString(map)); 361 } 362 } 363