| package org.apache.juneau.marshall; |
| // *************************************************************************************************************************** |
| // * 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. * |
| // *************************************************************************************************************************** |
| |
| import static org.apache.juneau.internal.ExceptionUtils.*; |
| |
| import java.io.*; |
| import java.lang.reflect.*; |
| import java.text.*; |
| |
| import org.apache.juneau.*; |
| import org.apache.juneau.parser.*; |
| import org.apache.juneau.parser.ParseException; |
| import org.apache.juneau.serializer.*; |
| |
| /** |
| * Top-level class for a pairing of a {@link Serializer} and {@link Parser} into a single class with convenience read/write methods. |
| * |
| * <p> |
| * The general idea is to combine a single serializer and parser inside a simplified API for reading and writing POJOs. |
| * |
| * <h5 class='figure'>Examples:</h5> |
| * <p class='bcode w800'> |
| * <jc>// Using instance.</jc> |
| * Marshall json = <jk>new</jk> Json(); |
| * MyPojo myPojo = json.read(string, MyPojo.<jk>class</jk>); |
| * String string = json.write(myPojo); |
| * </p> |
| * <p class='bcode w800'> |
| * <jc>// Using DEFAULT instance.</jc> |
| * MyPojo myPojo = Json.<jsf>DEFAULT</jsf>.read(string, MyPojo.<jk>class</jk>); |
| * String string = Json.<jsf>DEFAULT</jsf>.write(myPojo); |
| * </p> |
| * |
| * <ul class='seealso'> |
| * <li class='link'>{@doc Marshalls} |
| * </ul> |
| */ |
| public abstract class Marshall { |
| |
| private final Serializer s; |
| private final Parser p; |
| |
| /** |
| * Constructor. |
| * |
| * @param s |
| * The serializer to use for serializing output. |
| * <br>Must not be <jk>null</jk>. |
| * @param p |
| * The parser to use for parsing input. |
| * <br>Must not be <jk>null</jk>. |
| */ |
| protected Marshall(Serializer s, Parser p) { |
| this.s = s; |
| this.p = p; |
| } |
| |
| /** |
| * Returns the serializer associated with this marshall. |
| * |
| * @return The serializer associated with this marshall. |
| */ |
| public Serializer getSerializer() { |
| return s; |
| } |
| |
| /** |
| * Returns the parser associated with this marshall. |
| * |
| * @return The parser associated with this marshall. |
| */ |
| public Parser getParser() { |
| return p; |
| } |
| |
| /** |
| * Serializes a POJO directly to either a <c>String</c> or <code><jk>byte</jk>[]</code> depending on the serializer type. |
| * |
| * @param o The object to serialize. |
| * @return |
| * The serialized object. |
| * <br>Character-based serializers will return a <c>String</c> |
| * <br>Stream-based serializers will return a <code><jk>byte</jk>[]</code> |
| * @throws SerializeException If a problem occurred trying to convert the output. |
| */ |
| public Object write(Object o) throws SerializeException { |
| return s.createSession().serialize(o); |
| } |
| |
| /** |
| * Serializes a POJO to the specified output stream or writer. |
| * |
| * <p> |
| * Equivalent to calling <c>serializer.createSession().serialize(o, output);</c> |
| * |
| * @param o The object to serialize. |
| * @param output |
| * The output object. |
| * <br>Character-based serializers can handle the following output class types: |
| * <ul> |
| * <li>{@link Writer} |
| * <li>{@link OutputStream} - Output will be written as UTF-8 encoded stream. |
| * <li>{@link File} - Output will be written as system-default encoded stream. |
| * <li>{@link StringBuilder} - Output will be written to the specified string builder. |
| * </ul> |
| * <br>Stream-based serializers can handle the following output class types: |
| * <ul> |
| * <li>{@link OutputStream} |
| * <li>{@link File} |
| * </ul> |
| * @throws SerializeException If a problem occurred trying to convert the output. |
| * @throws IOException Thrown by underlying stream. |
| */ |
| public final void write(Object o, Object output) throws SerializeException, IOException { |
| s.createSession().serialize(o, output); |
| } |
| |
| /** |
| * Convenience method for serializing an object to a String. |
| * |
| * <p> |
| * For writer-based serializers, this is identical to calling {@link Serializer#serialize(Object)}. |
| * <br>For stream-based serializers, this converts the returned byte array to a string based on |
| * the {@link OutputStreamSerializer#OSSERIALIZER_binaryFormat} setting. |
| * |
| * @param o The object to serialize. |
| * @return The output serialized to a string. |
| */ |
| public final String toString(Object o) { |
| try { |
| return s.serializeToString(o); |
| } catch (Exception e) { |
| throw runtimeException(e); |
| } |
| } |
| |
| |
| /** |
| * Convenience method for calling <c>System.out.println(...)</c> on the specified object after calling {@link #toString(Object)}. |
| * |
| * @param o The object to serialize and then send to the console. |
| * @return This object (for method chaining). |
| */ |
| public final Marshall println(Object o) { |
| System.out.println(toString(o)); |
| return this; |
| } |
| |
| /** |
| * Prints a message with arguments to {@link System#out}. |
| * |
| * <p> |
| * Arguments are automatically converted to strings using this marshaller. |
| * |
| * <p> |
| * Useful for debug messages. |
| * |
| * <h5 class='figure'>Example:</h5> |
| * <p class='bcode'> |
| * SimpleJson.<jsf>DEFAULT</jsf>.out(<js>"myPojo={0}"</js>, myPojo); |
| * </p> |
| * |
| * @param msg The {@link MessageFormat}-styled message. |
| * @param args The arguments sent to the the formatter after running them through this marshaller. |
| * @return This object (for method chaining). |
| */ |
| public final Marshall out(String msg, Object...args) { |
| System.out.println(format(msg, args)); |
| return this; |
| } |
| |
| /** |
| * Prints a message with arguments to {@link System#err}. |
| * |
| * <p> |
| * Arguments are automatically converted to strings using this marshaller. |
| * |
| * <p> |
| * Useful for debug messages. |
| * |
| * <h5 class='figure'>Example:</h5> |
| * <p class='bcode'> |
| * SimpleJson.<jsf>DEFAULT</jsf>.err(<js>"myPojo={0}"</js>, myPojo); |
| * </p> |
| * |
| * @param msg The {@link MessageFormat}-styled message. |
| * @param args The arguments sent to the the formatter after running them through this marshaller. |
| * @return This object (for method chaining). |
| */ |
| public final Marshall err(String msg, Object...args) { |
| System.err.println(format(msg, args)); // NOT DEBUG |
| return this; |
| } |
| |
| /** |
| * Formats a message with arguments. |
| * |
| * <p> |
| * Arguments are automatically converted to strings using this marshaller. |
| * |
| * <p> |
| * Useful for debug messages. |
| * |
| * <h5 class='figure'>Example:</h5> |
| * <p class='bcode'> |
| * String msg = SimpleJson.<jsf>DEFAULT</jsf>.format(<js>"myPojo={0}"</js>, myPojo); |
| * </p> |
| * |
| * @param msg The {@link MessageFormat}-styled message. |
| * @param args The arguments sent to the the formatter after running them through this marshaller. |
| * @return This object (for method chaining). |
| */ |
| public final String format(String msg, Object...args) { |
| for (int i = 0; i < args.length; i++) |
| args[i] = toString(args[i]); |
| return MessageFormat.format(msg, args); |
| } |
| |
| /** |
| * Parses input into the specified object type. |
| * |
| * <p> |
| * The type can be a simple type (e.g. beans, strings, numbers) or parameterized type (collections/maps). |
| * |
| * <h5 class='section'>Examples:</h5> |
| * <p class='bcode w800'> |
| * Marshall m = Json.<jsf>DEFAULT</jsf>; |
| * |
| * <jc>// Parse into a linked-list of strings.</jc> |
| * List l = m.read(json, LinkedList.<jk>class</jk>, String.<jk>class</jk>); |
| * |
| * <jc>// Parse into a linked-list of beans.</jc> |
| * List l = m.read(json, LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>); |
| * |
| * <jc>// Parse into a linked-list of linked-lists of strings.</jc> |
| * List l = m.read(json, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>); |
| * |
| * <jc>// Parse into a map of string keys/values.</jc> |
| * Map m = m.read(json, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>); |
| * |
| * <jc>// Parse into a map containing string keys and values of lists containing beans.</jc> |
| * Map m = m.read(json, TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>); |
| * </p> |
| * |
| * <p> |
| * <c>Collection</c> classes are assumed to be followed by zero or one objects indicating the element type. |
| * |
| * <p> |
| * <c>Map</c> classes are assumed to be followed by zero or two meta objects indicating the key and value types. |
| * |
| * <p> |
| * The array can be arbitrarily long to indicate arbitrarily complex data structures. |
| * |
| * <ul class='notes'> |
| * <li> |
| * Use the {@link #read(Object, Class)} method instead if you don't need a parameterized map/collection. |
| * </ul> |
| * |
| * @param <T> The class type of the object to create. |
| * @param input |
| * The input. |
| * <br>Character-based parsers can handle the following input class types: |
| * <ul> |
| * <li><jk>null</jk> |
| * <li>{@link Reader} |
| * <li>{@link CharSequence} |
| * <li>{@link InputStream} containing UTF-8 encoded text (or charset defined by |
| * {@link ReaderParser#RPARSER_streamCharset} property value). |
| * <li><code><jk>byte</jk>[]</code> containing UTF-8 encoded text (or charset defined by |
| * {@link ReaderParser#RPARSER_streamCharset} property value). |
| * <li>{@link File} containing system encoded text (or charset defined by |
| * {@link ReaderParser#RPARSER_fileCharset} property value). |
| * </ul> |
| * <br>Stream-based parsers can handle the following input class types: |
| * <ul> |
| * <li><jk>null</jk> |
| * <li>{@link InputStream} |
| * <li><code><jk>byte</jk>[]</code> |
| * <li>{@link File} |
| * <li>{@link CharSequence} containing encoded bytes according to the {@link InputStreamParser#ISPARSER_binaryFormat} setting. |
| * </ul> |
| * @param type |
| * The object type to create. |
| * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} |
| * @param args |
| * The type arguments of the class if it's a collection or map. |
| * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} |
| * <br>Ignored if the main type is not a map or collection. |
| * @return The parsed object. |
| * @throws ParseException Malformed input encountered. |
| * @throws IOException Thrown by underlying stream. |
| * @see BeanSession#getClassMeta(Type,Type...) for argument syntax for maps and collections. |
| */ |
| public final <T> T read(Object input, Type type, Type...args) throws ParseException, IOException { |
| return p.createSession().parse(input, type, args); |
| } |
| |
| /** |
| * Same as {@link #read(Object,Type,Type...)} but reads from a string and thus doesn't throw an <c>IOException</c>. |
| * |
| * @param <T> The class type of the object to create. |
| * @param input |
| * The input. |
| * <br>Character-based parsers can handle the following input class types: |
| * <ul> |
| * <li><jk>null</jk> |
| * <li>{@link Reader} |
| * <li>{@link CharSequence} |
| * <li>{@link InputStream} containing UTF-8 encoded text (or charset defined by |
| * {@link ReaderParser#RPARSER_streamCharset} property value). |
| * <li><code><jk>byte</jk>[]</code> containing UTF-8 encoded text (or charset defined by |
| * {@link ReaderParser#RPARSER_streamCharset} property value). |
| * <li>{@link File} containing system encoded text (or charset defined by |
| * {@link ReaderParser#RPARSER_fileCharset} property value). |
| * </ul> |
| * <br>Stream-based parsers can handle the following input class types: |
| * <ul> |
| * <li><jk>null</jk> |
| * <li>{@link InputStream} |
| * <li><code><jk>byte</jk>[]</code> |
| * <li>{@link File} |
| * <li>{@link CharSequence} containing encoded bytes according to the {@link InputStreamParser#ISPARSER_binaryFormat} setting. |
| * </ul> |
| * @param type |
| * The object type to create. |
| * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} |
| * @param args |
| * The type arguments of the class if it's a collection or map. |
| * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} |
| * <br>Ignored if the main type is not a map or collection. |
| * @return The parsed object. |
| * @throws ParseException Malformed input encountered. |
| * @see BeanSession#getClassMeta(Type,Type...) for argument syntax for maps and collections. |
| */ |
| public final <T> T read(String input, Type type, Type...args) throws ParseException { |
| return p.createSession().parse(input, type, args); |
| } |
| |
| /** |
| * Same as {@link #read(Object, Type, Type...)} except optimized for a non-parameterized class. |
| * |
| * <p> |
| * This is the preferred parse method for simple types since you don't need to cast the results. |
| * |
| * <h5 class='section'>Examples:</h5> |
| * <p class='bcode w800'> |
| * Marshall m = Json.<jsf>DEFAULT</jsf>; |
| * |
| * <jc>// Parse into a string.</jc> |
| * String s = m.read(json, String.<jk>class</jk>); |
| * |
| * <jc>// Parse into a bean.</jc> |
| * MyBean b = m.read(json, MyBean.<jk>class</jk>); |
| * |
| * <jc>// Parse into a bean array.</jc> |
| * MyBean[] ba = m.read(json, MyBean[].<jk>class</jk>); |
| * |
| * <jc>// Parse into a linked-list of objects.</jc> |
| * List l = m.read(json, LinkedList.<jk>class</jk>); |
| * |
| * <jc>// Parse into a map of object keys/values.</jc> |
| * Map m = m.read(json, TreeMap.<jk>class</jk>); |
| * </p> |
| * |
| * @param <T> The class type of the object being created. |
| * @param input |
| * The input. |
| * See {@link #read(Object, Type, Type...)} for details. |
| * @param type The object type to create. |
| * @return The parsed object. |
| * @throws ParseException Malformed input encountered. |
| * @throws IOException Thrown by underlying stream. |
| */ |
| public final <T> T read(Object input, Class<T> type) throws ParseException, IOException { |
| return p.createSession().parse(input, type); |
| } |
| |
| /** |
| * Same as {@link #read(Object,Class)} but reads from a string and thus doesn't throw an <c>IOException</c>. |
| * |
| * <p> |
| * This is the preferred parse method for simple types since you don't need to cast the results. |
| * |
| * <h5 class='section'>Examples:</h5> |
| * <p class='bcode w800'> |
| * Marshall m = Json.<jsf>DEFAULT</jsf>; |
| * |
| * <jc>// Parse into a string.</jc> |
| * String s = m.read(json, String.<jk>class</jk>); |
| * |
| * <jc>// Parse into a bean.</jc> |
| * MyBean b = m.read(json, MyBean.<jk>class</jk>); |
| * |
| * <jc>// Parse into a bean array.</jc> |
| * MyBean[] ba = m.read(json, MyBean[].<jk>class</jk>); |
| * |
| * <jc>// Parse into a linked-list of objects.</jc> |
| * List l = m.read(json, LinkedList.<jk>class</jk>); |
| * |
| * <jc>// Parse into a map of object keys/values.</jc> |
| * Map m = m.read(json, TreeMap.<jk>class</jk>); |
| * </p> |
| * |
| * @param <T> The class type of the object being created. |
| * @param input |
| * The input. |
| * See {@link #read(Object, Type, Type...)} for details. |
| * @param type The object type to create. |
| * @return The parsed object. |
| * @throws ParseException Malformed input encountered. |
| */ |
| public final <T> T read(String input, Class<T> type) throws ParseException { |
| return p.createSession().parse(input, type); |
| } |
| } |