{title:'Swaps', updated:'9.0.0'}

{@link oaj.swap.ObjectSwap Swaps} are a critical component of Juneau. They allow the serializers and parsers to handle Java objects that wouldn't normally be serializable.

Swaps are, simply put, 'object swappers' that swap in serializable objects for non-serializable ones during serialization, and vis-versa during parsing.

Some examples of non-serializable objects are File, Reader, Iterable, etc... These are classes that aren't beans and cannot be represented as simple maps, collections, or primitives.

In the following example, we introduce a ObjectSwap that will swap in a bean of a particular type with a map containing customized key-value pairs:

| // Sample swap for converting a bean to a specialized map of key-value pairs. | public class MyBeanSwap extends ObjectSwap<MyBean,JsonMap> { | | // Converts a bean to a generic map. | @Override /* ObjectSwap */ | public JsonMap swap(BeanSession session, MyBean bean) { | return JsonMap.of("foo", bean.getBar()); | } | | // Converts the generic map back into a bean. | @Override /* ObjectSwap */ | public MyBean unswap(BeanSession session, JsonMap map, ClassMeta<?> hint) throws Exception { | MyBean bean = new MyBean(); | bean.setBar(map.getString("foo")); | return bean; | } | }

The swap can then be associated with serializers and parsers like so:

| // Create a new JSON serializer with our swap. | WriterSerializer serializer = JsonSerializer.create().simple().swaps(MyBeanSwap.class).build(); | String json = serializer.serialize(new MyBean()); | | // Create a JSON parser with our swap. | ReaderParser parser = JsonParser.create().swaps(MyBeanSwap.class).build(); | MyBean bean = parser.parse(json, MyBean.class);

Another example of a ObjectSwap is one that converts byte[] arrays to BASE64-encoded strings:

| public class ByteArrayBase64Swap extends StringSwap<byte[]> { | | @Override /* StringSwap */ | public String swap(byte[] bytes) throws Exception { | ByteArrayOutputStream baos = new ByteArrayOutputStream(); | OutputStream b64os = MimeUtility.encode(baos, "base64"); | b64os.write(bytes); | b64os.close(); | return new String(baos.toByteArray()); | } | | @Override /* StringSwap */ | public byte[] unswap(String string, ClassMeta<?> hint) throws Exception { | byte[] bytes = string.getBytes(); | ByteArrayInputStream bais = new ByteArrayInputStream(bytes); | InputStream b64is = MimeUtility.decode(bais, "base64"); | byte[] tmp = new byte[bytes.length]; | int n = b64is.read(tmp); | byte[] res = new byte[n]; | System.arraycopy(tmp, 0, res, 0, n); | return res; | } | }

The following example shows the BASE64 swap in use:

| // Create a JSON serializer and register the BASE64 encoding swap with it. | WriterSerializer serializer = JsonSerializer.create().simple().swaps(ByteArrayBase64Swap.class).build(); | ReaderParser parser = JsonParser.create().swaps(ByteArrayBase64Swap.class).build(); | | byte[] bytes = {1,2,3}; | String json = serializer.serialize(bytes); // Produces "'AQID'" | bytes = parser.parse(json, byte[].class); // Reproduces {1,2,3} | | byte[][] bytes2d = {{1,2,3},{4,5,6},null}; | json = serializer.serialize(bytes2d); // Produces "['AQID','BAUG',null]" | bytes2d = parser.parse(json, byte[][].class); // Reproduces {{1,2,3},{4,5,6},null}

The {@link oaj.BeanContextable.Builder#swap(Class,Class,ThrowingFunction)} and {@link oaj.BeanContextable.Builder#swap(Class,Class,ThrowingFunction,ThrowingFunction)} methods are another way to define swaps by using functions.

| // Use a function to convert beans to strings. | WriterSerializer serializer = JsonSerializer | .create() | .simple() | .swap(MyBean.class, String.class, x -> myBeanStringifier(x)) | .build();