
com.cedarsoftware.io.WriteOptionsBuilder Maven / Gradle / Ivy
package com.cedarsoftware.io;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import com.cedarsoftware.io.reflect.Accessor;
import com.cedarsoftware.io.reflect.AccessorFactory;
import com.cedarsoftware.io.reflect.factories.GetMethodAccessorFactory;
import com.cedarsoftware.io.reflect.factories.IsMethodAccessorFactory;
import com.cedarsoftware.io.reflect.filters.FieldFilter;
import com.cedarsoftware.io.reflect.filters.MethodFilter;
import com.cedarsoftware.io.reflect.filters.field.EnumFieldFilter;
import com.cedarsoftware.io.reflect.filters.field.StaticFieldFilter;
import com.cedarsoftware.io.reflect.filters.method.DefaultMethodFilter;
import com.cedarsoftware.io.reflect.filters.method.NamedMethodFilter;
import com.cedarsoftware.util.ClassUtilities;
import com.cedarsoftware.util.ClassValueMap;
import com.cedarsoftware.util.ClassValueSet;
import com.cedarsoftware.util.ConcurrentSet;
import com.cedarsoftware.util.Convention;
import com.cedarsoftware.util.LRUCache;
import com.cedarsoftware.util.ReflectionUtils;
import com.cedarsoftware.util.StringUtilities;
import com.cedarsoftware.util.LoggingConfig;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Builder class for building the writeOptions.
*
* @author John DeRegnaucourt ([email protected])
* @author Kenny Partlow ([email protected])
*
* Copyright (c) Cedar Software LLC
*
* Licensed 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
*
* License
*
* 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.
*/
public class WriteOptionsBuilder {
private static final Logger LOG = Logger.getLogger(WriteOptionsBuilder.class.getName());
static { LoggingConfig.init(); }
// The BASE_* Maps are regular ConcurrentHashMap's because they are not constantly searched, otherwise they would be ClassValueMaps.
private static final Map BASE_ALIAS_MAPPINGS = new ConcurrentHashMap<>();
private static final Map, JsonWriter.JsonClassWriter> BASE_WRITERS = new ConcurrentHashMap<>();
private static final Set> BASE_NON_REFS = new ConcurrentSet<>();
private static final Set> BASE_NOT_CUSTOM_WRITTEN = new ConcurrentSet<>();
static final Map, Set> BASE_EXCLUDED_FIELD_NAMES = new ConcurrentHashMap<>();
private static final Map, Map> BASE_NONSTANDARD_GETTERS = new ConcurrentHashMap<>();;
private static final Map BASE_FIELD_FILTERS = new ConcurrentHashMap<>();
private static final Map BASE_METHOD_FILTERS = new ConcurrentHashMap<>();
private static final Map BASE_ACCESSOR_FACTORIES = new ConcurrentHashMap<>();
private static final WriteOptions defWriteOptions;
private final DefaultWriteOptions options;
static {
ReadOptionsBuilder.loadBaseAliasMappings(WriteOptionsBuilder::addPermanentAlias);
loadBaseWriters();
loadBaseNonRefs();
loadBaseNotCustomWrittenClasses();
loadBaseExcludedFields();
loadBaseNonStandardGetters();
// If the lists below become large, then break these out into load* APIs like we've done for the ones above.
addPermanentFieldFilter("static", new StaticFieldFilter());
addPermanentFieldFilter("enum", new EnumFieldFilter());
addPermanentMethodFilter("default", new DefaultMethodFilter());
addPermanentAccessorFactory("get", new GetMethodAccessorFactory());
addPermanentAccessorFactory("is", new IsMethodAccessorFactory());
defWriteOptions = new WriteOptionsBuilder().build();
// low-level alias support (bigint, bigdec)
ClassUtilities.addPermanentClassAlias(BigInteger.class, "bigint");
ClassUtilities.addPermanentClassAlias(BigInteger.class, "BigInt");
ClassUtilities.addPermanentClassAlias(BigDecimal.class, "bigdec");
ClassUtilities.addPermanentClassAlias(BigDecimal.class, "BigDec");
}
/**
* @return Default WriteOptions - no construction needed, unmodifiable.
*/
public static WriteOptions getDefaultWriteOptions() {
return defWriteOptions;
}
/**
* Start with default options
*/
public WriteOptionsBuilder() {
options = new DefaultWriteOptions();
// Start with all BASE_ALIAS_MAPPINGS (more aliases can be added to this instance, and more aliases
// can be added to the BASE_ALIAS_MAPPINGS via the static method, so that all instances get them.)
options.nonStandardGetters.putAll(BASE_NONSTANDARD_GETTERS);
options.aliasTypeNames.putAll(BASE_ALIAS_MAPPINGS);
options.customWrittenClasses.putAll(BASE_WRITERS);
options.nonRefClasses.addAll(BASE_NON_REFS);
options.notCustomWrittenClasses.addAll(BASE_NOT_CUSTOM_WRITTEN);
options.excludedFieldNames.putAll(BASE_EXCLUDED_FIELD_NAMES);
options.fieldFilters.putAll(BASE_FIELD_FILTERS);
options.methodFilters.putAll(BASE_METHOD_FILTERS);
options.accessorFactories.putAll(BASE_ACCESSOR_FACTORIES);
}
/**
* Copy another WriteOptions as a starting point. If null is passed in, then you get the default options.
*/
public WriteOptionsBuilder(WriteOptions copy) {
this();
if (copy != null) {
DefaultWriteOptions other = (DefaultWriteOptions) copy;
// Copy simple settings
options.allowNanAndInfinity = other.allowNanAndInfinity;
options.closeStream = other.closeStream;
options.classLoader = other.classLoader;
options.enumPublicFieldsOnly = other.enumPublicFieldsOnly;
options.enumSetWrittenOldWay = other.enumSetWrittenOldWay;
options.forceMapOutputAsTwoArrays = other.forceMapOutputAsTwoArrays;
options.prettyPrint = other.prettyPrint;
options.lruSize = other.lruSize;
options.shortMetaKeys = other.shortMetaKeys;
options.showTypeInfo = other.showTypeInfo;
options.skipNullFields = other.skipNullFields;
options.writeLongsAsStrings = other.writeLongsAsStrings;
// Copy complex settings
options.includedFieldNames.clear();
options.includedFieldNames.putAll(other.includedFieldNames);
options.nonStandardGetters.clear();
options.nonStandardGetters.putAll(other.nonStandardGetters);
options.aliasTypeNames.clear();
options.aliasTypeNames.putAll(other.aliasTypeNames);
options.excludedFieldNames.clear();
options.excludedFieldNames.putAll(other.excludedFieldNames);
options.customWrittenClasses.clear();
options.customWrittenClasses.putAll(other.customWrittenClasses);
options.notCustomWrittenClasses.clear();
options.notCustomWrittenClasses.addAll(other.notCustomWrittenClasses);
options.nonRefClasses.clear();
options.nonRefClasses.addAll(other.nonRefClasses);
options.fieldFilters.clear();
options.fieldFilters.putAll(other.fieldFilters);
options.methodFilters.clear();
options.methodFilters.putAll(other.methodFilters);
options.accessorFactories.clear();
options.accessorFactories.putAll(other.accessorFactories);
// Copy caches
options.accessorsCache = new LRUCache<>(other.lruSize);
options.accessorsCache.putAll(other.accessorsCache);
options.classMetaCache = new LRUCache<>(other.lruSize);
options.classMetaCache.putAll(other.classMetaCache);
}
}
/**
* Call this method to add a permanent (JVM lifetime) alias of a class to an often shorter, name. All WriteOptions
* will automatically be created with any permanent aliases added to them.
*
* @param clazz Class that will be aliased by a shorter name in the JSON.
* @param alias Shorter alias name, for example, "ArrayList" as opposed to "java.util.ArrayList"
*/
public static void addPermanentAlias(Class> clazz, String alias) {
BASE_ALIAS_MAPPINGS.put(clazz.getName(), alias);
}
/**
* Call this method to remove alias patterns from the WriteOptionsBuilder so that when it writes JSON,
* the classes that match the passed in pattern are not aliased. This API matches your wildcard pattern
* of *, ?, and characters against class names in its cache. It removes the entries (className to aliasName)
* from the cache to prevent the resultant classes from being aliased during output.
*
* @param classNamePattern String pattern to match class names. This String matches using a wild-card
* pattern, where * matches anything and ? matches one character. As many * or ? can be used as needed.
*/
public static void removePermanentAliasTypeNamesMatching(String classNamePattern) {
String regex = StringUtilities.wildcardToRegexString(classNamePattern);
Pattern pattern = Pattern.compile(regex);
BASE_ALIAS_MAPPINGS.keySet().removeIf(key -> pattern.matcher(key).matches());
}
/**
* Call this method to add a permanent (JVM lifetime) excluded field name of class. All WriteOptions will
* automatically be created this field field on the excluded list.
*
* @param clazz Class that contains the named field.
* @param fieldName to be excluded.
*/
public static void addPermanentExcludedField(Class> clazz, String fieldName) {
BASE_EXCLUDED_FIELD_NAMES.computeIfAbsent(clazz, cls -> ConcurrentHashMap.newKeySet()).add(fieldName);
}
/**
* Call this method to add a permanent (JVM lifetime) class that should not be treated as referencable
* when being written out to JSON. This means it will never have an @id nor @ref. This feature is
* useful for small, immutable classes.
*
* @param clazz Class that will no longer be treated as referenceable when being written to JSON.
*/
public static void addPermanentNonRef(Class> clazz) {
BASE_NON_REFS.add(clazz);
}
/**
* Call this method to add a permanent (JVM lifetime) class that should not be written with a custom
* writer when being written out to JSON.
*
* @param clazz Class that will no longer be written with a custom writer.
*/
public static void addPermanentNotCustomWrittenClass(Class> clazz) {
BASE_NOT_CUSTOM_WRITTEN.add(clazz);
}
/**
* Call this method to add a custom JSON writer to json-io. It will
* associate the Class 'c' to the writer you pass in. The writers are
* found with isAssignableFrom(). If this is too broad, causing too
* many classes to be associated to the custom writer, you can indicate
* that json-io should not use a custom write for a particular class,
* by calling the addNotCustomWrittenClass() method. This method will add
* the custom writer such that it will be there permanently, for the
* life of the JVM (static).
*
* @param clazz Class to assign a custom JSON writer to
* @param writer The JsonClassWriter which will write the custom JSON format of class.
*/
public static void addPermanentWriter(Class> clazz, JsonWriter.JsonClassWriter writer) {
BASE_WRITERS.put(clazz, writer);
}
/**
* This option permits adding non-standard getters (used when writing JSON) that access properties from objects,
* where the method name does not follow a standard setter/getter property name. For example, on java.time.Instance,
* to get the 'second' field, the accessor method is 'getEpochSecond()'.
* Anything added here will automatically be made in all WriteOptions.
* @param clazz Class that has the non-standard getter. java.time.Instance in the example above.
* @param field String name of the class property. 'second' in the example above.
* @param methodName The name of the non-standard method used to get the field value. 'getEpochSecond' in the example above.
*/
public static void addPermanentNonStandardGetter(Class> clazz, String field, String methodName) {
BASE_NONSTANDARD_GETTERS.computeIfAbsent(clazz, cls -> new ConcurrentHashMap<>()).put(field, methodName);
}
/**
* Add a FieldFilter that is JVM lifecycle scoped.
* @param fieldFilter {@link FieldFilter} class used to eliminate fields from being included in the serialized JSON.
*/
public static void addPermanentFieldFilter(String name, FieldFilter fieldFilter) {
BASE_FIELD_FILTERS.put(name, fieldFilter);
}
/**
* Add a MethodFilter that is JVM lifecycle scoped. All WriteOptions instances will contain this filter.
* @param name String name of this particular MethodFilter instance.
* @param methodFilter {@link MethodFilter} class used to eliminate a particular accessor method from being used.
*/
public static void addPermanentMethodFilter(String name, MethodFilter methodFilter) {
BASE_METHOD_FILTERS.put(name, methodFilter);
}
/**
* Remove a permanently registered MethodFilter from json-io.
*
* @param name String name of the MethodFilter to remove.
*/
public static void removePermanentMethodFilter(String name) {
BASE_METHOD_FILTERS.remove(name);
}
/**
* Add a MethodFilter that is JVM lifecycle scoped. All WriteOptions instances will contain this filter.
* @param name String name of this particular method filter instance.
* @param clazz class that contains the method to be filtered (can be derived class, with field defined on parent class).
* @param methodName String name of no-argument method to be filtered.
*/
public static void addPermanentNamedMethodFilter(String name, Class> clazz, String methodName) {
BASE_METHOD_FILTERS.put(name, new NamedMethodFilter(clazz, methodName));
}
/**
* Add an AccessorFactory that is JVM lifecycle scoped. All WriteOptions instances will contain this AccessorFactory.
* It is the job of an AccessorFactory to provide a possible method name for a particular field. json-io ships with
* a GetMethodAccessorFactory and an IsMethodAccessFactory. These produce a possible method name for a given field.
* When a field on a Java class is being accessed (read), and it cannot be obtained directly, then all AccessoryFactory
* instances will be consulted until an API can be used to read the field.
* @param name String name of AccessorFactory. Each factory should have it's own unique name. This will allow these
* to be defined in a file, later if needed.
* @param factory AccessorFactory subclass.
*/
public static void addPermanentAccessorFactory(String name, AccessorFactory factory) {
BASE_ACCESSOR_FACTORIES.put(name, factory);
}
/**
* Remove a permanently registered AccessorFactory from json-io.
*
* @param name String name of the AccessorFactory to remove.
*/
public static void removePermanentAccessorFactory(String name) {
BASE_ACCESSOR_FACTORIES.remove(name);
}
/**
* @param loader ClassLoader to use when writing JSON to resolve String named classes.
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder classLoader(ClassLoader loader) {
options.classLoader = loader;
return this;
}
/**
* @param shortMetaKeys boolean true to turn on short meta-keys, false for long.
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder shortMetaKeys(boolean shortMetaKeys) {
options.shortMetaKeys = shortMetaKeys;
return this;
}
/**
* @param aliases Map containing String class names to alias names. The passed in Map will
* be copied, and be the new baseline settings.
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder aliasTypeNames(Map aliases) {
aliases.forEach(this::addUniqueAlias);
return this;
}
/**
* @param type Class to alias
* @param alias String shorter name to use, typically.
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder aliasTypeName(Class> type, String alias) {
options.aliasTypeNames.put(type.getName(), alias);
return this;
}
/**
* @param typeName String class name
* @param alias String shorter name to use, typically.
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder aliasTypeName(String typeName, String alias) {
addUniqueAlias(typeName, alias);
return this;
}
/**
* Since we are swapping keys/values, we must check for duplicate values (which are now keys).
* @param typeName String fully qualified class name.
* @param alias String shorter alias name.
*/
private void addUniqueAlias(String typeName, String alias) {
Convention.throwIfClassNotFound(typeName, options.classLoader);
Convention.throwIfKeyExists(options.aliasTypeNames, typeName, "Tried to create @type alias '" + alias + "' for '" + typeName + "', but it is already aliased to: " + options.aliasTypeNames.get(typeName));
options.aliasTypeNames.put(typeName, alias);
}
/**
* Remove alias entries from this WriteOptionsBuilder instance where the Java fully qualified string
* class name matches the passed in wildcard pattern.
* @param typeNamePattern String pattern to match class names. This String matches using a wild-card
* pattern, where * matches anything and ? matches one character. As many * or ? can be used as needed.
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder removeAliasTypeNamesMatching(String typeNamePattern) {
String regex = StringUtilities.wildcardToRegexString(typeNamePattern);
Pattern pattern = Pattern.compile(regex);
options.aliasTypeNames.keySet().removeIf(key -> pattern.matcher(key).matches());
return this;
}
/**
* Set to always show type
*
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder showTypeInfoAlways() {
options.showTypeInfo = WriteOptions.ShowType.ALWAYS;
return this;
}
/**
* Set to never show type
*
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder showTypeInfoNever() {
options.showTypeInfo = WriteOptions.ShowType.NEVER;
return this;
}
/**
* Set to show minimal type. This means that when the type of object can be inferred, a type field will not
* be output. A Field that points to an instance of the same time, or a typed [] of objects don't need the type
* info. However, an Object[], a Collection with no generics, the reader will need to know what type the JSON
* object is, in order to instantiate the write Java class to which the information will be copied.
*
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder showTypeInfoMinimal() {
options.showTypeInfo = WriteOptions.ShowType.MINIMAL;
return this;
}
/**
* @param prettyPrint boolean 'prettyPrint' setting, true to turn on, false will turn off.
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder prettyPrint(boolean prettyPrint) {
options.prettyPrint = prettyPrint;
return this;
}
/**
* @param size Max size of the cache for Class fields and Class accessors
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder lruSize(int size) {
options.lruSize = size;
Map, List> accessorCacheCopy = options.accessorsCache;
options.accessorsCache = new LRUCache<>(options.getLruSize());
options.accessorsCache.putAll(accessorCacheCopy);
Map, Map> classMetaCacheCopy = options.classMetaCache;
options.classMetaCache = new LRUCache<>(options.getLruSize());
options.classMetaCache.putAll(classMetaCacheCopy);
return this;
}
/**
* @param writeLongsAsStrings boolean true to turn on writing longs as Strings, false to write them as
* native JSON longs.
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder writeLongsAsStrings(boolean writeLongsAsStrings) {
options.writeLongsAsStrings = writeLongsAsStrings;
return this;
}
/**
* @param skipNullFields boolean setting, where true indicates fields with null values will not be written
* to the JSON, false will allow the field to still be written.
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder skipNullFields(boolean skipNullFields) {
options.skipNullFields = skipNullFields;
return this;
}
/**
* @param forceMapOutputAsTwoArrays boolean 'forceMapOutputAsTwoArrays' setting. true will force Java Maps to be
* written out as two parallel arrays, once for keys, one array for values.
* false will write out one JSON { } object, if all keys are Strings. If not,
* then the Map will be output as two parallel arrays (@keys:[...], @values:[...]).
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder forceMapOutputAsTwoArrays(boolean forceMapOutputAsTwoArrays) {
options.forceMapOutputAsTwoArrays = forceMapOutputAsTwoArrays;
return this;
}
/**
* @param allowNanAndInfinity boolean 'allowNanAndInfinity' setting. true will allow
* Double and Floats to be output as NAN and INFINITY, false
* and these values will come across as null.
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder allowNanAndInfinity(boolean allowNanAndInfinity) {
options.allowNanAndInfinity = allowNanAndInfinity;
return this;
}
/**
* Option to write out enums as a String, it will write out the enum.name() field.
* This is the default way enums will be written out.
*
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder writeEnumsAsString() {
options.enumWriter = new Writers.EnumsAsStringWriter();
return this;
}
/**
* Option to write out all the member fields of an enum. You can also filter the
* field to write out only the public fields on the enum.
*
* @param writePublicFieldsOnly boolean, only write out the public fields when writing enums as objects. Defaults to false.
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder writeEnumAsJsonObject(boolean writePublicFieldsOnly) {
options.enumWriter = DefaultWriteOptions.nullWriter;
options.enumPublicFieldsOnly = writePublicFieldsOnly;
return this;
}
/**
* Option to write out all the member fields of an enum. You can also filter the
* field to write out only the public fields on the enum.
*
* @param writeOldWay boolean, write EnumSet using @enum instead of @type.
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder writeEnumSetOldWay(boolean writeOldWay) {
options.enumSetWrittenOldWay = writeOldWay;
return this;
}
/**
* @param closeStream boolean set to 'true' to have JsonIo close the OutputStream when it is finished writing
* to it. The default is 'true'. If false, the OutputStream will not be closed, allowing
* you to continue writing further. Example, NDJSON that has new line eliminated JSON
* objects repeated.
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder closeStream(boolean closeStream) {
options.closeStream = closeStream;
return this;
}
/**
* @param customWrittenClasses Map of Class to JsonWriter.JsonClassWriter. Establish the passed in Map as the
* established Map of custom writers to be used when writing JSON. Using this method
* more than once, will set the custom writers to only the values from the Set in
* the last call made.
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder setCustomWrittenClasses(Map, JsonWriter.JsonClassWriter> customWrittenClasses) {
options.customWrittenClasses.clear();
addCustomWrittenClasses(customWrittenClasses);
return this;
}
/**
* @param customWrittenClasses Map of Class to JsonWriter.JsonClassWriter. Adds all custom writers into the custom
* writers map.
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder addCustomWrittenClasses(Map, JsonWriter.JsonClassWriter> customWrittenClasses) {
options.customWrittenClasses.putAll(customWrittenClasses);
return this;
}
/**
* @param clazz Class to add a custom writer for.
* @param customWriter JsonClassWriter to use when the passed in Class is encountered during serialization.
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder addCustomWrittenClass(Class> clazz, JsonWriter.JsonClassWriter customWriter) {
options.customWrittenClasses.put(clazz, customWriter);
return this;
}
/**
* Add a class to the not-customized list - the list of classes that you do not want to be picked up by a
* custom writer (that could happen through inheritance).
*
* @param notCustomClass Class to add to the not-customized list.
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder addNotCustomWrittenClass(Class> notCustomClass) {
options.notCustomWrittenClasses.add(notCustomClass);
return this;
}
/**
* @param notCustomClasses initialize the list of classes on the non-customized list. All prior associations
* will be dropped and this Collection will establish the new list.
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder setNotCustomWrittenClasses(Collection> notCustomClasses) {
options.notCustomWrittenClasses.clear();
options.notCustomWrittenClasses.addAll(notCustomClasses);
return this;
}
/**
* @param clazz Class to add a single field to be included in the written JSON.
* @param includedFieldName String name of field to include in written JSON.
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder addIncludedField(Class> clazz, String includedFieldName) {
Convention.throwIfNull(includedFieldName, "includedFieldName cannot be null");
options.includedFieldNames.computeIfAbsent(clazz, k -> new LinkedHashSet<>()).add(includedFieldName);
return this;
}
/**
* @param clazz Class to add a Collection of fields to be included in written JSON.
* @param includedFieldNames Collection of String name of fields to include in written JSON.
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder addIncludedFields(Class> clazz, Collection includedFieldNames) {
options.includedFieldNames.computeIfAbsent(clazz, k -> new LinkedHashSet<>()).addAll(includedFieldNames);
return this;
}
/**
* @param includedFieldNames Map of Class's mapped to Collection of String field names to include in the written JSON.
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder addIncludedFields(Map, Collection> includedFieldNames) {
includedFieldNames.forEach(this::addIncludedFields);
return this;
}
/**
* @param clazz Class to add a single field to be excluded.
* @param excludedFieldName String name of field to exclude from written JSON.
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder addExcludedField(Class> clazz, String excludedFieldName) {
options.excludedFieldNames.computeIfAbsent(clazz, k -> new LinkedHashSet<>()).add(excludedFieldName);
return this;
}
/**
* @param clazz Class to add a Collection of fields to be excluded in written JSON.
* @param excludedFields Collection of String name of fields to exclude in written JSON.
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder addExcludedFields(Class> clazz, Collection excludedFields) {
options.excludedFieldNames.computeIfAbsent(clazz, k -> new LinkedHashSet<>()).addAll(excludedFields);
return this;
}
/**
* @param excludedFieldNames Map of Class's mapped to Collection of String field names to exclude from written JSON.
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder addExcludedFields(Map, Collection> excludedFieldNames) {
excludedFieldNames.forEach(this::addExcludedFields);
return this;
}
/**
* Change the date-time format to the ISO date format: "yyyy-MM-ddThh:mm:ss.SSSZ". This is for java.util.Date and
* java.sql.Date. The fractional sections are omitted if millis are 0.
*
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder isoDateFormat() {
addCustomWrittenClass(Date.class, new Writers.DateWriter());
return this;
}
/**
* Change the java.uti.Date and java.sql.Date format output to a "long," the number of seconds since Jan 1, 1970
* at midnight. Useful if you do not need to see the JSON, and want to keep the format smaller. This is for
* java.util.Date and java.sql.Date.
*
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder longDateFormat() {
addCustomWrittenClass(Date.class, new Writers.DateAsLongWriter());
return this;
}
/**
* This option permits adding non-standard accessors (used when writing JSON) that access properties from objects,
* where the method name does not follow a standard setter/getter property name. For example, on java.time.Instance,
* to get the 'second' field, the accessor method is 'getEpochSecond()'.
* @param c Class that has the non-standard accessor. java.time.Instance in the example above.
* @param fieldName String name of the class property. 'second' in the example above.
* @param methodName The name of the non-standard method used to get the field value. 'getEpochSecond' in the example above.
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder addNonStandardGetter(Class> c, String fieldName, String methodName) {
Convention.throwIfNull(c, "class cannot be null");
Convention.throwIfNull(fieldName, "fieldName cannot be null");
Convention.throwIfNull(methodName, "methodName cannot be null");
options.nonStandardGetters.computeIfAbsent(c, cls -> new LinkedHashMap<>()).put(fieldName, methodName);
return this;
}
/**
* @param clazz class to add to be considered a non-referenceable object. Just like an "int" for example, any
* class added here will never use an @id/@ref pair. The downside, is that when read,
* each instance, even if the same as another, will use memory.
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder addNonReferenceableClass(Class> clazz) {
Convention.throwIfNull(clazz, "clazz cannot be null");
options.nonRefClasses.add(clazz);
return this;
}
/**
* Add a custom option, which may be useful when writing custom writers. To remove a custom option, add the
* option with the appropriate key and null as the value.
* @param key String name of the custom option
* @param value Object value of the custom option
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder addCustomOption(String key, Object value) {
if (key == null) {
throw new JsonIoException("Custom option key must not be null.");
}
if (value == null) {
options.customOptions.remove(key);
} else {
options.customOptions.put(key, value);
}
return this;
}
/**
* Add FieldFilter to the field filter chain. FieldFilters are presented a chance to eliminate a field by returning true
* from its boolean filter() method. If any FieldFilter returns true, the field is excluded.
* @param filterName String name of filter
* @param filter FieldFilter to add
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder addFieldFilter(String filterName, FieldFilter filter) {
options.fieldFilters.put(filterName, filter);
return this;
}
/**
* Remove named FieldFilter from the filter chain.
* @param filterName String name of FieldFilter to delete
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder removeFieldFilter(String filterName) {
options.fieldFilters.remove(filterName);
return this;
}
/**
* Add MethodFilter to the filter chain. MethodFilters are presented a chance to eliminate a "getter" type accessing
* method by returning true from the boolean filter() method. If any MethodFilter returns true, the accessor method
* is excluded. This means it will drop back to use field access (attempting to access private field in same module
* with setAccessible()). This API is used when you want to write your own implementation of a MethodFilter.
* @param filterName String name of filter
* @param methodFilter {@link MethodFilter} to add
* @return {@link WriteOptionsBuilder} for chained access.
*/
public WriteOptionsBuilder addMethodFilter(String filterName, MethodFilter methodFilter) {
options.methodFilters.put(filterName, methodFilter);
return this;
}
/**
* Add a NamedMethodFilter to the filter chain. NamedMethodFilters are presented a chance to eliminate a "getter"
* type accessing method by returning true from the boolean filter() method. If the NamedMethodFilter matches a class
* and accessor method name, and it has no args, is public, and non-static, the accessor method is excluded. This
* means it will drop back to use field access (attempting to access private field in same module with setAccessible()).
* @param filterName String name of filter
* @param clazz Class containing method to filter
* @param methodName String name of method to not use for accessing value.
* @return {@link WriteOptionsBuilder} for chained access.
*/
public WriteOptionsBuilder addNamedMethodFilter(String filterName, Class> clazz, String methodName) {
options.methodFilters.put(filterName, new NamedMethodFilter(clazz, methodName));
return this;
}
/**
* Remove named MethodFilter from the method filter chain.
* @param filterName String name of filter
* @return {@link WriteOptionsBuilder} for chained access.
*/
public WriteOptionsBuilder removeMethodFilter(String filterName) {
options.methodFilters.remove(filterName);
return this;
}
/**
* Add AccessorFactory to the accessor factories chain. AccessFactories permit adding a standard pattern of
* locating "read" methods, for example, there is a built-in "get" and "is" AccessoryFactory.
* @param factoryName String name of accessor factory
* @param accessorFactory {@link AccessorFactory} to add
* @return {@link WriteOptionsBuilder} for chained access.
*/
public WriteOptionsBuilder addAccessorFactory(String factoryName, AccessorFactory accessorFactory) {
Map map = new LinkedHashMap<>();
map.put(factoryName, accessorFactory);
map.putAll(options.accessorFactories);
options.accessorFactories.clear();
options.accessorFactories.putAll(map);
return this;
}
/**
* Remove named AccessorFactory from the access factories.
* @param factoryName String name of accessor filter
* @return {@link WriteOptionsBuilder} for chained access.
*/
public WriteOptionsBuilder removeAccessorFactory(String factoryName) {
options.accessorFactories.remove(factoryName);
return this;
}
/**
* Seal the instance of this class so that no more changes can be made to it.
*
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptions build() {
options.clearCaches();
options.includedFieldNames = ((ClassValueMap>)options.includedFieldNames).unmodifiableView();
options.nonStandardGetters = ((ClassValueMap
© 2015 - 2025 Weber Informatics LLC | Privacy Policy