org.apache.juneau.serializer.Serializer Maven / Gradle / Ivy
// ***************************************************************************************************************************
// * 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. *
// ***************************************************************************************************************************
package org.apache.juneau.serializer;
import java.io.*;
import org.apache.juneau.*;
import org.apache.juneau.annotation.*;
import org.apache.juneau.http.*;
/**
* Parent class for all Juneau serializers.
*
* Description:
*
* Base serializer class that serves as the parent class for all serializers.
*
*
* The purpose of this class is:
*
* - Maintain a read-only configuration state of a serializer (i.e. {@link SerializerContext}).
*
- Create session objects used for serializing POJOs (i.e. {@link SerializerSession}).
*
- Provide convenience methods for serializing POJOs without having to construct session objects.
*
*
*
* Subclasses should extend directly from {@link OutputStreamSerializer} or {@link WriterSerializer} depending on
* whether it's a stream or character based serializer.
*/
public abstract class Serializer extends CoreObject {
//-------------------------------------------------------------------------------------------------------------------
// Configurable properties
//-------------------------------------------------------------------------------------------------------------------
private static final String PREFIX = "Serializer.";
/**
* Configuration property: Max serialization depth.
*
*
* - Name:
"Serializer.maxDepth"
* - Data type:
Integer
* - Default:
100
* - Session-overridable:
true
*
*
*
* Abort serialization if specified depth is reached in the POJO tree.
* If this depth is exceeded, an exception is thrown.
* This prevents stack overflows from occurring when trying to serialize models with recursive references.
*/
public static final String SERIALIZER_maxDepth = PREFIX + "maxDepth";
/**
* Configuration property: Initial depth.
*
*
* - Name:
"Serializer.initialDepth"
* - Data type:
Integer
* - Default:
0
* - Session-overridable:
true
*
*
*
* The initial indentation level at the root.
* Useful when constructing document fragments that need to be indented at a certain level.
*/
public static final String SERIALIZER_initialDepth = PREFIX + "initialDepth";
/**
* Configuration property: Automatically detect POJO recursions.
*
*
* - Name:
"Serializer.detectRecursions"
* - Data type:
Boolean
* - Default:
false
* - Session-overridable:
true
*
*
*
* Specifies that recursions should be checked for during serialization.
*
*
* Recursions can occur when serializing models that aren't true trees, but rather contain loops.
*
*
* The behavior when recursions are detected depends on the value for {@link #SERIALIZER_ignoreRecursions}.
*
*
* For example, if a model contains the links A->B->C->A, then the JSON generated will look like
* the following when SERIALIZER_ignoreRecursions is true ...
* {A:{B:{C:null}}}
*
*
Notes:
*
* - Checking for recursion can cause a small performance penalty.
*
*/
public static final String SERIALIZER_detectRecursions = PREFIX + "detectRecursions";
/**
* Configuration property: Ignore recursion errors.
*
*
* - Name:
"Serializer.ignoreRecursions"
* - Data type:
Boolean
* - Default:
false
* - Session-overridable:
true
*
*
*
* Used in conjunction with {@link #SERIALIZER_detectRecursions}.
* Setting is ignored if SERIALIZER_detectRecursions is false .
*
*
* If true , when we encounter the same object when serializing a tree, we set the value to null .
* Otherwise, an exception is thrown.
*/
public static final String SERIALIZER_ignoreRecursions = PREFIX + "ignoreRecursions";
/**
* Configuration property: Use whitespace.
*
*
* - Name:
"Serializer.useWhitespace"
* - Data type:
Boolean
* - Default:
false
* - Session-overridable:
true
*
*
*
* If true , whitespace is added to the output to improve readability.
*
*
* This setting does not apply to the MessagePack serializer.
*/
public static final String SERIALIZER_useWhitespace = PREFIX + "useWhitespace";
/**
* Configuration property: Maximum indentation.
*
*
* - Name:
"Serializer.maxIndent"
* - Data type:
Integer
* - Default:
100
* - Session-overridable:
true
*
*
*
* Specifies the maximum indentation level in the serialized document.
*
*
* This setting does not apply to the MessagePack or RDF serializers.
*/
public static final String SERIALIZER_maxIndent = PREFIX + "maxIndent";
/**
* Configuration property: Add "_type" properties when needed.
*
*
* - Name:
"Serializer.addBeanTypeProperties"
* - Data type:
Boolean
* - Default:
true
* - Session-overridable:
true
*
*
*
* If true , then "_type" properties will be added to beans if their type cannot be inferred
* through reflection.
* This is used to recreate the correct objects during parsing if the object types cannot be inferred.
* For example, when serializing a {@code Map} field, where the bean class cannot be determined from
* the value type.
*/
public static final String SERIALIZER_addBeanTypeProperties = PREFIX + "addBeanTypeProperties";
/**
* Configuration property: Quote character.
*
*
* - Name:
"Serializer.quoteChar"
* - Data type:
Character
* - Default:
'"'
* - Session-overridable:
true
*
*
*
* This is the character used for quoting attributes and values.
*
*
* This setting does not apply to the MessagePack or RDF serializers.
*/
public static final String SERIALIZER_quoteChar = PREFIX + "quoteChar";
/**
* Configuration property: Trim null bean property values.
*
*
* - Name:
"Serializer.trimNullProperties"
* - Data type:
Boolean
* - Default:
true
* - Session-overridable:
true
*
*
*
* If true , null bean values will not be serialized to the output.
*
*
* Note that enabling this setting has the following effects on parsing:
*
* -
* Map entries with
null values will be lost.
*
*/
public static final String SERIALIZER_trimNullProperties = PREFIX + "trimNullProperties";
/**
* Configuration property: Trim empty lists and arrays.
*
*
* - Name:
"Serializer.trimEmptyLists"
* - Data type:
Boolean
* - Default:
false
* - Session-overridable:
true
*
*
*
* If true , empty list values will not be serialized to the output.
*
*
* Note that enabling this setting has the following effects on parsing:
*
* -
* Map entries with empty list values will be lost.
*
-
* Bean properties with empty list values will not be set.
*
*/
public static final String SERIALIZER_trimEmptyCollections = PREFIX + "trimEmptyLists";
/**
* Configuration property: Trim empty maps.
*
*
* - Name:
"Serializer.trimEmptyMaps"
* - Data type:
Boolean
* - Default:
false
* - Session-overridable:
true
*
*
*
* If true , empty map values will not be serialized to the output.
*
*
* Note that enabling this setting has the following effects on parsing:
*
* -
* Bean properties with empty map values will not be set.
*
*/
public static final String SERIALIZER_trimEmptyMaps = PREFIX + "trimEmptyMaps";
/**
* Configuration property: Trim strings.
*
*
* - Name:
"Serializer.trimStrings"
* - Data type:
Boolean
* - Default:
false
* - Session-overridable:
true
*
*
*
* If true , string values will be trimmed of whitespace using {@link String#trim()} before being serialized.
*/
public static final String SERIALIZER_trimStrings = PREFIX + "trimStrings";
/**
* Configuration property: URI context bean.
*
*
* - Name:
"Serializer.uriContext"
* - Data type: {@link UriContext}
*
- Default: {@link UriContext#DEFAULT}
*
- Session-overridable:
true
*
*
*
* Bean used for resolution of URIs to absolute or root-relative form.
*
*
Example:
*
* "{authority:'http://localhost:10000',contextRoot:'/myContext',servletPath:'/myServlet',pathInfo:'/foo'}"
*
*/
public static final String SERIALIZER_uriContext = PREFIX + "uriContext";
/**
* Configuration property: URI resolution.
*
*
* - Name:
"Serializer.uriResolution"
* - Data type: {@link UriResolution}
*
- Default: {@link UriResolution#NONE}
*
- Session-overridable:
true
*
*
*
* Defines the resolution level for URIs when serializing any of the following:
*
* - {@link java.net.URI}
*
- {@link java.net.URL}
*
- Properties annotated with {@link org.apache.juneau.annotation.URI @URI}
*
*
*
* Possible values are:
*
* - {@link UriResolution#ABSOLUTE}
* - Resolve to an absolute URL (e.g.
"http://host:port/context-root/servlet-path/path-info" ).
* - {@link UriResolution#ROOT_RELATIVE}
* - Resolve to a root-relative URL (e.g.
"/context-root/servlet-path/path-info" ).
* - {@link UriResolution#NONE}
* - Don't do any URL resolution.
*
*/
public static final String SERIALIZER_uriResolution = PREFIX + "uriResolution";
/**
* Configuration property: URI relativity.
*
*
* - Name:
"Serializer.uriRelativity"
* - Data type: {@link UriRelativity}
*
- Default: {@link UriRelativity#RESOURCE}
*
- Session-overridable:
true
*
*
*
* Defines what relative URIs are relative to when serializing any of the following:
*
* - {@link java.net.URI}
*
- {@link java.net.URL}
*
- Properties annotated with {@link org.apache.juneau.annotation.URI @URI}
*
*
*
* Possible values are:
*
* - {@link UriRelativity#RESOURCE}
* - Relative URIs should be considered relative to the servlet URI.
*
- {@link UriRelativity#PATH_INFO}
* - Relative URIs should be considered relative to the request URI.
*
*/
public static final String SERIALIZER_uriRelativity = PREFIX + "uriRelativity";
/**
* Configuration property: Sort arrays and collections alphabetically.
*
*
* - Name:
"Serializer.sortCollections"
* - Data type:
Boolean
* - Default:
false
* - Session-overridable:
true
*
*
*
* Note that this introduces a performance penalty.
*/
public static final String SERIALIZER_sortCollections = PREFIX + "sortCollections";
/**
* Configuration property: Sort maps alphabetically.
*
*
* - Name:
"Serializer.sortMaps"
* - Data type:
Boolean
* - Default:
false
* - Session-overridable:
true
*
*
*
* Note that this introduces a performance penalty.
*/
public static final String SERIALIZER_sortMaps = PREFIX + "sortMaps";
/**
* Configuration property: Abridged output.
*
*
* - Name:
"Serializer.abridged"
* - Data type:
Boolean
* - Default:
false
* - Session-overridable:
true
*
*
*
* When enabled, it is assumed that the parser knows the exact Java POJO type being parsed, and therefore top-level
* type information that might normally be included to determine the data type will not be serialized.
*
*
* For example, when serializing a POJO with a {@link Bean#typeName()} value, a "_type" will be added when
* this setting is disabled, but not added when it is enabled.
*/
public static final String SERIALIZER_abridged = PREFIX + "abridged";
/**
* Configuration property: Serializer listener.
*
*
* - Name:
"Serializer.listener"
* - Data type:
Class<? extends SerializerListener>
* - Default:
null
* - Session-overridable:
true
*
*
*
* Class used to listen for errors and warnings that occur during serialization.
*/
public static final String SERIALIZER_listener = PREFIX + "listener";
//-------------------------------------------------------------------------------------------------------------------
// Instance
//-------------------------------------------------------------------------------------------------------------------
private final MediaType[] accept;
private final MediaType produces;
// Hidden constructors to force subclass from OuputStreamSerializer or WriterSerializer.
Serializer(PropertyStore propertyStore, String produces, String...accept) {
super(propertyStore);
this.produces = MediaType.forString(produces);
if (accept.length == 0) {
this.accept = new MediaType[]{this.produces};
} else {
this.accept = new MediaType[accept.length];
for (int i = 0; i < accept.length; i++) {
this.accept[i] = MediaType.forString(accept[i]);
}
}
}
@Override /* CoreObject */
public SerializerBuilder builder() {
return new SerializerBuilder(propertyStore);
}
//--------------------------------------------------------------------------------
// Abstract methods
//--------------------------------------------------------------------------------
/**
* Returns true if this serializer subclasses from {@link WriterSerializer}.
*
* @return true if this serializer subclasses from {@link WriterSerializer}.
*/
public abstract boolean isWriterSerializer();
/**
* Create the session object used for actual serialization of objects.
*
* @param args
* Runtime arguments.
* These specify session-level information such as locale and URI context.
* It also include session-level properties that override the properties defined on the bean and serializer
* contexts.
* @return
* The new session object.
*
Note that you must call {@link SerializerSession#close()} on this object to perform any necessary
* cleanup.
*/
public abstract SerializerSession createSession(SerializerSessionArgs args);
//--------------------------------------------------------------------------------
// Convenience methods
//--------------------------------------------------------------------------------
/**
* Shortcut for calling createSession(null )
.
*
* @return
* The new session object.
*
Note that you must call {@link SerializerSession#close()} on this object to perform any necessary
* cleanup.
*/
public final SerializerSession createSession() {
return createSession(createDefaultSessionArgs());
}
/**
* Creates the session arguments object that gets passed to the {@link #createSession(SerializerSessionArgs)} method.
*
* @return
* A new default session arguments object.
*
The arguments can be modified before passing to the {@link #createSession(SerializerSessionArgs)}.
*/
public final SerializerSessionArgs createDefaultSessionArgs() {
return new SerializerSessionArgs(ObjectMap.EMPTY_MAP, null, null, null, getResponseContentType(), null);
}
/**
* Serializes a POJO to the specified output stream or writer.
*
*
* Equivalent to calling serializer.createSession().serialize(o, output);
*
* @param o The object to serialize.
* @param output
* The output object.
*
Character-based serializers can handle the following output class types:
*
* - {@link Writer}
*
- {@link OutputStream} - Output will be written as UTF-8 encoded stream.
*
- {@link File} - Output will be written as system-default encoded stream.
*
- {@link StringBuilder} - Output will be written to the specified string builder.
*
*
Stream-based serializers can handle the following output class types:
*
* - {@link OutputStream}
*
- {@link File}
*
* @throws SerializeException If a problem occurred trying to convert the output.
*/
public final void serialize(Object o, Object output) throws SerializeException {
SerializerSession s = createSession();
try {
s.serialize(o, output);
} finally {
s.close();
}
}
/**
* Shortcut method for serializing objects directly to either a String
or byte []
* depending on the serializer type.
*
* @param o The object to serialize.
* @return
* The serialized object.
*
Character-based serializers will return a String
*
Stream-based serializers will return a byte []
* @throws SerializeException If a problem occurred trying to convert the output.
*/
public Object serialize(Object o) throws SerializeException {
SerializerSession s = createSession();
try {
return s.serialize(o);
} finally {
s.close();
}
}
//--------------------------------------------------------------------------------
// Other methods
//--------------------------------------------------------------------------------
/**
* Returns the media types handled based on the value of the accept
parameter passed into the constructor.
*
* @return The list of media types. Never null .
*/
public final MediaType[] getMediaTypes() {
return accept;
}
/**
* Optional method that returns the response Content-Type
for this serializer if it is different from
* the matched media type.
*
*
* This method is specified to override the content type for this serializer.
* For example, the {@link org.apache.juneau.json.JsonSerializer.Simple} class returns that it handles media type
* "text/json+simple" , but returns "text/json" as the actual content type.
* This allows clients to request specific 'flavors' of content using specialized Accept
header values.
*
*
* This method is typically meaningless if the serializer is being used stand-alone (i.e. outside of a REST server
* or client).
*
* @return The response content type. If null , then the matched media type is used.
*/
public final MediaType getResponseContentType() {
return produces;
}
}