1 package com.fasterxml.jackson.databind; 2 3 import java.io.*; 4 import java.util.*; 5 6 import com.fasterxml.jackson.annotation.JsonAnyGetter; 7 import com.fasterxml.jackson.annotation.JsonAnySetter; 8 import com.fasterxml.jackson.databind.type.TypeFactory; 9 import com.fasterxml.jackson.databind.util.LRUMap; 10 11 /** 12 * Tests to verify that most core Jackson components can be serialized 13 * using default JDK serialization: this feature is useful for some 14 * platforms, such as Android, where memory management is handled 15 * much more aggressively. 16 */ 17 public class TestJDKSerialization extends BaseMapTest 18 { 19 static class MyPojo { 20 public int x; 21 protected int y; 22 MyPojo()23 public MyPojo() { } MyPojo(int x0, int y0)24 public MyPojo(int x0, int y0) { 25 x = x0; 26 y = y0; 27 } 28 getY()29 public int getY() { return y; } setY(int y)30 public void setY(int y) { this.y = y; } 31 } 32 33 // for [databind#899] 34 static class EnumPOJO { 35 public ABC abc = ABC.B; 36 37 public Map<String,ABC> stuff = new LinkedHashMap<String,ABC>(); 38 } 39 40 static class AnyBean { 41 HashMap<String,Object> _map; 42 AnyBean()43 public AnyBean() { 44 _map = new HashMap<String,Object>(); 45 } 46 47 @JsonAnySetter addEntry(String key, Object value)48 AnyBean addEntry(String key, Object value) { 49 _map.put(key, value); 50 return this; 51 } 52 53 @JsonAnyGetter properties()54 public Map<String,Object> properties() { 55 return _map; 56 } 57 } 58 59 /* 60 /********************************************************** 61 /* Tests for individual objects 62 /********************************************************** 63 */ 64 65 /* 18-Oct-2013, tatu: Not sure why, but looks like sharing the default 66 * ObjectMapper here can lead to strange unit test suite failures, so 67 * let's create a private copy for this class only. 68 */ 69 private final ObjectMapper MAPPER = newJsonMapper(); 70 testConfigs()71 public void testConfigs() throws IOException 72 { 73 byte[] base = jdkSerialize(MAPPER.getDeserializationConfig().getBaseSettings()); 74 assertNotNull(jdkDeserialize(base)); 75 76 // first things first: underlying BaseSettings 77 78 DeserializationConfig origDC = MAPPER.getDeserializationConfig(); 79 SerializationConfig origSC = MAPPER.getSerializationConfig(); 80 byte[] dcBytes = jdkSerialize(origDC); 81 byte[] scBytes = jdkSerialize(origSC); 82 83 DeserializationConfig dc = jdkDeserialize(dcBytes); 84 assertNotNull(dc); 85 assertEquals(dc._deserFeatures, origDC._deserFeatures); 86 SerializationConfig sc = jdkDeserialize(scBytes); 87 assertNotNull(sc); 88 assertEquals(sc._serFeatures, origSC._serFeatures); 89 } 90 91 // for [databind#899] testEnumHandlers()92 public void testEnumHandlers() throws IOException 93 { 94 ObjectMapper mapper = newJsonMapper(); 95 // ensure we have serializers and/or deserializers, first 96 String json = mapper.writerFor(EnumPOJO.class) 97 .writeValueAsString(new EnumPOJO()); 98 EnumPOJO result = mapper.readerFor(EnumPOJO.class) 99 .readValue(json); 100 assertNotNull(result); 101 102 // and then use JDK serialization to freeze/thaw objects 103 byte[] bytes = jdkSerialize(mapper); 104 ObjectMapper mapper2 = jdkDeserialize(bytes); 105 assertNotNull(mapper2); 106 107 bytes = jdkSerialize(mapper.readerFor(EnumPOJO.class)); 108 ObjectReader r = jdkDeserialize(bytes); 109 assertNotNull(r); 110 111 /* 14-Aug-2015, tatu: Looks like pre-loading JsonSerializer is problematic 112 * at this point; comment out for now. Try to fix later on. 113 */ 114 bytes = jdkSerialize(mapper.writerFor(EnumPOJO.class)); 115 ObjectWriter w = jdkDeserialize(bytes); 116 assertNotNull(w); 117 118 // plus, ensure objects are usable: 119 String json2 = w.writeValueAsString(new EnumPOJO()); 120 assertEquals(json, json2); 121 EnumPOJO result2 = r.readValue(json2); 122 assertNotNull(result2); 123 } 124 testObjectWriter()125 public void testObjectWriter() throws IOException 126 { 127 ObjectWriter origWriter = MAPPER.writer(); 128 final String EXP_JSON = "{\"x\":2,\"y\":3}"; 129 final MyPojo p = new MyPojo(2, 3); 130 assertEquals(EXP_JSON, origWriter.writeValueAsString(p)); 131 String json = origWriter.writeValueAsString(new AnyBean() 132 .addEntry("a", "b")); 133 assertNotNull(json); 134 byte[] bytes = jdkSerialize(origWriter); 135 ObjectWriter writer2 = jdkDeserialize(bytes); 136 assertEquals(EXP_JSON, writer2.writeValueAsString(p)); 137 } 138 testObjectReader()139 public void testObjectReader() throws IOException 140 { 141 ObjectReader origReader = MAPPER.readerFor(MyPojo.class); 142 String JSON = "{\"x\":1,\"y\":2}"; 143 MyPojo p1 = origReader.readValue(JSON); 144 assertEquals(2, p1.y); 145 ObjectReader anyReader = MAPPER.readerFor(AnyBean.class); 146 AnyBean any = anyReader.readValue(JSON); 147 assertEquals(Integer.valueOf(2), any.properties().get("y")); 148 149 byte[] readerBytes = jdkSerialize(origReader); 150 ObjectReader reader2 = jdkDeserialize(readerBytes); 151 MyPojo p2 = reader2.readValue(JSON); 152 assertEquals(2, p2.y); 153 154 ObjectReader anyReader2 = jdkDeserialize(jdkSerialize(anyReader)); 155 AnyBean any2 = anyReader2.readValue(JSON); 156 assertEquals(Integer.valueOf(2), any2.properties().get("y")); 157 } 158 testObjectMapper()159 public void testObjectMapper() throws IOException 160 { 161 final String EXP_JSON = "{\"x\":2,\"y\":3}"; 162 final MyPojo p = new MyPojo(2, 3); 163 assertEquals(EXP_JSON, MAPPER.writeValueAsString(p)); 164 assertNotNull(MAPPER.getFactory()); 165 assertNotNull(MAPPER.getFactory().getCodec()); 166 167 byte[] bytes = jdkSerialize(MAPPER); 168 ObjectMapper mapper2 = jdkDeserialize(bytes); 169 assertEquals(EXP_JSON, mapper2.writeValueAsString(p)); 170 MyPojo p2 = mapper2.readValue(EXP_JSON, MyPojo.class); 171 assertEquals(p.x, p2.x); 172 assertEquals(p.y, p2.y); 173 174 // [databind#2038]: verify that codec is not lost 175 assertNotNull(mapper2.getFactory()); 176 assertNotNull(mapper2.getFactory().getCodec()); 177 } 178 testTypeFactory()179 public void testTypeFactory() throws Exception 180 { 181 TypeFactory orig = TypeFactory.defaultInstance(); 182 JavaType t = orig.constructType(JavaType.class); 183 assertNotNull(t); 184 185 byte[] bytes = jdkSerialize(orig); 186 TypeFactory result = jdkDeserialize(bytes); 187 assertNotNull(result); 188 t = orig.constructType(JavaType.class); 189 assertEquals(JavaType.class, t.getRawClass()); 190 } 191 testLRUMap()192 public void testLRUMap() throws Exception 193 { 194 LRUMap<String,Integer> map = new LRUMap<String,Integer>(32, 32); 195 map.put("a", 1); 196 197 byte[] bytes = jdkSerialize(map); 198 LRUMap<String,Integer> result = jdkDeserialize(bytes); 199 // transient implementation, will be read as empty 200 assertEquals(0, result.size()); 201 202 // but should be possible to re-populate 203 result.put("a", 2); 204 assertEquals(1, result.size()); 205 } 206 207 /* 208 /********************************************************** 209 /* Helper methods 210 /********************************************************** 211 */ 212 jdkSerialize(Object o)213 protected byte[] jdkSerialize(Object o) throws IOException 214 { 215 ByteArrayOutputStream bytes = new ByteArrayOutputStream(2000); 216 ObjectOutputStream obOut = new ObjectOutputStream(bytes); 217 obOut.writeObject(o); 218 obOut.close(); 219 return bytes.toByteArray(); 220 } 221 222 @SuppressWarnings("unchecked") jdkDeserialize(byte[] raw)223 protected <T> T jdkDeserialize(byte[] raw) throws IOException 224 { 225 ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(raw)); 226 try { 227 return (T) objIn.readObject(); 228 } catch (ClassNotFoundException e) { 229 fail("Missing class: "+e.getMessage()); 230 return null; 231 } finally { 232 objIn.close(); 233 } 234 } 235 } 236