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.*; 017 018import org.apache.juneau.*; 019import org.apache.juneau.parser.*; 020import org.apache.juneau.serializer.*; 021 022/** 023 * Top-level class for a pairing of a {@link Serializer} and {@link Parser} into a single class with convenience read/write methods. 024 * 025 * <p> 026 * The general idea is to combine a single serializer and parser inside a simplified API for reading and writing POJOs. 027 * 028 * <h5 class='figure'>Examples:</h5> 029 * <p class='bcode w800'> 030 * <jc>// Using instance.</jc> 031 * Marshall json = <jk>new</jk> Json(); 032 * MyPojo myPojo = json.read(string, MyPojo.<jk>class</jk>); 033 * String string = json.write(myPojo); 034 * </p> 035 * <p class='bcode w800'> 036 * <jc>// Using DEFAULT instance.</jc> 037 * MyPojo myPojo = Json.<jsf>DEFAULT</jsf>.read(string, MyPojo.<jk>class</jk>); 038 * String string = Json.<jsf>DEFAULT</jsf>.write(myPojo); 039 * </p> 040 * 041 * <h5 class='section'>See Also:</h5> 042 * <ul> 043 * <li class='link'>{@doc juneau-marshall.Marshalls} 044 * </ul> 045 */ 046public abstract class Marshall { 047 048 private final Serializer s; 049 private final Parser p; 050 051 /** 052 * Constructor. 053 * 054 * @param s 055 * The serializer to use for serializing output. 056 * <br>Must not be <jk>null</jk>. 057 * @param p 058 * The parser to use for parsing input. 059 * <br>Must not be <jk>null</jk>. 060 */ 061 protected Marshall(Serializer s, Parser p) { 062 this.s = s; 063 this.p = p; 064 } 065 066 /** 067 * Serializes a POJO directly to either a <code>String</code> or <code><jk>byte</jk>[]</code> depending on the serializer type. 068 * 069 * @param o The object to serialize. 070 * @return 071 * The serialized object. 072 * <br>Character-based serializers will return a <code>String</code> 073 * <br>Stream-based serializers will return a <code><jk>byte</jk>[]</code> 074 * @throws SerializeException If a problem occurred trying to convert the output. 075 */ 076 public Object write(Object o) throws SerializeException { 077 return s.createSession().serialize(o); 078 } 079 080 /** 081 * Serializes a POJO to the specified output stream or writer. 082 * 083 * <p> 084 * Equivalent to calling <code>serializer.createSession().serialize(o, output);</code> 085 * 086 * @param o The object to serialize. 087 * @param output 088 * The output object. 089 * <br>Character-based serializers can handle the following output class types: 090 * <ul> 091 * <li>{@link Writer} 092 * <li>{@link OutputStream} - Output will be written as UTF-8 encoded stream. 093 * <li>{@link File} - Output will be written as system-default encoded stream. 094 * <li>{@link StringBuilder} - Output will be written to the specified string builder. 095 * </ul> 096 * <br>Stream-based serializers can handle the following output class types: 097 * <ul> 098 * <li>{@link OutputStream} 099 * <li>{@link File} 100 * </ul> 101 * @throws SerializeException If a problem occurred trying to convert the output. 102 */ 103 public final void write(Object o, Object output) throws SerializeException { 104 s.createSession().serialize(o, output); 105 } 106 107 /** 108 * Convenience method for serializing an object to a String. 109 * 110 * <p> 111 * For writer-based serializers, this is identical to calling {@link Serializer#serialize(Object)}. 112 * <br>For stream-based serializers, this converts the returned byte array to a string based on 113 * the {@link OutputStreamSerializer#OSSERIALIZER_binaryFormat} setting. 114 * 115 * @param o The object to serialize. 116 * @return The output serialized to a string. 117 */ 118 public final String toString(Object o) { 119 try { 120 return s.serializeToString(o); 121 } catch (Exception e) { 122 throw new RuntimeException(e); 123 } 124 } 125 126 127 /** 128 * Convenience method for calling <code>System.out.println(...)</code> on the specified object after calling {@link #toString(Object)}. 129 * 130 * @param o The object to serialize and then send to the console. 131 * @return This object (for method chaining). 132 */ 133 public final Marshall println(Object o) { 134 System.out.println(toString(o)); 135 return this; 136 } 137 138 /** 139 * Parses input into the specified object type. 140 * 141 * <p> 142 * The type can be a simple type (e.g. beans, strings, numbers) or parameterized type (collections/maps). 143 * 144 * <h5 class='section'>Examples:</h5> 145 * <p class='bcode w800'> 146 * Marshall m = Json.<jsf>DEFAULT</jsf>; 147 * 148 * <jc>// Parse into a linked-list of strings.</jc> 149 * List l = m.read(json, LinkedList.<jk>class</jk>, String.<jk>class</jk>); 150 * 151 * <jc>// Parse into a linked-list of beans.</jc> 152 * List l = m.read(json, LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>); 153 * 154 * <jc>// Parse into a linked-list of linked-lists of strings.</jc> 155 * List l = m.read(json, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>); 156 * 157 * <jc>// Parse into a map of string keys/values.</jc> 158 * Map m = m.read(json, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>); 159 * 160 * <jc>// Parse into a map containing string keys and values of lists containing beans.</jc> 161 * Map m = m.read(json, TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>); 162 * </p> 163 * 164 * <p> 165 * <code>Collection</code> classes are assumed to be followed by zero or one objects indicating the element type. 166 * 167 * <p> 168 * <code>Map</code> classes are assumed to be followed by zero or two meta objects indicating the key and value types. 169 * 170 * <p> 171 * The array can be arbitrarily long to indicate arbitrarily complex data structures. 172 * 173 * <h5 class='section'>Notes:</h5> 174 * <ul class='spaced-list'> 175 * <li> 176 * Use the {@link #read(Object, Class)} method instead if you don't need a parameterized map/collection. 177 * </ul> 178 * 179 * @param <T> The class type of the object to create. 180 * @param input 181 * The input. 182 * <br>Character-based parsers can handle the following input class types: 183 * <ul> 184 * <li><jk>null</jk> 185 * <li>{@link Reader} 186 * <li>{@link CharSequence} 187 * <li>{@link InputStream} containing UTF-8 encoded text (or charset defined by 188 * {@link ReaderParser#RPARSER_inputStreamCharset} property value). 189 * <li><code><jk>byte</jk>[]</code> containing UTF-8 encoded text (or charset defined by 190 * {@link ReaderParser#RPARSER_inputStreamCharset} property value). 191 * <li>{@link File} containing system encoded text (or charset defined by 192 * {@link ReaderParser#RPARSER_fileCharset} property value). 193 * </ul> 194 * <br>Stream-based parsers can handle the following input class types: 195 * <ul> 196 * <li><jk>null</jk> 197 * <li>{@link InputStream} 198 * <li><code><jk>byte</jk>[]</code> 199 * <li>{@link File} 200 * <li>{@link CharSequence} containing encoded bytes according to the {@link InputStreamParser#ISPARSER_binaryFormat} setting. 201 * </ul> 202 * @param type 203 * The object type to create. 204 * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 205 * @param args 206 * The type arguments of the class if it's a collection or map. 207 * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 208 * <br>Ignored if the main type is not a map or collection. 209 * @return The parsed object. 210 * @throws ParseException 211 * If the input contains a syntax error or is malformed, or is not valid for the specified type. 212 * @see BeanSession#getClassMeta(Type,Type...) for argument syntax for maps and collections. 213 */ 214 public final <T> T read(Object input, Type type, Type...args) throws ParseException { 215 return p.createSession().parse(input, type, args); 216 } 217 218 /** 219 * Same as {@link #read(Object, Type, Type...)} except optimized for a non-parameterized class. 220 * 221 * <p> 222 * This is the preferred parse method for simple types since you don't need to cast the results. 223 * 224 * <h5 class='section'>Examples:</h5> 225 * <p class='bcode w800'> 226 * Marshall m = Json.<jsf>DEFAULT</jsf>; 227 * 228 * <jc>// Parse into a string.</jc> 229 * String s = m.read(json, String.<jk>class</jk>); 230 * 231 * <jc>// Parse into a bean.</jc> 232 * MyBean b = m.read(json, MyBean.<jk>class</jk>); 233 * 234 * <jc>// Parse into a bean array.</jc> 235 * MyBean[] ba = m.read(json, MyBean[].<jk>class</jk>); 236 * 237 * <jc>// Parse into a linked-list of objects.</jc> 238 * List l = m.read(json, LinkedList.<jk>class</jk>); 239 * 240 * <jc>// Parse into a map of object keys/values.</jc> 241 * Map m = m.read(json, TreeMap.<jk>class</jk>); 242 * </p> 243 * 244 * @param <T> The class type of the object being created. 245 * @param input 246 * The input. 247 * See {@link #read(Object, Type, Type...)} for details. 248 * @param type The object type to create. 249 * @return The parsed object. 250 * @throws ParseException 251 * If the input contains a syntax error or is malformed, or is not valid for the specified type. 252 */ 253 public final <T> T read(Object input, Class<T> type) throws ParseException { 254 return p.createSession().parse(input, type); 255 } 256}