com.sun.jersey.api.json.JSONConfiguration Maven / Gradle / Ivy
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2010-2012 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.sun.jersey.api.json;
import com.sun.jersey.json.impl.ImplMessages;
import com.sun.jersey.json.impl.JSONHelper;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* An immutable configuration of JSON notation and options. JSONConfiguration could be used
* for configuring the JSON notation on {@link JSONJAXBContext}
*
* @author [email protected]
*/
public class JSONConfiguration {
/**
* A ResourceConfig feature, which allows you to enable JSON/POJO mapping functionality
* in Jersey. If set to true, your application will be capable of transforming JSON
* data to and out of POJOs. This also includes any JAXB beans existing in your
* application. I.e. all those beans would not be processed via XML, but rather
* directly marshaled and un-marshaled to and from JSON using the POJO mapping functionality.
*/
public final static String FEATURE_POJO_MAPPING
= "com.sun.jersey.api.json.POJOMappingFeature";
/**
* Enumeration of supported JSON notations.
*/
public enum Notation {
/**
* The mapped (default) JSON notation.
* Example JSON expression:
* {"columns":[{"id":"userid","label":"UserID"},{"id":"name","label":"User Name"}],"rows":{"userid":"1621","name":"Grotefend"}}
*
*/
MAPPED,
/**
* The mapped Jettison JSON notation.
* Example JSON expression:
* {"userTable":{"columns":[{"id":"userid","label":"UserID"},{"id":"name","label":"User Name"}],"rows":{"userid":1621,"name":"Grotefend"}}}
*
*/
MAPPED_JETTISON,
/**
* The mapped Badgerfish JSON notation.
* Example JSON expression:
* {"userTable":{"columns":[{"id":{"$":"userid"},"label":{"$":"UserID"}},{"id":{"$":"name"},"label":{"$":"User Name"}}],"rows":{"userid":{"$":"1621"},"name":{"$":"Grotefend"}}}}
*
*/
BADGERFISH,
/**
* The natural JSON notation, leveraging closely-coupled JAXB RI integration.
* Example JSON expression:
* {"columns":[{"id":"userid","label":"UserID"},{"id":"name","label":"User Name"}],"rows":[{"userid":1621,"name":"Grotefend"}]}
*
*/
NATURAL
}
private final Notation notation;
private final Collection arrays;
private final Collection attrsAsElems;
private final Collection nonStrings;
private final boolean rootUnwrapping;
private final boolean humanReadableFormatting;
private final Map jsonXml2JsonNs;
private final boolean usePrefixAtNaturalAttributes;
private final Character namespaceSeparator;
/**
* Builder class for constructing {@link JSONConfiguration} options
*/
public static class Builder {
private final Notation notation;
protected Collection arrays = new HashSet(0);
protected Collection attrsAsElems = new HashSet(0);
protected Collection nonStrings = new HashSet(0);
protected boolean rootUnwrapping = true;
protected boolean humanReadableFormatting = false;
protected Map jsonXml2JsonNs = new HashMap(0);
protected boolean usePrefixAtNaturalAttributes = false;
protected Character namespaceSeparator = '.';
private Builder(Notation notation) {
this.notation = notation;
}
/**
* Constructs a new immutable {@link JSONConfiguration} object based on options set on this Builder
*
* @return a non-null {@link JSONConfiguration} instance
*/
public JSONConfiguration build() {
return new JSONConfiguration(this);
}
private void copyAttributes(JSONConfiguration jc) {
arrays.addAll(jc.getArrays());
attrsAsElems.addAll(jc.getAttributeAsElements());
nonStrings.addAll(jc.getNonStrings());
rootUnwrapping = jc.isRootUnwrapping();
humanReadableFormatting = jc.isHumanReadableFormatting();
jsonXml2JsonNs.putAll(jc.getXml2JsonNs());
usePrefixAtNaturalAttributes = jc.isUsingPrefixesAtNaturalAttributes();
namespaceSeparator = jc.getNsSeparator();
}
}
/**
* Builder class for constructing {@link JSONConfiguration} options
* for the {@link Notation#NATURAL} convention.
*/
public static class NaturalBuilder extends Builder {
private NaturalBuilder(Notation notation) {
super(notation);
}
/**
* Setter for XML root element unwrapping.
* This property is valid for the {@link JSONConfiguration.Notation#MAPPED}
* and {@link JSONConfiguration.Notation#NATURAL} notations only.
*
* If set to true, JSON code corresponding to the XML root element will be stripped out
*
* The default value is false.
* @param rootUnwrapping if set to true, JSON code corresponding to the
* XML root element will be stripped out.
* @return the natural builder.
*/
public NaturalBuilder rootUnwrapping(boolean rootUnwrapping) {
this.rootUnwrapping = rootUnwrapping;
return this;
}
/**
* If set to true, generated JSON will contain new-line characters and indentation, so that
* the output is easy to read for people.
* This property is valid for the {@link JSONConfiguration.Notation#NATURAL} notation only.
*
* The default value is false.
*
* @param humanReadableFormatting whether the output should be formatted to be readable by humans easily.
* @return the natural builder.
*/
public NaturalBuilder humanReadableFormatting(boolean humanReadableFormatting) {
this.humanReadableFormatting = humanReadableFormatting;
return this;
}
/**
* JSON names corresponding to XML attributes will be written using a '@' prefix
* This property is valid for the {@link JSONConfiguration.Notation#NATURAL} notation only.
* @return the natural builder.
*/
public NaturalBuilder usePrefixesAtNaturalAttributes() {
this.usePrefixAtNaturalAttributes = true;
return this;
}
}
/**
* Builder class for constructing {@link JSONConfiguration} options
* for the {@link Notation#MAPPED_JETTISON} convention.
*/
public static class MappedJettisonBuilder extends Builder {
private MappedJettisonBuilder(Notation notation) {
super(notation);
rootUnwrapping = false;
}
/**
* Setter for XML to JSON namespace mapping.
* This property is valid for the {@link JSONConfiguration.Notation#MAPPED_JETTISON}
* and {@link JSONConfiguration.Notation#MAPPED} notations only.
*
* The value is a map with zero or more
* key/value pairs, where the key is an XML namespace and the value
* is the prefix to use as the replacement for the XML namespace.
*
* The default value is a map with zero key/value pairs.
*/
public MappedJettisonBuilder xml2JsonNs(Map jsonXml2JsonNs) {
this.jsonXml2JsonNs = jsonXml2JsonNs;
return this;
}
}
/**
* Builder class for constructing {@link JSONConfiguration} options
* for the {@link Notation#MAPPED} convention.
*/
public static class MappedBuilder extends Builder {
private MappedBuilder(Notation notation) {
super(notation);
}
/**
* Adds name(s) to JSON arrays configuration property.
* This property is valid for the {@link JSONConfiguration.Notation#MAPPED} notation only.
*
* The property value is a collection of strings representing JSON object names.
* Those objects will be declared as arrays in JSON document.
*
* For example, consider that the property value is not set and the
* JSON document is { ..., "arr1":"single element", ... }
.
* If the property value is set to contain "arr1"
then
* the JSON document would become { ..., "arr1":["single element"], ... }
.
*
* The default value is an empty collection.
* @param arrays an array of strings representing JSON object names.
* @return the mapped builder.
*/
public MappedBuilder arrays(String... arrays) {
this.arrays.addAll(Arrays.asList(arrays));
return this;
}
/**
* Adds name(s) toJSON attributes as elements property.
* This property is valid for the {@link JSONConfiguration.Notation#MAPPED} notation only.
*
* The value is a collection of string values that are
* object names that correspond to XML attribute information items.
* The value of an object name in the JSON document that exists in the collection
* of object names will be declared as an element as not as an attribute if
* the object corresponds to an XML attribute information item.
*
* For example, consider that the property value is not set and the
* JSON document is { ..., "@number":"12", ... }
.
* If the property value is set to contain "number"
* then the JSON document would be { ..., "number":"12", ... }
.
*
* The default value is an empty collection.
* @param attributeAsElements an array of string values that are
* object names that correspond to XML attribute information items.
* @return the mapped builder.
*/
public MappedBuilder attributeAsElement(String... attributeAsElements) {
this.attrsAsElems.addAll(Arrays.asList(attributeAsElements));
return this;
}
/**
* Adds name(s) JSON non-string values property.
* This property is valid for the {@link JSONConfiguration.Notation#MAPPED} notation only.
*
* The value is collection of string values that are
* object names.
* The value of an object name in the JSON document that exists in the collection
* of object names will be declared as non-string value, which is not surrounded
* by double quotes.
*
* For example, consider that the property value is not set and the
* JSON document is { ..., "anumber":"12", ... }
.
* If the property value is set to contain "anumber"
* then the JSON document would be { ..., "anumber":12, ... }
.
*
* The default value is an empty collection.
* @param nonStrings an array of string values that are
* object names
* @return the mapped builder.
*/
public MappedBuilder nonStrings(String... nonStrings) {
this.nonStrings.addAll(Arrays.asList(nonStrings));
return this;
}
/**
* Setter for XML to JSON namespace mapping.
* This property is valid for the {@link JSONConfiguration.Notation#MAPPED_JETTISON}
* and {@link JSONConfiguration.Notation#MAPPED} notations only.
*
* The value is a map with zero or more
* key/value pairs, where the key is an XML namespace and the value
* is the prefix to use as the replacement for the XML namespace.
*
* The default value is a map with zero key/value pairs.
*/
public MappedBuilder xml2JsonNs(Map jsonXml2JsonNs) {
this.jsonXml2JsonNs = jsonXml2JsonNs;
return this;
}
/**
* Setter for XML namespace separator.
* This property is valid for the {@link JSONConfiguration.Notation#MAPPED} notation only.
*
* The value is a character used to separate a namespace identifier from the name
* of a XML attribute/element in JSON.
*
* The default value is dot character ('.').
*/
public MappedBuilder nsSeparator(Character separator) {
if (separator == null) {
throw new NullPointerException("Namespace separator can not be null!");
}
this.namespaceSeparator = separator;
return this;
}
/**
* Setter for XML root element unwrapping.
* This property is valid for the {@link JSONConfiguration.Notation#MAPPED}
* and {@link JSONConfiguration.Notation#NATURAL} notations only.
*
* If set to true, JSON code corresponding to the XML root element will be stripped out
*
* The default value is false.
* @param rootUnwrapping if set to true, JSON code corresponding to the
* XML root element will be stripped out.
* @return the mapped builder.
*/
public MappedBuilder rootUnwrapping(boolean rootUnwrapping) {
this.rootUnwrapping = rootUnwrapping;
return this;
}
}
private JSONConfiguration(Builder b) {
notation = b.notation;
arrays = b.arrays;
attrsAsElems = b.attrsAsElems;
nonStrings = b.nonStrings;
rootUnwrapping = b.rootUnwrapping;
humanReadableFormatting = b.humanReadableFormatting;
jsonXml2JsonNs = b.jsonXml2JsonNs;
usePrefixAtNaturalAttributes = b.usePrefixAtNaturalAttributes;
namespaceSeparator = b.namespaceSeparator;
}
/**
* A static method for obtaining {@link JSONConfiguration} instance with humanReadableFormatting
* set according to formatted parameter.
*
* @param c original instance of {@link JSONConfiguration}, can't be null
* @param formatted whether the output should be formatted.
* @return copy of provided {@link JSONConfiguration} with humanReadableFormatting set to formatted.
* @throws IllegalArgumentException when provided JSONConfiguration is null.
*/
public static JSONConfiguration createJSONConfigurationWithFormatted(JSONConfiguration c, boolean formatted) throws IllegalArgumentException {
if(c == null) {
throw new IllegalArgumentException("JSONConfiguration can't be null");
}
if (c.isHumanReadableFormatting() == formatted) {
return c;
}
Builder b = copyBuilder(c);
b.humanReadableFormatting = formatted;
return b.build();
}
/**
* A static method for obtaining {@link JSONConfiguration} instance with rootUnwrapping
* set according to formatted parameter.
*
* @param c original instance of {@link JSONConfiguration}, can't be null
* @param rootUnwrapping
* @return copy of provided {@link JSONConfiguration} with humanReadableFormatting set to formatted.
* @throws IllegalArgumentException when provided JSONConfiguration is null.
*/
public static JSONConfiguration createJSONConfigurationWithRootUnwrapping(JSONConfiguration c, boolean rootUnwrapping) throws IllegalArgumentException {
if(c == null){
throw new IllegalArgumentException("JSONConfiguration can't be null");
}
if (c.isRootUnwrapping() == rootUnwrapping) {
return c;
}
Builder b = copyBuilder(c);
b.rootUnwrapping = rootUnwrapping;
return b.build();
}
/**
* The default JSONConfiguration uses {@link JSONConfiguration.Notation#MAPPED} notation with root unwrapping option set to true.
*/
public static final JSONConfiguration DEFAULT = mapped().rootUnwrapping(true).build();
/**
* A static method for obtaining a builder of {@link JSONConfiguration} instance, which will use {@link Notation#NATURAL} JSON notation.
* After getting the builder, you can set configuration options on it, and finally get an immutable JSONConfiguration
* instance using the {@link Builder#build() } method.
*
* @return a builder for JSONConfiguration instance
*/
public static NaturalBuilder natural() {
// this is to make sure people trying to use NATURAL notation will get clear message what is missing, when an old JAXB RI version is used
if (!JSONHelper.isNaturalNotationEnabled()) {
Logger.getLogger(JSONConfiguration.class.getName()).log(Level.SEVERE, ImplMessages.ERROR_JAXB_RI_2_1_10_MISSING());
throw new RuntimeException(ImplMessages.ERROR_JAXB_RI_2_1_10_MISSING());
}
return new NaturalBuilder(Notation.NATURAL);
}
/**
* A static method for obtaining a builder of {@link JSONConfiguration} instance, which will use {@link Notation#MAPPED} JSON notation.
* After getting the builder, you can set configuration options on it and finally get an immutable JSONConfiguration
* instance the using {@link Builder#build() } method.
*
* @return a builder for JSONConfiguration instance
*/
public static MappedBuilder mapped() {
return new MappedBuilder(Notation.MAPPED);
}
/**
* A static method for obtaining a builder of {@link JSONConfiguration} instance, which will use {@link Notation#MAPPED_JETTISON} JSON notation.
* After getting the builder, you can set configuration options on it and finally get an immutable JSONConfiguration
* instance using the {@link Builder#build() } method.
*
* @return a builder for JSONConfiguration instance
*/
public static MappedJettisonBuilder mappedJettison() {
return new MappedJettisonBuilder(Notation.MAPPED_JETTISON);
}
/**
* A static method for obtaining a builder of {@link JSONConfiguration} instance, which will use {@link Notation#BADGERFISH} JSON notation.
* After getting the builder, you can set configuration options on it and finally get an immutable JSONConfiguration
* instance using the {@link Builder#build() } method.
*
* @return a builder for JSONConfiguration instance
*/
public static Builder badgerFish() {
Builder badgerFishBuilder = new Builder(Notation.BADGERFISH);
badgerFishBuilder.rootUnwrapping = false;
return badgerFishBuilder;
}
public static Builder copyBuilder(final JSONConfiguration jc) {
Builder result = new Builder(jc.getNotation());
switch (jc.notation) {
case BADGERFISH:
result = new Builder(jc.getNotation());
break;
case MAPPED_JETTISON:
result = new MappedJettisonBuilder(jc.getNotation());
break;
case MAPPED:
result = new MappedBuilder(jc.getNotation());
break;
case NATURAL:
result = new NaturalBuilder(jc.getNotation());
break;
}
result.copyAttributes(jc);
return result;
}
/**
* Returns JSON notation selected for this configuration
* @return JSON notation
*/
public Notation getNotation() {
return notation;
}
/**
* Returns JSON array names property
* This property is valid for the {@link JSONConfiguration.Notation#MAPPED} notation only.
* @return collection of array names
* @see MappedBuilder#arrays(java.lang.String...)
*/
public Collection getArrays() {
return (arrays != null) ? Collections.unmodifiableCollection(arrays) : null;
}
/**
* Returns names of attributes, which will be handled as elements
* This property is valid for the {@link JSONConfiguration.Notation#MAPPED} notation only.
* @return attribute as element names collection
* @see MappedBuilder#attributeAsElement(java.lang.String...)
*/
public Collection getAttributeAsElements() {
return (attrsAsElems != null) ? Collections.unmodifiableCollection(attrsAsElems) : null;
}
/**
* Returns a map for XML to JSON namespace mapping
* This property is valid for the {@link JSONConfiguration.Notation#MAPPED} notation only.
* @return a map for XML to JSON namespace mapping
* @see MappedBuilder#xml2JsonNs(java.util.Map)
*/
public Map getXml2JsonNs() {
return (jsonXml2JsonNs != null) ? Collections.unmodifiableMap(jsonXml2JsonNs) : null;
}
/**
* Returns XML namespace separator, which is used when constructing JSON identifiers
* for XML elements/attributes in other than default namespace
* This property is valid for the {@link JSONConfiguration.Notation#MAPPED} notation only.
* @return XML namespace separator character
* @see MappedBuilder#nsSeparator(java.lang.Character)
*/
public Character getNsSeparator() {
return namespaceSeparator;
}
/**
* Returns names of JSON objects, which will be serialized out as non-strings, i.e. without delimiting their values with double quotes
* This property is valid for the {@link JSONConfiguration.Notation#MAPPED} notation only.
* @return name of non-string JSON objects
* @see MappedBuilder#nonStrings(java.lang.String...)
*/
public Collection getNonStrings() {
return (nonStrings != null) ? Collections.unmodifiableCollection(nonStrings) : null;
}
/**
* Says if the root element will be stripped off
* This property is valid for the {@link JSONConfiguration.Notation#MAPPED}
* and {@link Notation#NATURAL} notations.
* @return true, if root element has to be stripped off
* @see MappedBuilder#rootUnwrapping(boolean)
*/
public boolean isRootUnwrapping() {
return rootUnwrapping;
}
/**
* Says if the JSON names corresponding to XML attributes should use a '@' prefix.
* This property is valid for the {@link JSONConfiguration.Notation#NATURAL} notation only.
* @return true, if prefixes are added
* @see NaturalBuilder#usePrefixesAtNaturalAttributes()
*/
public boolean isUsingPrefixesAtNaturalAttributes() {
return usePrefixAtNaturalAttributes;
}
/**
* Says if the output JSON will be formatted with new-line characters
* and indentation so that it is easy to read for people.
* This property is valid for the {@link JSONConfiguration.Notation#NATURAL} notation only.
* @return true, if formatting is applied on the output JSON
* @see NaturalBuilder#humanReadableFormatting(boolean)
*/
public boolean isHumanReadableFormatting() {
return humanReadableFormatting;
}
@Override
public String toString() {
return String.format("{notation:%s,rootStripping:%b}", notation, rootUnwrapping);
}
}