| <!-- |
| /*************************************************************************************************************************** |
| * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations under the License. |
| ***************************************************************************************************************************/ |
| --> |
| |
| PojoSwaps |
| |
| <p> |
| {@link oaj.transform.PojoSwap PojoSwaps} are a critical component of Juneau. |
| They allow the serializers and parsers to handle Java objects that wouldn't normally be serializable. |
| </p> |
| <p> |
| Swaps are, simply put, 'object swappers' that swap in serializable objects for |
| non-serializable ones during serialization, and vis-versa during parsing. |
| </p> |
| <p> |
| Some examples of non-serializable POJOs are <c>File</c>, <c>Reader</c>, |
| <c>Iterable</c>, etc... |
| These are classes that aren't beans and cannot be represented as simple maps, collections, or primitives. |
| </p> |
| <p> |
| In the following example, we introduce a <c>PojoSwap</c> that will swap in ISO8601 strings for |
| <c>Date</c> objects: |
| </p> |
| <p class='bpcode w800'> |
| <jc>// Sample swap for converting Dates to ISO8601 strings.</jc> |
| <jk>public class</jk> MyDateSwap <jk>extends</jk> PojoSwap<Date,String> { |
| |
| <jc>// ISO8601 formatter.</jc> |
| <jk>private</jk> DateFormat <jf>format</jf> = <jk>new</jk> SimpleDateFormat(<js>"yyyy-MM-dd'T'HH:mm:ssZ"</js>); |
| |
| <jc>// Converts a Date object to an ISO8601 string.</jc> |
| <ja>@Override</ja> <jc>/* PojoSwap */</jc> |
| <jk>public</jk> String swap(BeanSession session, Date o) { |
| <jk>return</jk> <jf>format</jf>.format(o); |
| } |
| |
| <jc>// Converts an ISO8601 string to a Date object.</jc> |
| <ja>@Override</ja> <jc>/* PojoSwap */</jc> |
| <jk>public</jk> Date unswap(BeanSession session, String o, ClassMeta hint) <jk>throws</jk> Exception { |
| <jk>return</jk> <jf>format</jf>.parse(o); |
| } |
| } |
| </p> |
| <p> |
| The swap can then be associated with serializers and parsers like so: |
| </p> |
| <p class='bpcode w800'> |
| <jc>// Sample bean with a Date field.</jc> |
| <jk>public class</jk> MyBean { |
| <jk>public</jk> Date <jf>date</jf> = <jk>new</jk> Date(112, 2, 3, 4, 5, 6); |
| } |
| |
| <jc>// Create a new JSON serializer, associate our date swap with it, and serialize a sample bean.</jc> |
| WriterSerializer s = JsonSerializer.<jsm>create</jsm>().simple().pojoSwaps(MyDateSwap.<jk>class</jk>).build(); |
| String json = s.serialize(<jk>new</jk> MyBean()); <jc>// == "{date:'2012-03-03T04:05:06-0500'}"</jc> |
| |
| <jc>// Create a JSON parser, associate our date swap with it, and reconstruct our bean (including the date).</jc> |
| ReaderParser p = JsonParser.<jsm>create</jsm>().pojoSwaps(MyDateSwap.<jk>class</jk>).build(); |
| MyBean bean = p.parse(json, MyBean.<jk>class</jk>); |
| <jk>int</jk> day = bean.<jf>date</jf>.getDay(); <jc>// == 3</jc> |
| </p> |
| <p> |
| The {@link oaj.BeanMap#get(Object)} and {@link oaj.BeanMap#put(String,Object)} |
| methods will automatically convert to swapped values as the following example shows: |
| </p> |
| <p class='bpcode w800'> |
| <jc>// Create a new bean context and add our swap.</jc> |
| BeanContext bc = BeanContext.<jsm>create</jsm>().pojoSwaps(MyDateSwap.<jk>class</jk>).build(); |
| |
| <jc>// Create a new bean.</jc> |
| MyBean myBean = <jk>new</jk> MyBean(); |
| |
| <jc>// Wrap it in a bean map.</jc> |
| BeanMap<Bean> beanMap = bc.forBean(myBean); |
| |
| <jc>// Use the get() method to get the date field as an ISO8601 string.</jc> |
| String date = (String)beanMap.get(<js>"date"</js>); <jc>// == "2012-03-03T04:05:06-0500"</jc> |
| |
| <jc>// Use the put() method to set the date field to an ISO8601 string.</jc> |
| beanMap.put(<js>"date"</js>, <js>"2013-01-01T12:30:00-0500"</js>); <jc>// Set it to a new value.</jc> |
| |
| <jc>// Verify that the date changed on the original bean.</jc> |
| <jk>int</jk> year = myBean.<jf>date</jf>.getYear(); <jc>// == 113</jc> |
| </p> |
| <p> |
| Another example of a <c>PojoSwap</c> is one that converts <c><jk>byte</jk>[]</c> arrays to |
| BASE64-encoded strings: |
| </p> |
| <p class='bpcode w800'> |
| <jk>public class</jk> ByteArrayBase64Swap <jk>extends</jk> StringSwap<<jk>byte</jk>[]> { |
| |
| <ja>@Override</ja> <jc>/* StringSwap */</jc> |
| <jk>public</jk> String swap(<jk>byte</jk>[] b) <jk>throws</jk> Exception { |
| ByteArrayOutputStream baos = <jk>new</jk> ByteArrayOutputStream(); |
| OutputStream b64os = MimeUtility.encode(baos, <js>"base64"</js>); |
| b64os.write(b); |
| b64os.close(); |
| <jk>return new</jk> String(baos.toByteArray()); |
| } |
| |
| <ja>@Override</ja> <jc>/* StringSwap */</jc> |
| <jk>public byte</jk>[] unswap(String s, ClassMeta<?> hint) <jk>throws</jk> Exception { |
| <jk>byte</jk>[] b = s.getBytes(); |
| ByteArrayInputStream bais = <jk>new</jk> ByteArrayInputStream(b); |
| InputStream b64is = MimeUtility.decode(bais, <js>"base64"</js>); |
| <jk>byte</jk>[] tmp = <jk>new byte</jk>[b.length]; |
| <jk>int</jk> n = b64is.read(tmp); |
| <jk>byte</jk>[] res = <jk>new byte</jk>[n]; |
| System.<jsm>arraycopy</jsm>(tmp, 0, res, 0, n); |
| <jk>return</jk> res; |
| } |
| } |
| </p> |
| <p> |
| The following example shows the BASE64 swap in use: |
| </p> |
| <p class='bpcode w800'> |
| <jc>// Create a JSON serializer and register the BASE64 encoding swap with it.</jc> |
| WriterSerializer s = JsonSerializer.<jsm>create</jsm>().simple().pojoSwaps(ByteArrayBase64Swap.<jk>class</jk>).build(); |
| ReaderParser p = JsonParser.<jsm>create</jsm>().pojoSwaps(ByteArrayBase64Swap.<jk>class</jk>).build(); |
| |
| <jk>byte</jk>[] bytes = {1,2,3}; |
| String json = s.serialize(bytes); <jc>// Produces "'AQID'"</jc> |
| bytes = p.parse(json, <jk>byte</jk>[].<jk>class</jk>); <jc>// Reproduces {1,2,3}</jc> |
| |
| <jk>byte</jk>[][] bytes2d = {{1,2,3},{4,5,6},<jk>null</jk>}; |
| json = s.serialize(bytes2d); <jc>// Produces "['AQID','BAUG',null]"</jc> |
| bytes2d = p.parse(json, <jk>byte</jk>[][].<jk>class</jk>); <jc>// Reproduces {{1,2,3},{4,5,6},null}</jc> |
| </p> |
| <p> |
| Several <c>PojoSwaps</c> are already provided for common Java objects: |
| </p> |
| <ul class='doctree'> |
| <li class='jp'><jk>org.apache.juneau.transforms</jk> |
| <ul> |
| <li class='jc'> |
| {@link oaj.transforms.ByteArrayBase64Swap} |
| <li class='jac'> |
| {@link oaj.transforms.CalendarSwap} |
| <li class='jac'> |
| {@link oaj.transforms.DateSwap} |
| <li class='jc'> |
| {@link oaj.transforms.EnumerationSwap} |
| <li class='jc'> |
| {@link oaj.transforms.IteratorSwap} |
| <li class='jc'> |
| {@link oaj.transforms.ReaderSwap} |
| <li class='jc'> |
| {@link oaj.transforms.XMLGregorianCalendarSwap} |
| </ul> |
| </li> |
| </ul> |
| <p> |
| In particular, the {@link oaj.transforms.CalendarSwap} and |
| {@link oaj.transforms.DateSwap} transforms provide a large number of customized swaps to |
| ISO, RFC, or localized strings. |
| </p> |
| <p> |
| Swaps have access to the session locale and timezone through the {@link oaj.BeanSession#getLocale()} and |
| {@link oaj.BeanSession#getTimeZone()} methods. |
| This allows you to specify localized swap values when needed. |
| </p> |
| <p> |
| If using the REST server API, the locale and timezone are set based on the <c>Accept-Language</c> and |
| <c>Time-Zone</c> headers on the request. |
| </p> |
| |
| <ul class='doctree'> |
| <li class='info'> |
| The 'swapped' class type must be a serializable type. |
| <br>See the definition for Category 4 objects in {@doc PojoCategories}. |
| </ul> |