001package org.apache.juneau.marshall; 002// *************************************************************************************************************************** 003// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 004// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 005// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 006// * with the License. You may obtain a copy of the License at * 007// * * 008// * http://www.apache.org/licenses/LICENSE-2.0 * 009// * * 010// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 011// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 012// * specific language governing permissions and limitations under the License. * 013// *************************************************************************************************************************** 014 015import java.io.*; 016import java.lang.reflect.*; 017import java.text.*; 018 019import org.apache.juneau.*; 020import org.apache.juneau.parser.*; 021import org.apache.juneau.parser.ParseException; 022import org.apache.juneau.serializer.*; 023 024/** 025 * Top-level class for a pairing of a {@link Serializer} and {@link Parser} into a single class with convenience read/write methods. 026 * 027 * <p> 028 * The general idea is to combine a single serializer and parser inside a simplified API for reading and writing POJOs. 029 * 030 * <h5 class='figure'>Examples:</h5> 031 * <p class='bcode w800'> 032 * <jc>// Using instance.</jc> 033 * Marshall json = <jk>new</jk> Json(); 034 * MyPojo myPojo = json.read(string, MyPojo.<jk>class</jk>); 035 * String string = json.write(myPojo); 036 * </p> 037 * <p class='bcode w800'> 038 * <jc>// Using DEFAULT instance.</jc> 039 * MyPojo myPojo = Json.<jsf>DEFAULT</jsf>.read(string, MyPojo.<jk>class</jk>); 040 * String string = Json.<jsf>DEFAULT</jsf>.write(myPojo); 041 * </p> 042 * 043 * <ul class='seealso'> 044 * <li class='link'>{@doc juneau-marshall.Marshalls} 045 * </ul> 046 */ 047public abstract class Marshall { 048 049 private final Serializer s; 050 private final Parser p; 051 052 /** 053 * Constructor. 054 * 055 * @param s 056 * The serializer to use for serializing output. 057 * <br>Must not be <jk>null</jk>. 058 * @param p 059 * The parser to use for parsing input. 060 * <br>Must not be <jk>null</jk>. 061 */ 062 protected Marshall(Serializer s, Parser p) { 063 this.s = s; 064 this.p = p; 065 } 066 067 /** 068 * Returns the serializer associated with this marshall. 069 * 070 * @return The serializer associated with this marshall. 071 */ 072 public Serializer getSerializer() { 073 return s; 074 } 075 076 /** 077 * Returns the parser associated with this marshall. 078 * 079 * @return The parser associated with this marshall. 080 */ 081 public Parser getParser() { 082 return p; 083 } 084 085 /** 086 * Serializes a POJO directly to either a <c>String</c> or <code><jk>byte</jk>[]</code> depending on the serializer type. 087 * 088 * @param o The object to serialize. 089 * @return 090 * The serialized object. 091 * <br>Character-based serializers will return a <c>String</c> 092 * <br>Stream-based serializers will return a <code><jk>byte</jk>[]</code> 093 * @throws SerializeException If a problem occurred trying to convert the output. 094 */ 095 public Object write(Object o) throws SerializeException { 096 return s.createSession().serialize(o); 097 } 098 099 /** 100 * Serializes a POJO to the specified output stream or writer. 101 * 102 * <p> 103 * Equivalent to calling <c>serializer.createSession().serialize(o, output);</c> 104 * 105 * @param o The object to serialize. 106 * @param output 107 * The output object. 108 * <br>Character-based serializers can handle the following output class types: 109 * <ul> 110 * <li>{@link Writer} 111 * <li>{@link OutputStream} - Output will be written as UTF-8 encoded stream. 112 * <li>{@link File} - Output will be written as system-default encoded stream. 113 * <li>{@link StringBuilder} - Output will be written to the specified string builder. 114 * </ul> 115 * <br>Stream-based serializers can handle the following output class types: 116 * <ul> 117 * <li>{@link OutputStream} 118 * <li>{@link File} 119 * </ul> 120 * @throws SerializeException If a problem occurred trying to convert the output. 121 * @throws IOException Thrown by underlying stream. 122 */ 123 public final void write(Object o, Object output) throws SerializeException, IOException { 124 s.createSession().serialize(o, output); 125 } 126 127 /** 128 * Convenience method for serializing an object to a String. 129 * 130 * <p> 131 * For writer-based serializers, this is identical to calling {@link Serializer#serialize(Object)}. 132 * <br>For stream-based serializers, this converts the returned byte array to a string based on 133 * the {@link OutputStreamSerializer#OSSERIALIZER_binaryFormat} setting. 134 * 135 * @param o The object to serialize. 136 * @return The output serialized to a string. 137 */ 138 public final String toString(Object o) { 139 try { 140 return s.serializeToString(o); 141 } catch (Exception e) { 142 throw new RuntimeException(e); 143 } 144 } 145 146 147 /** 148 * Convenience method for calling <c>System.out.println(...)</c> on the specified object after calling {@link #toString(Object)}. 149 * 150 * @param o The object to serialize and then send to the console. 151 * @return This object (for method chaining). 152 */ 153 public final Marshall println(Object o) { 154 System.out.println(toString(o)); 155 return this; 156 } 157 158 /** 159 * Prints a message with arguments to {@link System#out}. 160 * 161 * <p> 162 * Arguments are automatically converted to strings using this marshaller. 163 * 164 * <p> 165 * Useful for debug messages. 166 * 167 * <h5 class='figure'>Example:</h5> 168 * <p class='bcode'> 169 * SimpleJson.<jsf>DEFAULT</jsf>.out(<js>"myPojo={0}"</js>, myPojo); 170 * </p> 171 * 172 * @param msg The {@link MessageFormat}-styled message. 173 * @param args The arguments sent to the the formatter after running them through this marshaller. 174 * @return This object (for method chaining). 175 */ 176 public final Marshall out(String msg, Object...args) { 177 System.out.println(format(msg, args)); 178 return this; 179 } 180 181 /** 182 * Prints a message with arguments to {@link System#err}. 183 * 184 * <p> 185 * Arguments are automatically converted to strings using this marshaller. 186 * 187 * <p> 188 * Useful for debug messages. 189 * 190 * <h5 class='figure'>Example:</h5> 191 * <p class='bcode'> 192 * SimpleJson.<jsf>DEFAULT</jsf>.err(<js>"myPojo={0}"</js>, myPojo); 193 * </p> 194 * 195 * @param msg The {@link MessageFormat}-styled message. 196 * @param args The arguments sent to the the formatter after running them through this marshaller. 197 * @return This object (for method chaining). 198 */ 199 public final Marshall err(String msg, Object...args) { 200 System.err.println(format(msg, args)); 201 return this; 202 } 203 204 /** 205 * Formats a message with arguments. 206 * 207 * <p> 208 * Arguments are automatically converted to strings using this marshaller. 209 * 210 * <p> 211 * Useful for debug messages. 212 * 213 * <h5 class='figure'>Example:</h5> 214 * <p class='bcode'> 215 * String msg = SimpleJson.<jsf>DEFAULT</jsf>.format(<js>"myPojo={0}"</js>, myPojo); 216 * </p> 217 * 218 * @param msg The {@link MessageFormat}-styled message. 219 * @param args The arguments sent to the the formatter after running them through this marshaller. 220 * @return This object (for method chaining). 221 */ 222 public final String format(String msg, Object...args) { 223 for (int i = 0; i < args.length; i++) 224 args[i] = toString(args[i]); 225 return MessageFormat.format(msg, args); 226 } 227 228 /** 229 * Parses input into the specified object type. 230 * 231 * <p> 232 * The type can be a simple type (e.g. beans, strings, numbers) or parameterized type (collections/maps). 233 * 234 * <h5 class='section'>Examples:</h5> 235 * <p class='bcode w800'> 236 * Marshall m = Json.<jsf>DEFAULT</jsf>; 237 * 238 * <jc>// Parse into a linked-list of strings.</jc> 239 * List l = m.read(json, LinkedList.<jk>class</jk>, String.<jk>class</jk>); 240 * 241 * <jc>// Parse into a linked-list of beans.</jc> 242 * List l = m.read(json, LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>); 243 * 244 * <jc>// Parse into a linked-list of linked-lists of strings.</jc> 245 * List l = m.read(json, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>); 246 * 247 * <jc>// Parse into a map of string keys/values.</jc> 248 * Map m = m.read(json, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>); 249 * 250 * <jc>// Parse into a map containing string keys and values of lists containing beans.</jc> 251 * Map m = m.read(json, TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>); 252 * </p> 253 * 254 * <p> 255 * <c>Collection</c> classes are assumed to be followed by zero or one objects indicating the element type. 256 * 257 * <p> 258 * <c>Map</c> classes are assumed to be followed by zero or two meta objects indicating the key and value types. 259 * 260 * <p> 261 * The array can be arbitrarily long to indicate arbitrarily complex data structures. 262 * 263 * <ul class='notes'> 264 * <li> 265 * Use the {@link #read(Object, Class)} method instead if you don't need a parameterized map/collection. 266 * </ul> 267 * 268 * @param <T> The class type of the object to create. 269 * @param input 270 * The input. 271 * <br>Character-based parsers can handle the following input class types: 272 * <ul> 273 * <li><jk>null</jk> 274 * <li>{@link Reader} 275 * <li>{@link CharSequence} 276 * <li>{@link InputStream} containing UTF-8 encoded text (or charset defined by 277 * {@link ReaderParser#RPARSER_streamCharset} property value). 278 * <li><code><jk>byte</jk>[]</code> containing UTF-8 encoded text (or charset defined by 279 * {@link ReaderParser#RPARSER_streamCharset} property value). 280 * <li>{@link File} containing system encoded text (or charset defined by 281 * {@link ReaderParser#RPARSER_fileCharset} property value). 282 * </ul> 283 * <br>Stream-based parsers can handle the following input class types: 284 * <ul> 285 * <li><jk>null</jk> 286 * <li>{@link InputStream} 287 * <li><code><jk>byte</jk>[]</code> 288 * <li>{@link File} 289 * <li>{@link CharSequence} containing encoded bytes according to the {@link InputStreamParser#ISPARSER_binaryFormat} setting. 290 * </ul> 291 * @param type 292 * The object type to create. 293 * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 294 * @param args 295 * The type arguments of the class if it's a collection or map. 296 * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 297 * <br>Ignored if the main type is not a map or collection. 298 * @return The parsed object. 299 * @throws ParseException Malformed input encountered. 300 * @throws IOException Thrown by underlying stream. 301 * @see BeanSession#getClassMeta(Type,Type...) for argument syntax for maps and collections. 302 */ 303 public final <T> T read(Object input, Type type, Type...args) throws ParseException, IOException { 304 return p.createSession().parse(input, type, args); 305 } 306 307 /** 308 * Same as {@link #read(Object,Type,Type...)} but reads from a string and thus doesn't throw an <c>IOException</c>. 309 * 310 * @param <T> The class type of the object to create. 311 * @param input 312 * The input. 313 * <br>Character-based parsers can handle the following input class types: 314 * <ul> 315 * <li><jk>null</jk> 316 * <li>{@link Reader} 317 * <li>{@link CharSequence} 318 * <li>{@link InputStream} containing UTF-8 encoded text (or charset defined by 319 * {@link ReaderParser#RPARSER_streamCharset} property value). 320 * <li><code><jk>byte</jk>[]</code> containing UTF-8 encoded text (or charset defined by 321 * {@link ReaderParser#RPARSER_streamCharset} property value). 322 * <li>{@link File} containing system encoded text (or charset defined by 323 * {@link ReaderParser#RPARSER_fileCharset} property value). 324 * </ul> 325 * <br>Stream-based parsers can handle the following input class types: 326 * <ul> 327 * <li><jk>null</jk> 328 * <li>{@link InputStream} 329 * <li><code><jk>byte</jk>[]</code> 330 * <li>{@link File} 331 * <li>{@link CharSequence} containing encoded bytes according to the {@link InputStreamParser#ISPARSER_binaryFormat} setting. 332 * </ul> 333 * @param type 334 * The object type to create. 335 * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 336 * @param args 337 * The type arguments of the class if it's a collection or map. 338 * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 339 * <br>Ignored if the main type is not a map or collection. 340 * @return The parsed object. 341 * @throws ParseException Malformed input encountered. 342 * @see BeanSession#getClassMeta(Type,Type...) for argument syntax for maps and collections. 343 */ 344 public final <T> T read(String input, Type type, Type...args) throws ParseException { 345 return p.createSession().parse(input, type, args); 346 } 347 348 /** 349 * Same as {@link #read(Object, Type, Type...)} except optimized for a non-parameterized class. 350 * 351 * <p> 352 * This is the preferred parse method for simple types since you don't need to cast the results. 353 * 354 * <h5 class='section'>Examples:</h5> 355 * <p class='bcode w800'> 356 * Marshall m = Json.<jsf>DEFAULT</jsf>; 357 * 358 * <jc>// Parse into a string.</jc> 359 * String s = m.read(json, String.<jk>class</jk>); 360 * 361 * <jc>// Parse into a bean.</jc> 362 * MyBean b = m.read(json, MyBean.<jk>class</jk>); 363 * 364 * <jc>// Parse into a bean array.</jc> 365 * MyBean[] ba = m.read(json, MyBean[].<jk>class</jk>); 366 * 367 * <jc>// Parse into a linked-list of objects.</jc> 368 * List l = m.read(json, LinkedList.<jk>class</jk>); 369 * 370 * <jc>// Parse into a map of object keys/values.</jc> 371 * Map m = m.read(json, TreeMap.<jk>class</jk>); 372 * </p> 373 * 374 * @param <T> The class type of the object being created. 375 * @param input 376 * The input. 377 * See {@link #read(Object, Type, Type...)} for details. 378 * @param type The object type to create. 379 * @return The parsed object. 380 * @throws ParseException Malformed input encountered. 381 * @throws IOException Thrown by underlying stream. 382 */ 383 public final <T> T read(Object input, Class<T> type) throws ParseException, IOException { 384 return p.createSession().parse(input, type); 385 } 386 387 /** 388 * Same as {@link #read(Object,Class)} but reads from a string and thus doesn't throw an <c>IOException</c>. 389 * 390 * <p> 391 * This is the preferred parse method for simple types since you don't need to cast the results. 392 * 393 * <h5 class='section'>Examples:</h5> 394 * <p class='bcode w800'> 395 * Marshall m = Json.<jsf>DEFAULT</jsf>; 396 * 397 * <jc>// Parse into a string.</jc> 398 * String s = m.read(json, String.<jk>class</jk>); 399 * 400 * <jc>// Parse into a bean.</jc> 401 * MyBean b = m.read(json, MyBean.<jk>class</jk>); 402 * 403 * <jc>// Parse into a bean array.</jc> 404 * MyBean[] ba = m.read(json, MyBean[].<jk>class</jk>); 405 * 406 * <jc>// Parse into a linked-list of objects.</jc> 407 * List l = m.read(json, LinkedList.<jk>class</jk>); 408 * 409 * <jc>// Parse into a map of object keys/values.</jc> 410 * Map m = m.read(json, TreeMap.<jk>class</jk>); 411 * </p> 412 * 413 * @param <T> The class type of the object being created. 414 * @param input 415 * The input. 416 * See {@link #read(Object, Type, Type...)} for details. 417 * @param type The object type to create. 418 * @return The parsed object. 419 * @throws ParseException Malformed input encountered. 420 */ 421 public final <T> T read(String input, Class<T> type) throws ParseException { 422 return p.createSession().parse(input, type); 423 } 424}