001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.marshall.marshaller; 018 019import java.io.*; 020import java.lang.reflect.*; 021import java.nio.charset.*; 022 023import org.apache.juneau.marshall.*; 024import org.apache.juneau.marshall.parser.*; 025import org.apache.juneau.marshall.serializer.*; 026import org.apache.juneau.marshall.stream.*; 027 028/** 029 * Top-level class for a pairing of a {@link Serializer} and {@link Parser} into a single class with convenience read/write methods. 030 * 031 * <p> 032 * The general idea is to combine a single serializer and parser inside a simplified API for reading and writing POJOs. 033 * 034 * <h5 class='figure'>Examples:</h5> 035 * <p class='bjava'> 036 * <jc>// Using instance.</jc> 037 * Marshaller <jv>json</jv> = <jk>new</jk> Json(); 038 * MyPojo <jv>myPojo</jv> = <jv>json</jv>.read(<jv>string</jv>, MyPojo.<jk>class</jk>); 039 * String <jv>string</jv> = <jv>json</jv>.write(<jv>myPojo</jv>); 040 * </p> 041 * <p class='bjava'> 042 * <jc>// Using DEFAULT instance.</jc> 043 * MyPojo <jv>myPojo</jv> = Json.<jsf>DEFAULT</jsf>.read(<jv>string</jv>, MyPojo.<jk>class</jk>); 044 * String <jv>string</jv> = Json.<jsf>DEFAULT</jsf>.write(<jv>myPojo</jv>); 045 * </p> 046 * 047 * <h5 class='section'>See Also:</h5><ul> 048 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/Marshallers">Marshallers</a> 049 * </ul> 050 */ 051@SuppressWarnings({ 052 "resource" // Cursor shortcut methods return Closeables owned by the caller; Eclipse JDT @Owning warning is by design. 053}) 054public abstract class Marshaller { 055 private final Serializer s; 056 private final Parser p; 057 058 /** 059 * Constructor. 060 * 061 * @param s 062 * The serializer to use for serializing output. 063 * <br>Must not be <jk>null</jk>. 064 * @param p 065 * The parser to use for parsing input. 066 * <br>Must not be <jk>null</jk>. 067 */ 068 protected Marshaller(Serializer s, Parser p) { 069 this.s = s; 070 this.p = p; 071 } 072 073 /** 074 * Returns the parser associated with this marshaller. 075 * 076 * @return The parser associated with this marshaller. 077 */ 078 public Parser getParser() { return p; } 079 080 /** 081 * Returns the serializer associated with this marshaller. 082 * 083 * @return The serializer associated with this marshaller. 084 */ 085 public Serializer getSerializer() { return s; } 086 087 /** 088 * Same as {@link #read(Object, Type, Type...)} except optimized for a non-parameterized class. 089 * 090 * <p> 091 * This is the preferred parse method for simple types since you don't need to cast the results. 092 * 093 * <h5 class='section'>Examples:</h5> 094 * <p class='bjava'> 095 * Marshaller <jv>marshaller</jv> = Json.<jsf>DEFAULT</jsf>; 096 * 097 * <jc>// Parse into a string.</jc> 098 * String <jv>string</jv> = <jv>marshaller</jv> .read(<jv>json</jv>, String.<jk>class</jk>); 099 * 100 * <jc>// Parse into a bean.</jc> 101 * MyBean <jv>bean</jv> = <jv>marshaller</jv> .read(<jv>json</jv>, MyBean.<jk>class</jk>); 102 * 103 * <jc>// Parse into a bean array.</jc> 104 * MyBean[] <jv>beanArray</jv> = <jv>marshaller</jv> .read(<jv>json</jv>, MyBean[].<jk>class</jk>); 105 * 106 * <jc>// Parse into a linked-list of objects.</jc> 107 * List <jv>list</jv> = <jv>marshaller</jv> .read(<jv>json</jv>, LinkedList.<jk>class</jk>); 108 * 109 * <jc>// Parse into a map of object keys/values.</jc> 110 * Map <jv>map</jv> = <jv>marshaller</jv> .read(<jv>json</jv>, TreeMap.<jk>class</jk>); 111 * </p> 112 * 113 * @param <T> The class type of the object being created. 114 * @param input 115 * The input. 116 * <br>Character-based parsers can handle the following input class types: 117 * <ul> 118 * <li><jk>null</jk> 119 * <li>{@link Reader} 120 * <li>{@link CharSequence} 121 * <li>{@link InputStream} containing UTF-8 encoded text (or charset defined by 122 * {@link org.apache.juneau.marshall.parser.ReaderParser.Builder#streamCharset(Charset) ReaderParser.Builder#streamCharset(Charset)} property value). 123 * <li><code><jk>byte</jk>[]</code> containing UTF-8 encoded text (or charset defined by 124 * {@link org.apache.juneau.marshall.parser.ReaderParser.Builder#streamCharset(Charset) ReaderParser.Builder#streamCharset(Charset)} property value). 125 * <li>{@link File} containing system encoded text. 126 * </ul> 127 * <br>Stream-based parsers can handle the following input class types: 128 * <ul> 129 * <li><jk>null</jk> 130 * <li>{@link InputStream} 131 * <li><code><jk>byte</jk>[]</code> 132 * <li>{@link File} 133 * <li>{@link CharSequence} containing encoded bytes according to the {@link org.apache.juneau.marshall.parser.InputStreamParser.Builder#binaryFormat(BinaryFormat) InputStreamParser.Builder#binaryFormat(BinaryFormat)} setting. 134 * </ul> 135 * @param type The object type to create. 136 * @return The parsed object. 137 * @throws ParseException Malformed input encountered. 138 * @throws IOException Thrown by underlying stream. 139 */ 140 public final <T> T read(Object input, Class<T> type) throws ParseException, IOException { 141 return p.read(input, type); 142 } 143 144 /** 145 * Parses input into the specified object type. 146 * 147 * <p> 148 * The type can be a simple type (e.g. beans, strings, numbers) or parameterized type (collections/maps). 149 * 150 * <h5 class='section'>Examples:</h5> 151 * <p class='bjava'> 152 * Marshaller <jv>marshaller</jv> = Json.<jsf>DEFAULT</jsf>; 153 * 154 * <jc>// Parse into a linked-list of strings.</jc> 155 * List <jv>list1</jv> = <jv>marshaller</jv> .read(<jv>json</jv>, LinkedList.<jk>class</jk>, String.<jk>class</jk>); 156 * 157 * <jc>// Parse into a linked-list of beans.</jc> 158 * List <jv>list2</jv> = <jv>marshaller</jv> .read(<jv>json</jv>, LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>); 159 * 160 * <jc>// Parse into a linked-list of linked-lists of strings.</jc> 161 * List <jv>list3</jv> = <jv>marshaller</jv> .read(<jv>json</jv>, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>); 162 * 163 * <jc>// Parse into a map of string keys/values.</jc> 164 * Map <jv>map1</jv> = <jv>marshaller</jv> .read(<jv>json</jv>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>); 165 * 166 * <jc>// Parse into a map containing string keys and values of lists containing beans.</jc> 167 * Map <jv>map2</jv> = <jv>marshaller</jv> .read(<jv>json</jv>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>); 168 * </p> 169 * 170 * <p> 171 * <c>Collection</c> classes are assumed to be followed by zero or one objects indicating the element type. 172 * 173 * <p> 174 * <c>Map</c> classes are assumed to be followed by zero or two meta objects indicating the key and value types. 175 * 176 * <p> 177 * The array can be arbitrarily long to indicate arbitrarily complex data structures. 178 * 179 * <h5 class='section'>Notes:</h5><ul> 180 * <li class='note'> 181 * Use the {@link #read(Object, Class)} method instead if you don't need a parameterized map/collection. 182 * </ul> 183 * 184 * @param <T> The class type of the object to create. 185 * @param input 186 * The input. 187 * <br>Character-based parsers can handle the following input class types: 188 * <ul> 189 * <li><jk>null</jk> 190 * <li>{@link Reader} 191 * <li>{@link CharSequence} 192 * <li>{@link InputStream} containing UTF-8 encoded text (or charset defined by 193 * {@link org.apache.juneau.marshall.parser.ReaderParser.Builder#streamCharset(Charset) ReaderParser.Builder#streamCharset(Charset)} property value). 194 * <li><code><jk>byte</jk>[]</code> containing UTF-8 encoded text (or charset defined by 195 * {@link org.apache.juneau.marshall.parser.ReaderParser.Builder#streamCharset(Charset) ReaderParser.Builder#streamCharset(Charset)} property value). 196 * <li>{@link File} containing system encoded text. 197 * </ul> 198 * <br>Stream-based parsers can handle the following input class types: 199 * <ul> 200 * <li><jk>null</jk> 201 * <li>{@link InputStream} 202 * <li><code><jk>byte</jk>[]</code> 203 * <li>{@link File} 204 * <li>{@link CharSequence} containing encoded bytes according to the {@link org.apache.juneau.marshall.parser.InputStreamParser.Builder#binaryFormat(BinaryFormat) InputStreamParser.Builder#binaryFormat(BinaryFormat)} setting. 205 * </ul> 206 * @param type 207 * The object type to create. 208 * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 209 * @param args 210 * The type arguments of the class if it's a collection or map. 211 * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 212 * <br>Ignored if the main type is not a map or collection. 213 * @return The parsed object. 214 * @throws ParseException Malformed input encountered. 215 * @throws IOException Thrown by underlying stream. 216 * @see MarshallingSession#getClassMeta(Type,Type...) for argument syntax for maps and collections. 217 */ 218 public final <T> T read(Object input, Type type, Type...args) throws ParseException, IOException { 219 return p.read(input, type, args); 220 } 221 222 /** 223 * Serializes a POJO to the specified output stream or writer. 224 * 225 * <p> 226 * Equivalent to calling <c>serializer.createSession().write(o, output);</c> 227 * 228 * @param object The object to serialize. 229 * @param output 230 * The output object. 231 * <br>Character-based serializers can handle the following output class types: 232 * <ul> 233 * <li>{@link Writer} 234 * <li>{@link OutputStream} - Output will be written as UTF-8 encoded stream. 235 * <li>{@link File} - Output will be written as system-default encoded stream. 236 * <li>{@link StringBuilder} - Output will be written to the specified string builder. 237 * </ul> 238 * <br>Stream-based serializers can handle the following output class types: 239 * <ul> 240 * <li>{@link OutputStream} 241 * <li>{@link File} 242 * </ul> 243 * @throws SerializeException If a problem occurred trying to convert the output. 244 * @throws IOException Thrown by underlying stream. 245 */ 246 public final void write(Object object, Object output) throws SerializeException, IOException { 247 s.write(object, output); 248 } 249 250 /** 251 * Opens a low-level {@link TokenReader} cursor over the specified input. 252 * 253 * <p> 254 * Shortcut for <c>((TokenReadable)getParser()).readTokens(<jv>input</jv>)</c>. See 255 * {@link TokenReader} for the cursor contract. 256 * 257 * <h5 class='section'>Notes:</h5><ul> 258 * <li class='note'>This capability depends on the underlying parser. Formats whose parser 259 * does not implement {@link TokenReadable} (the structural-token role) yield a 260 * {@link ClassCastException}. 261 * </ul> 262 * 263 * @param input The input. Accepts {@link Reader}, {@link CharSequence}, {@link InputStream}, 264 * <code><jk>byte</jk>[]</code>, or {@link File}. 265 * @return A new {@link TokenReader} cursor. 266 * @throws IOException If a problem occurred opening the underlying input. 267 */ 268 public TokenReader readTokens(Object input) throws IOException { 269 return ((TokenReadable) getParser()).readTokens(input); 270 } 271 272 /** 273 * Opens a low-level push generator that emits structural events one at a time. 274 * 275 * <p> 276 * Shortcut for <c>((TokenWritable)getSerializer()).writeTokens(<jv>output</jv>)</c>. See 277 * {@link TokenWriter} for the generator contract. 278 * 279 * <h5 class='section'>Notes:</h5><ul> 280 * <li class='note'>This capability depends on the underlying serializer. Formats whose 281 * serializer does not implement {@link TokenWritable} (the structural-token role) yield a 282 * {@link ClassCastException}. 283 * </ul> 284 * 285 * @param output The output. Accepts {@link Writer}, {@link OutputStream}, {@link File}, or 286 * {@link StringBuilder}. 287 * @return A new {@link TokenWriter}. 288 * @throws IOException If the output type is not supported or could not be opened. 289 */ 290 public TokenWriter writeTokens(Object output) throws IOException { 291 return ((TokenWritable) getSerializer()).writeTokens(output); 292 } 293 294 /** 295 * Opens a low-level {@link RecordReader} cursor that yields each top-level value as a record. 296 * 297 * <p> 298 * Shortcut for <c>((RecordReadable)getParser()).readRecords(<jv>input</jv>)</c>. See 299 * {@link RecordReader} for the cursor contract. 300 * 301 * <h5 class='section'>Notes:</h5><ul> 302 * <li class='note'>This capability depends on the underlying parser. Formats whose parser 303 * does not implement {@link RecordReadable} yield a {@link ClassCastException}. 304 * </ul> 305 * 306 * @param input The input. Accepts {@link Reader}, {@link CharSequence}, {@link InputStream}, 307 * <code><jk>byte</jk>[]</code>, or {@link File}. 308 * @return A new {@link RecordReader} cursor. 309 * @throws IOException If a problem occurred opening the underlying input. 310 */ 311 public RecordReader readRecords(Object input) throws IOException { 312 return ((RecordReadable) getParser()).readRecords(input); 313 } 314 315 /** 316 * Opens a low-level {@link RecordWriter} cursor that emits each whole value as a record. 317 * 318 * <p> 319 * Shortcut for <c>((RecordWritable)getSerializer()).writeRecords(<jv>output</jv>)</c>. See 320 * {@link RecordWriter} for the cursor contract. 321 * 322 * <h5 class='section'>Notes:</h5><ul> 323 * <li class='note'>This capability depends on the underlying serializer. Formats whose 324 * serializer does not implement {@link RecordWritable} yield a {@link ClassCastException}. 325 * </ul> 326 * 327 * @param output The output. Accepts {@link Writer}, {@link OutputStream}, {@link File}, or 328 * {@link StringBuilder}. 329 * @return A new {@link RecordWriter} cursor. 330 * @throws IOException If a problem occurred opening the underlying output. 331 */ 332 public RecordWriter writeRecords(Object output) throws IOException { 333 return ((RecordWritable) getSerializer()).writeRecords(output); 334 } 335 336 /** 337 * Opens a {@link RecordReader} that yields each element of a top-level wire array as a 338 * separate record. 339 * 340 * <p> 341 * Shortcut for <c>((ArrayRecordReadable)getParser()).readArrayRecords(<jv>input</jv>)</c>. 342 * 343 * <h5 class='section'>Notes:</h5><ul> 344 * <li class='note'>This capability depends on the underlying parser. Formats whose parser 345 * does not implement {@link ArrayRecordReadable} yield a {@link ClassCastException}. 346 * </ul> 347 * 348 * @param input The input. Accepts {@link Reader}, {@link CharSequence}, {@link InputStream}, 349 * <code><jk>byte</jk>[]</code>, or {@link File}. 350 * @return A new element-streamed {@link RecordReader} cursor. 351 * @throws IOException If a problem occurred opening the underlying input. 352 */ 353 public RecordReader readArrayRecords(Object input) throws IOException { 354 return ((ArrayRecordReadable) getParser()).readArrayRecords(input); 355 } 356 357 /** 358 * Opens a {@link RecordWriter} that wraps each written value as one element of a top-level 359 * wire array. 360 * 361 * <p> 362 * Shortcut for <c>((ArrayRecordWritable)getSerializer()).writeArrayRecords(<jv>output</jv>)</c>. 363 * 364 * <h5 class='section'>Notes:</h5><ul> 365 * <li class='note'>This capability depends on the underlying serializer. Formats whose 366 * serializer does not implement {@link ArrayRecordWritable} yield a 367 * {@link ClassCastException}. 368 * </ul> 369 * 370 * @param output The output. Accepts {@link Writer}, {@link OutputStream}, {@link File}, or 371 * {@link StringBuilder}. 372 * @return A new element-streamed {@link RecordWriter} cursor. 373 * @throws IOException If a problem occurred opening the underlying output. 374 */ 375 public RecordWriter writeArrayRecords(Object output) throws IOException { 376 return ((ArrayRecordWritable) getSerializer()).writeArrayRecords(output); 377 } 378}