http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/jso/package.html ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/jso/package.html b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/jso/package.html new file mode 100755 index 0000000..ac9a67e --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/jso/package.html @@ -0,0 +1,34 @@ +<!DOCTYPE HTML> +<!-- + Licensed Materials - Property of IBM + (c) Copyright IBM Corporation 2014. All Rights Reserved. + + Note to U.S. Government Users Restricted Rights: + Use, duplication or disclosure restricted by GSA ADP Schedule + Contract with IBM Corp. + --> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <style type="text/css"> + /* For viewing in Page Designer */ + @IMPORT url("../../../../../../javadoc.css"); + + /* For viewing in REST interface */ + @IMPORT url("../htdocs/javadoc.css"); + body { + margin: 20px; + } + </style> + <script> + /* Replace all @code and @link tags. */ + window.onload = function() { + document.body.innerHTML = document.body.innerHTML.replace(/\{\@code ([^\}]+)\}/g, '<code>$1</code>'); + document.body.innerHTML = document.body.innerHTML.replace(/\{\@link (([^\}]+)\.)?([^\.\}]+)\}/g, '<code>$3</code>'); + } + </script> +</head> +<body> +<p>Java-serialized-object support</p> +</body> +</html> \ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonClassMeta.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonClassMeta.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonClassMeta.class new file mode 100755 index 0000000..035d4fa Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonClassMeta.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonClassMeta.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonClassMeta.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonClassMeta.java new file mode 100755 index 0000000..7beba59 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonClassMeta.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.json; + +import com.ibm.juno.core.json.annotation.*; +import com.ibm.juno.core.utils.*; + +/** + * Metadata on classes specific to the JSON serializers and parsers pulled from the {@link Json @Json} annotation on the class. + * + * @author James Bognar ([email protected]) + */ +public class JsonClassMeta { + + private final Json json; + private final String wrapperAttr; + + /** + * Constructor. + * + * @param c The class that this annotation is defined on. + */ + public JsonClassMeta(Class<?> c) { + this.json = ReflectionUtils.getAnnotation(Json.class, c); + if (json != null) { + wrapperAttr = StringUtils.nullIfEmpty(json.wrapperAttr()); + } else { + wrapperAttr = null; + } + } + + /** + * Returns the {@link Json} annotation defined on the class. + * + * @return The value of the {@link Json} annotation, or <jk>null</jk> if not specified. + */ + protected Json getAnnotation() { + return json; + } + + /** + * Returns the {@link Json#wrapperAttr()} annotation defined on the class. + * + * @return The value of the {@link Json#wrapperAttr()} annotation, or <jk>null</jk> if not specified. + */ + protected String getWrapperAttr() { + return wrapperAttr; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonParser.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonParser.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonParser.class new file mode 100755 index 0000000..fe41f60 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonParser.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonParser.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonParser.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonParser.java new file mode 100755 index 0000000..42cd8b9 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonParser.java @@ -0,0 +1,852 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2011, 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.json; + +import static com.ibm.juno.core.json.JsonParserProperties.*; + +import java.io.*; +import java.lang.reflect.*; +import java.util.*; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.annotation.*; +import com.ibm.juno.core.filter.*; +import com.ibm.juno.core.parser.*; +import com.ibm.juno.core.utils.*; + +/** + * Parses any valid JSON text into a POJO model. + * + * + * <h6 class='topic'>Media types</h6> + * <p> + * Handles <code>Content-Type</code> types: <code>application/json, text/json</code> + * + * + * <h6 class='topic'>Description</h6> + * <p> + * This parser uses a state machine, which makes it very fast and efficient. It parses JSON in about 70% of the + * time that it takes the built-in Java DOM parsers to parse equivalent XML. + * <p> + * This parser handles all valid JSON syntax. + * In addition, when strict mode is disable, the parser also handles the following: + * <ul> + * <li> Javascript comments (both {@code /*} and {@code //}) are ignored. + * <li> Both single and double quoted strings. + * <li> Automatically joins concatenated strings (e.g. <code><js>"aaa"</js> + <js>'bbb'</js></code>). + * <li> Unquoted attributes. + * </ul> + * Also handles negative, decimal, hexadecimal, octal, and double numbers, including exponential notation. + * <p> + * This parser handles the following input, and automatically returns the corresponding Java class. + * <ul> + * <li> JSON objects (<js>"{...}"</js>) are converted to {@link ObjectMap ObjectMaps}. <br> + * Note: If a <code><xa>_class</xa>=<xs>'xxx'</xs></code> attribute is specified on the object, then an attempt is made to convert the object + * to an instance of the specified Java bean class. See the classProperty setting on the {@link BeanContextFactory} for more information + * about parsing beans from JSON. + * <li> JSON arrays (<js>"[...]"</js>) are converted to {@link ObjectList ObjectLists}. + * <li> JSON string literals (<js>"'xyz'"</js>) are converted to {@link String Strings}. + * <li> JSON numbers (<js>"123"</js>, including octal/hexadecimal/exponential notation) are converted to {@link Integer Integers}, + * {@link Long Longs}, {@link Float Floats}, or {@link Double Doubles} depending on whether the number is decimal, and the size of the number. + * <li> JSON booleans (<js>"false"</js>) are converted to {@link Boolean Booleans}. + * <li> JSON nulls (<js>"null"</js>) are converted to <jk>null</jk>. + * <li> Input consisting of only whitespace or JSON comments are converted to <jk>null</jk>. + * </ul> + * <p> + * Input can be any of the following:<br> + * <ul> + * <li> <js>"{...}"</js> - Converted to a {@link ObjectMap} or an instance of a Java bean if a <xa>_class</xa> attribute is present. + * <li> <js>"[...]"</js> - Converted to a {@link ObjectList}. + * <li> <js>"123..."</js> - Converted to a {@link Number} (either {@link Integer}, {@link Long}, {@link Float}, or {@link Double}). + * <li> <js>"true"</js>/<js>"false"</js> - Converted to a {@link Boolean}. + * <li> <js>"null"</js> - Returns <jk>null</jk>. + * <li> <js>"'xxx'"</js> - Converted to a {@link String}. + * <li> <js>"\"xxx\""</js> - Converted to a {@link String}. + * <li> <js>"'xxx' + \"yyy\""</js> - Converted to a concatenated {@link String}. + * </ul> + * <p> + * TIP: If you know you're parsing a JSON object or array, it can be easier to parse it using the {@link ObjectMap#ObjectMap(CharSequence) ObjectMap(CharSequence)} + * or {@link ObjectList#ObjectList(CharSequence) ObjectList(CharSequence)} constructors instead of using this class. The end result should be the same. + * + * + * <h6 class='topic'>Configurable properties</h6> + * <p> + * This class has the following properties associated with it: + * <ul> + * <li>{@link ParserProperties} + * <li>{@link BeanContextProperties} + * </ul> + * + * + * @author James Bognar ([email protected]) + */ +@SuppressWarnings({ "rawtypes", "unchecked" }) +@Consumes({"application/json","text/json"}) +public final class JsonParser extends ReaderParser { + + /** Default parser, all default settings.*/ + public static final JsonParser DEFAULT = new JsonParser().lock(); + + /** Default parser, all default settings.*/ + public static final JsonParser DEFAULT_STRICT = new JsonParser().setProperty(JSON_strictMode, true).lock(); + + /** JSON specific properties currently defined on this class */ + protected transient JsonParserProperties jpp = new JsonParserProperties(); + + private <T> T parseAnything(JsonParserContext ctx, ClassMeta<T> nt, ParserReader r, BeanPropertyMeta p, Object outer, Object name) throws ParseException { + + BeanContext bc = ctx.getBeanContext(); + if (nt == null) + nt = (ClassMeta<T>)object(); + PojoFilter<T,Object> filter = (PojoFilter<T,Object>)nt.getPojoFilter(); + ClassMeta<?> ft = nt.getFilteredClassMeta(); + String wrapperAttr = ft.getJsonMeta().getWrapperAttr(); + + int line = r.getLine(); + int column = r.getColumn(); + Object o = null; + try { + skipCommentsAndSpace(ctx, r); + if (wrapperAttr != null) + skipWrapperAttrStart(ctx, r, wrapperAttr); + int c = r.peek(); + if (c == -1) { + // Let o be null. + } else if ((c == ',' || c == '}' || c == ']')) { + if (ctx.isStrictMode()) + throw new ParseException(line, column, "Missing value detected."); + // Handle bug in Cognos 10.2.1 that can product non-existent values. + // Let o be null; + } else if (c == 'n') { + parseKeyword("null", r); + } else if (ft.isObject()) { + if (c == '{') { + ObjectMap m2 = new ObjectMap(bc); + parseIntoMap2(ctx, r, m2, string(), object()); + o = m2.cast(); + } else if (c == '[') + o = parseIntoCollection2(ctx, r, new ObjectList(bc), object()); + else if (c == '\'' || c == '"') { + o = parseString(ctx, r); + if (ft.isChar()) + o = o.toString().charAt(0); + } + else if (c >= '0' && c <= '9' || c == '-') + o = parseNumber(ctx, r, null); + else if (c == 't') { + parseKeyword("true", r); + o = Boolean.TRUE; + } else { + parseKeyword("false", r); + o = Boolean.FALSE; + } + } else if (ft.isBoolean()) { + o = parseBoolean(ctx, r); + } else if (ft.isCharSequence()) { + o = parseString(ctx, r); + } else if (ft.isChar()) { + o = parseString(ctx, r).charAt(0); + } else if (ft.isNumber()) { + o = parseNumber(ctx, r, (Class<? extends Number>)ft.getInnerClass()); + } else if (ft.isMap()) { + Map m = (ft.canCreateNewInstance(outer) ? (Map)ft.newInstance(outer) : new ObjectMap(bc)); + o = parseIntoMap2(ctx, r, m, ft.getKeyType(), ft.getValueType()); + } else if (ft.isCollection()) { + if (c == '{') { + ObjectMap m = new ObjectMap(bc); + parseIntoMap2(ctx, r, m, string(), object()); + o = m.cast(); + } else { + Collection l = (ft.canCreateNewInstance(outer) ? (Collection)ft.newInstance() : new ObjectList(bc)); + o = parseIntoCollection2(ctx, r, l, ft.getElementType()); + } + } else if (ft.canCreateNewInstanceFromObjectMap(outer)) { + ObjectMap m = new ObjectMap(bc); + parseIntoMap2(ctx, r, m, string(), object()); + o = ft.newInstanceFromObjectMap(outer, m); + } else if (ft.canCreateNewBean(outer)) { + BeanMap m = bc.newBeanMap(outer, ft.getInnerClass()); + o = parseIntoBeanMap2(ctx, r, m).getBean(); + } else if (ft.canCreateNewInstanceFromString(outer) && (c == '\'' || c == '"')) { + o = ft.newInstanceFromString(outer, parseString(ctx, r)); + } else if (ft.isArray()) { + if (c == '{') { + ObjectMap m = new ObjectMap(bc); + parseIntoMap2(ctx, r, m, string(), object()); + o = m.cast(); + } else { + ArrayList l = (ArrayList)parseIntoCollection2(ctx, r, new ArrayList(), ft.getElementType()); + o = bc.toArray(ft, l); + } + } else if (c == '{' ){ + Map m = new ObjectMap(bc); + parseIntoMap2(ctx, r, m, ft.getKeyType(), ft.getValueType()); + if (m.containsKey("_class")) + o = ((ObjectMap)m).cast(); + else + throw new ParseException(line, column, "Class ''{0}'' could not be instantiated. Reason: ''{1}''", ft.getInnerClass().getName(), ft.getNotABeanReason()); + } else if (ft.canCreateNewInstanceFromString(outer) && ! ctx.isStrictMode()) { + o = ft.newInstanceFromString(outer, parseString(ctx, r)); + } else { + throw new ParseException(line, column, "Unrecognized syntax for class type ''{0}'', starting character ''{1}''", ft, (char)c); + } + + if (wrapperAttr != null) + skipWrapperAttrEnd(ctx, r); + + if (filter != null && o != null) + o = filter.unfilter(o, nt); + + if (outer != null) + setParent(nt, o, outer); + + if (name != null) + setName(nt, o, name); + + return (T)o; + + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + if (p == null) + throw new ParseException("Error occurred trying to parse into class ''{0}''", ft).initCause(e); + throw new ParseException("Error occurred trying to parse value for bean property ''{0}'' on class ''{1}''", + p.getName(), p.getBeanMeta().getClassMeta() + ).initCause(e); + } + } + + private Number parseNumber(JsonParserContext ctx, ParserReader r, Class<? extends Number> type) throws IOException, ParseException { + int c = r.peek(); + if (c == '\'' || c == '"') + return parseNumber(ctx, parseString(ctx, r), type); + return parseNumber(ctx, StringUtils.parseNumberString(r), type); + } + + private Number parseNumber(JsonParserContext ctx, String s, Class<? extends Number> type) throws ParseException { + if (ctx.isStrictMode()) { + // Need to weed out octal and hexadecimal formats: 0123,-0123,0x123,-0x123. + // Don't weed out 0 or -0. + // All other number formats are supported in JSON. + boolean isNegative = false; + char c = (s.length() == 0 ? 'x' : s.charAt(0)); + if (c == '-') { + isNegative = true; + c = (s.length() == 1 ? 'x' : s.charAt(1)); + } + if (c == 'x' || (c == '0' && s.length() > (isNegative ? 2 : 1))) + throw new NumberFormatException("Invalid JSON number '"+s+"'"); + } + return StringUtils.parseNumber(s, type); + } + + private Boolean parseBoolean(JsonParserContext ctx, ParserReader r) throws IOException, ParseException { + int c = r.peek(); + if (c == '\'' || c == '"') + return Boolean.valueOf(parseString(ctx, r)); + if (c == 't') { + parseKeyword("true", r); + return Boolean.TRUE; + } + parseKeyword("false", r); + return Boolean.FALSE; + } + + + private <K,V> Map<K,V> parseIntoMap2(JsonParserContext ctx, ParserReader r, Map<K,V> m, ClassMeta<K> keyType, ClassMeta<V> valueType) throws ParseException, IOException { + int line = r.getLine(); + int column = r.getColumn(); + + if (keyType == null) + keyType = (ClassMeta<K>)string(); + + int S0=0; // Looking for outer { + int S1=1; // Looking for attrName start. + int S3=3; // Found attrName end, looking for :. + int S4=4; // Found :, looking for valStart: { [ " ' LITERAL. + int S5=5; // Looking for , or } + + int state = S0; + String currAttr = null; + int c = 0; + while (c != -1) { + c = r.read(); + if (state == S0) { + if (c == '{') + state = S1; + } else if (state == S1) { + if (c == '}') { + return m; + } else if (c == '/') { + skipCommentsAndSpace(ctx, r.unread()); + } else if (! Character.isWhitespace(c)) { + currAttr = parseFieldName(ctx, r.unread()); + state = S3; + } + } else if (state == S3) { + if (c == ':') + state = S4; + } else if (state == S4) { + if (c == '/') { + skipCommentsAndSpace(ctx, r.unread()); + } else if (! Character.isWhitespace(c)) { + K key = convertAttrToType(m, currAttr, keyType); + V value = parseAnything(ctx, valueType, r.unread(), null, m, key); + m.put(key, value); + state = S5; + } + } else if (state == S5) { + if (c == ',') + state = S1; + else if (c == '/') + skipCommentsAndSpace(ctx, r.unread()); + else if (c == '}') { + return m; + } + } + } + if (state == S0) + throw new ParseException(line, column, "Expected '{' at beginning of JSON object."); + if (state == S1) + throw new ParseException(line, column, "Could not find attribute name on JSON object."); + if (state == S3) + throw new ParseException(line, column, "Could not find ':' following attribute name on JSON object."); + if (state == S4) + throw new ParseException(line, column, "Expected one of the following characters: {,[,',\",LITERAL."); + if (state == S5) + throw new ParseException(line, column, "Could not find '}' marking end of JSON object."); + + return null; // Unreachable. + } + + /* + * Parse a JSON attribute from the character array at the specified position, then + * set the position marker to the last character in the field name. + */ + private String parseFieldName(JsonParserContext ctx, ParserReader r) throws ParseException, IOException { + int line = r.getLine(); + int column = r.getColumn(); + int c = r.peek(); + if (c == '\'' || c == '"') + return parseString(ctx, r); + if (ctx.isStrictMode()) + throw new ParseException(line, column, "Unquoted attribute detected."); + r.mark(); + // Look for whitespace. + while (c != -1) { + c = r.read(); + if (c == ':' || Character.isWhitespace(c) || c == '/') { + r.unread(); + String s = r.getMarked().intern(); + return s.equals("null") ? null : s; + } + } + throw new ParseException(line, column, "Could not find the end of the field name."); + } + + private <E> Collection<E> parseIntoCollection2(JsonParserContext ctx, ParserReader r, Collection<E> l, ClassMeta<E> elementType) throws ParseException, IOException { + int line = r.getLine(); + int column = r.getColumn(); + + int S0=0; // Looking for outermost [ + int S1=1; // Looking for starting [ or { or " or ' or LITERAL + int S2=2; // Looking for , or ] + + int state = S0; + int c = 0; + while (c != -1) { + c = r.read(); + if (state == S0) { + if (c == '[') + state = S1; + } else if (state == S1) { + if (c == ']') { + return l; + } else if (c == '/') { + skipCommentsAndSpace(ctx, r.unread()); + } else if (! Character.isWhitespace(c)) { + l.add(parseAnything(ctx, elementType, r.unread(), null, l, null)); + state = S2; + } + } else if (state == S2) { + if (c == ',') { + state = S1; + } else if (c == '/') { + skipCommentsAndSpace(ctx, r.unread()); + } else if (c == ']') { + return l; + } + } + } + if (state == S0) + throw new ParseException(line, column, "Expected '[' at beginning of JSON array."); + if (state == S1) + throw new ParseException(line, column, "Expected one of the following characters: {,[,',\",LITERAL."); + if (state == S2) + throw new ParseException(line, column, "Expected ',' or ']'."); + + return null; // Unreachable. + } + + private Object[] parseArgs(JsonParserContext ctx, ParserReader r, ClassMeta<?>[] argTypes) throws ParseException, IOException { + int line = r.getLine(); + int column = r.getColumn(); + + int S0=0; // Looking for outermost [ + int S1=1; // Looking for starting [ or { or " or ' or LITERAL + int S2=2; // Looking for , or ] + + Object[] o = new Object[argTypes.length]; + int i = 0; + + int state = S0; + int c = 0; + while (c != -1) { + c = r.read(); + if (state == S0) { + if (c == '[') + state = S1; + } else if (state == S1) { + if (c == ']') { + return o; + } else if (c == '/') { + skipCommentsAndSpace(ctx, r.unread()); + } else if (! Character.isWhitespace(c)) { + o[i] = parseAnything(ctx, argTypes[i], r.unread(), null, ctx.getOuter(), null); + i++; + state = S2; + } + } else if (state == S2) { + if (c == ',') { + state = S1; + } else if (c == '/') { + skipCommentsAndSpace(ctx, r.unread()); + } else if (c == ']') { + return o; + } + } + } + if (state == S0) + throw new ParseException(line, column, "Expected '[' at beginning of JSON array."); + if (state == S1) + throw new ParseException(line, column, "Expected one of the following characters: {,[,',\",LITERAL."); + if (state == S2) + throw new ParseException(line, column, "Expected ',' or ']'."); + + return null; // Unreachable. + } + + private <T> BeanMap<T> parseIntoBeanMap2(JsonParserContext ctx, ParserReader r, BeanMap<T> m) throws ParseException, IOException { + int line = r.getLine(); + int column = r.getColumn(); + + int S0=0; // Looking for outer { + int S1=1; // Looking for attrName start. + int S3=3; // Found attrName end, looking for :. + int S4=4; // Found :, looking for valStart: { [ " ' LITERAL. + int S5=5; // Looking for , or } + + int state = S0; + String currAttr = ""; + int c = 0; + int currAttrLine = -1, currAttrCol = -1; + while (c != -1) { + c = r.read(); + if (state == S0) { + if (c == '{') + state = S1; + } else if (state == S1) { + if (c == '}') { + return m; + } else if (c == '/') { + skipCommentsAndSpace(ctx, r.unread()); + } else if (! Character.isWhitespace(c)) { + r.unread(); + currAttrLine= r.getLine(); + currAttrCol = r.getColumn(); + currAttr = parseFieldName(ctx, r); + state = S3; + } + } else if (state == S3) { + if (c == ':') + state = S4; + } else if (state == S4) { + if (c == '/') { + skipCommentsAndSpace(ctx, r.unread()); + } else if (! Character.isWhitespace(c)) { + if (! currAttr.equals("_class")) { + BeanPropertyMeta pMeta = m.getPropertyMeta(currAttr); + if (pMeta == null) { + if (m.getMeta().isSubTyped()) { + m.put(currAttr, parseAnything(ctx, object(), r.unread(), null, m.getBean(false), currAttr)); + } else { + onUnknownProperty(ctx, currAttr, m, currAttrLine, currAttrCol); + parseAnything(ctx, object(), r.unread(), null, m.getBean(false), null); // Read content anyway to ignore it + } + } else { + Object value = parseAnything(ctx, pMeta.getClassMeta(), r.unread(), pMeta, m.getBean(false), currAttr); + pMeta.set(m, value); + } + } + state = S5; + } + } else if (state == S5) { + if (c == ',') + state = S1; + else if (c == '/') + skipCommentsAndSpace(ctx, r.unread()); + else if (c == '}') { + return m; + } + } + } + if (state == S0) + throw new ParseException(line, column, "Expected '{' at beginning of JSON object."); + if (state == S1) + throw new ParseException(line, column, "Could not find attribute name on JSON object."); + if (state == S3) + throw new ParseException(line, column, "Could not find ':' following attribute name on JSON object."); + if (state == S4) + throw new ParseException(line, column, "Expected one of the following characters: {,[,',\",LITERAL."); + if (state == S5) + throw new ParseException(line, column, "Could not find '}' marking end of JSON object."); + + return null; // Unreachable. + } + + /* + * Starting from the specified position in the character array, returns the + * position of the character " or '. + * If the string consists of a concatenation of strings (e.g. 'AAA' + "BBB"), this method + * will automatically concatenate the strings and return the result. + */ + private String parseString(JsonParserContext ctx, ParserReader r) throws ParseException, IOException { + int line = r.getLine(); + int column = r.getColumn(); + r.mark(); + int qc = r.read(); // The quote character being used (" or ') + if (qc != '"' && ctx.isStrictMode()) { + String msg = (qc == '\'' ? "Invalid quote character \"{0}\" being used." : "Did not find quote character marking beginning of string. Character=\"{0}\""); + throw new ParseException(line, column, msg, (char)qc); + } + final boolean isQuoted = (qc == '\'' || qc == '"'); + String s = null; + boolean isInEscape = false; + int c = 0; + while (c != -1) { + c = r.read(); + if (isInEscape) { + switch (c) { + case 'n': r.replace('\n'); break; + case 'r': r.replace('\r'); break; + case 't': r.replace('\t'); break; + case 'f': r.replace('\f'); break; + case 'b': r.replace('\b'); break; + case '\\': r.replace('\\'); break; + case '/': r.replace('/'); break; + case '\'': r.replace('\''); break; + case '"': r.replace('"'); break; + case 'u': { + String n = r.read(4); + r.replace(Integer.parseInt(n, 16), 6); + break; + } + default: + throw new ParseException(line, column, "Invalid escape sequence in string."); + } + isInEscape = false; + } else { + if (c == '\\') { + isInEscape = true; + r.delete(); + } else if (isQuoted) { + if (c == qc) { + s = r.getMarked(1, -1); + break; + } + } else { + if (c == ',' || c == '}' || Character.isWhitespace(c)) { + s = r.getMarked(0, -1); + r.unread(); + break; + } else if (c == -1) { + s = r.getMarked(0, 0); + break; + } + } + } + } + if (s == null) + throw new ParseException(line, column, "Could not find expected end character ''{0}''.", (char)qc); + + // Look for concatenated string (i.e. whitespace followed by +). + skipCommentsAndSpace(ctx, r); + if (r.peek() == '+') { + if (ctx.isStrictMode()) + throw new ParseException(r.getLine(), r.getColumn(), "String concatenation detected."); + r.read(); // Skip past '+' + skipCommentsAndSpace(ctx, r); + s += parseString(ctx, r); + } + return s; // End of input reached. + } + + /* + * Looks for the keywords true, false, or null. + * Throws an exception if any of these keywords are not found at the specified position. + */ + private void parseKeyword(String keyword, ParserReader r) throws ParseException, IOException { + int line = r.getLine(); + int column = r.getColumn(); + try { + String s = r.read(keyword.length()); + if (s.equals(keyword)) + return; + throw new ParseException(line, column, "Unrecognized syntax."); + } catch (IndexOutOfBoundsException e) { + throw new ParseException(line, column, "Unrecognized syntax."); + } + } + + /* + * Doesn't actually parse anything, but moves the position beyond any whitespace or comments. + * If positionOnNext is 'true', then the cursor will be set to the point immediately after + * the comments and whitespace. Otherwise, the cursor will be set to the last position of + * the comments and whitespace. + */ + private void skipCommentsAndSpace(JsonParserContext ctx, ParserReader r) throws ParseException, IOException { + int c = 0; + while ((c = r.read()) != -1) { + if (! Character.isWhitespace(c)) { + if (c == '/') { + if (ctx.isStrictMode()) + throw new ParseException(r.getLine(), r.getColumn(), "Javascript comment detected."); + skipComments(r); + } else { + r.unread(); + return; + } + } + } + } + + /* + * Doesn't actually parse anything, but moves the position beyond the construct "{wrapperAttr:" when + * the @Json.wrapperAttr() annotation is used on a class. + */ + private void skipWrapperAttrStart(JsonParserContext ctx, ParserReader r, String wrapperAttr) throws ParseException, IOException { + int line = r.getLine(); + int column = r.getColumn(); + + int S0=0; // Looking for outer { + int S1=1; // Looking for attrName start. + int S3=3; // Found attrName end, looking for :. + int S4=4; // Found :, looking for valStart: { [ " ' LITERAL. + + int state = S0; + String currAttr = null; + int c = 0; + while (c != -1) { + c = r.read(); + if (state == S0) { + if (c == '{') + state = S1; + } else if (state == S1) { + if (c == '/') { + skipCommentsAndSpace(ctx, r.unread()); + } else if (! Character.isWhitespace(c)) { + currAttr = parseFieldName(ctx, r.unread()); + if (! currAttr.equals(wrapperAttr)) + throw new ParseException(line, column, "Expected to find wrapper attribute ''{0}'' but found attribute ''{1}''", wrapperAttr, currAttr); + state = S3; + } + } else if (state == S3) { + if (c == ':') + state = S4; + } else if (state == S4) { + if (c == '/') { + skipCommentsAndSpace(ctx, r.unread()); + } else if (! Character.isWhitespace(c)) { + r.unread(); + return; + } + } + } + if (state == S0) + throw new ParseException(line, column, "Expected '{' at beginning of JSON object."); + if (state == S1) + throw new ParseException(line, column, "Could not find attribute name on JSON object."); + if (state == S3) + throw new ParseException(line, column, "Could not find ':' following attribute name on JSON object."); + if (state == S4) + throw new ParseException(line, column, "Expected one of the following characters: {,[,',\",LITERAL."); + } + + /* + * Doesn't actually parse anything, but moves the position beyond the construct "}" when + * the @Json.wrapperAttr() annotation is used on a class. + */ + private void skipWrapperAttrEnd(JsonParserContext ctx, ParserReader r) throws ParseException, IOException { + int c = 0; + int line = r.getLine(); + int column = r.getColumn(); + while ((c = r.read()) != -1) { + if (! Character.isWhitespace(c)) { + if (c == '/') { + if (ctx.isStrictMode()) + throw new ParseException(line, column, "Javascript comment detected."); + skipComments(r); + } else if (c == '}') { + return; + } else { + throw new ParseException(line, column, "Could not find '}' at the end of JSON wrapper object."); + } + } + } + } + + /* + * Doesn't actually parse anything, but when positioned at the beginning of comment, + * it will move the pointer to the last character in the comment. + */ + private void skipComments(ParserReader r) throws ParseException, IOException { + int line = r.getLine(); + int column = r.getColumn(); + int c = r.read(); + // "/* */" style comments + if (c == '*') { + while (c != -1) + if ((c = r.read()) == '*') + if ((c = r.read()) == '/') + return; + // "//" style comments + } else if (c == '/') { + while (c != -1) { + c = r.read(); + if (c == -1 || c == '\n') + return; + } + } + throw new ParseException(line, column, "Open ended comment."); + } + + /* + * Call this method after you've finished a parsing a string to make sure that if there's any + * remainder in the input, that it consists only of whitespace and comments. + */ + private void validateEnd(JsonParserContext ctx, ParserReader r) throws ParseException, IOException { + skipCommentsAndSpace(ctx, r); + int line = r.getLine(); + int column = r.getColumn(); + int c = r.read(); + if (c != -1 && c != ';') // var x = {...}; expressions can end with a semicolon. + throw new ParseException(line, column, "Remainder after parse: ''{0}''.", (char)c); + } + + + //-------------------------------------------------------------------------------- + // Overridden methods + //-------------------------------------------------------------------------------- + + @Override /* Parser */ + public JsonParserContext createContext(ObjectMap op, Method javaMethod, Object outer) { + return new JsonParserContext(getBeanContext(), jpp, pp, op, javaMethod, outer); + } + + @Override /* Parser */ + protected <T> T doParse(Reader in, int estimatedSize, ClassMeta<T> type, ParserContext ctx) throws ParseException, IOException { + JsonParserContext jctx = (JsonParserContext)ctx; + type = ctx.getBeanContext().normalizeClassMeta(type); + ParserReader r = jctx.getReader(in, estimatedSize); + T o = parseAnything(jctx, type, r, null, ctx.getOuter(), null); + validateEnd(jctx, r); + return o; + } + + @Override /* ReaderParser */ + protected <K,V> Map<K,V> doParseIntoMap(Reader in, int estimatedSize, Map<K,V> m, Type keyType, Type valueType, ParserContext ctx) throws ParseException, IOException { + JsonParserContext jctx = (JsonParserContext)ctx; + ParserReader r = jctx.getReader(in, estimatedSize); + m = parseIntoMap2(jctx, r, m, ctx.getBeanContext().getClassMeta(keyType), ctx.getBeanContext().getClassMeta(valueType)); + validateEnd(jctx, r); + return m; + } + + @Override /* ReaderParser */ + protected <E> Collection<E> doParseIntoCollection(Reader in, int estimatedSize, Collection<E> c, Type elementType, ParserContext ctx) throws ParseException, IOException { + JsonParserContext jctx = (JsonParserContext)ctx; + ParserReader r = jctx.getReader(in, estimatedSize); + c = parseIntoCollection2(jctx, r, c, ctx.getBeanContext().getClassMeta(elementType)); + validateEnd(jctx, r); + return c; + } + + @Override /* ReaderParser */ + protected Object[] doParseArgs(Reader in, int estimatedSize, ClassMeta<?>[] argTypes, ParserContext ctx) throws ParseException, IOException { + JsonParserContext jctx = (JsonParserContext)ctx; + ParserReader r = jctx.getReader(in, estimatedSize); + Object[] a = parseArgs(jctx, r, argTypes); + validateEnd(jctx, r); + return a; + } + + @Override /* Parser */ + public JsonParser setProperty(String property, Object value) throws LockedException { + checkLock(); + if (! jpp.setProperty(property, value)) + super.setProperty(property, value); + return this; + } + + @Override /* CoreApi */ + public JsonParser setProperties(ObjectMap properties) throws LockedException { + for (Map.Entry<String,Object> e : properties.entrySet()) + setProperty(e.getKey(), e.getValue()); + return this; + } + + @Override /* CoreApi */ + public JsonParser addNotBeanClasses(Class<?>...classes) throws LockedException { + super.addNotBeanClasses(classes); + return this; + } + + @Override /* CoreApi */ + public JsonParser addFilters(Class<?>...classes) throws LockedException { + super.addFilters(classes); + return this; + } + + @Override /* CoreApi */ + public <T> JsonParser addImplClass(Class<T> interfaceClass, Class<? extends T> implClass) throws LockedException { + super.addImplClass(interfaceClass, implClass); + return this; + } + + @Override /* CoreApi */ + public JsonParser setClassLoader(ClassLoader classLoader) throws LockedException { + super.setClassLoader(classLoader); + return this; + } + + @Override /* Lockable */ + public JsonParser lock() { + super.lock(); + return this; + } + + @Override /* Lockable */ + public JsonParser clone() { + try { + return (JsonParser)super.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); // Shouldn't happen + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonParserContext.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonParserContext.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonParserContext.class new file mode 100755 index 0000000..b0e2e01 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonParserContext.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonParserContext.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonParserContext.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonParserContext.java new file mode 100755 index 0000000..325169d --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonParserContext.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.json; + +import static com.ibm.juno.core.json.JsonParserProperties.*; + +import java.io.*; +import java.lang.reflect.*; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.parser.*; + +/** + * Context object that lives for the duration of a single parsing of {@link JsonParser}. + * <p> + * + * @author James Bognar ([email protected]) + */ +public final class JsonParserContext extends ParserContext { + + private final boolean strictMode; + + /** + * Create a new parser context with the specified options. + * + * @param beanContext The bean context being used. + * @param jpp The JSON parser properties. + * @param pp The default parser properties. + * @param op The override properties. + * @param javaMethod The java method that called this parser, usually the method in a REST servlet. + * @param outer The outer object for instantiating top-level non-static inner classes. + */ + public JsonParserContext(BeanContext beanContext, JsonParserProperties jpp, ParserProperties pp, ObjectMap op, Method javaMethod, Object outer) { + super(beanContext, pp, op, javaMethod, outer); + if (op == null || op.isEmpty()) { + strictMode = jpp.isStrictMode(); + } else { + strictMode = op.getBoolean(JSON_strictMode, jpp.isStrictMode()); + } + } + + /** + * Returns the {@link JsonParserProperties#JSON_strictMode} setting in this context. + * + * @return The {@link JsonParserProperties#JSON_strictMode} setting in this context. + */ + public boolean isStrictMode() { + return strictMode; + } + + /** + * Returns the reader associated with this context wrapped in a {@link ParserReader}. + * + * @param in The reader being wrapped. + * @param estimatedSize The estimated size of the input. + * @return The reader wrapped in a specialized parser reader. + */ + public ParserReader getReader(Reader in, int estimatedSize) { + if (in instanceof ParserReader) + return (ParserReader)in; + return new ParserReader(in, Math.min(8096, estimatedSize)); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonParserProperties.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonParserProperties.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonParserProperties.class new file mode 100755 index 0000000..869515e Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonParserProperties.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonParserProperties.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonParserProperties.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonParserProperties.java new file mode 100755 index 0000000..ac1f632 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonParserProperties.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.json; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.parser.*; + +/** + * Configurable properties on the {@link JsonParser} class. + * <p> + * Use the {@link JsonParser#setProperty(String, Object)} method to set property values. + * <p> + * In addition to these properties, the following properties are also applicable for {@link JsonParser}. + * <ul> + * <li>{@link ParserProperties} + * <li>{@link BeanContextProperties} + * </ul> + * + * @author James Bognar ([email protected]) + */ +public final class JsonParserProperties implements Cloneable { + + /** + * Set strict mode ({@link Boolean}, default=<jk>false</jk>). + * <p> + * When in strict mode, parser throws exceptions on the following invalid JSON syntax: + * <ul> + * <li>Unquoted attributes. + * <li>Missing attribute values. + * <li>Concatenated strings. + * <li>Javascript comments. + * <li>Numbers and booleans when Strings are expected. + * </ul> + */ + public static final String JSON_strictMode = "JsonParser.strictMode"; + + private boolean + strictMode = false; + + /** + * Sets the specified property value. + * + * @param property The property name. + * @param value The property value. + * @return <jk>true</jk> if property name was valid and property was set. + * @throws LockedException If the bean context has been locked. + */ + protected boolean setProperty(String property, Object value) throws LockedException { + BeanContext bc = BeanContext.DEFAULT; + if (property.equals(JSON_strictMode)) + strictMode = bc.convertToType(value, Boolean.class); + else + return false; + return true; + } + + /** + * Returns the current {@link #JSON_strictMode} value. + * + * @return The current {@link #JSON_strictMode} value. + */ + public boolean isStrictMode() { + return strictMode; + } + + + //-------------------------------------------------------------------------------- + // Overridden methods + //-------------------------------------------------------------------------------- + + @Override /* Object */ + public JsonParserProperties clone() { + try { + return (JsonParserProperties)super.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); // Shouldn't happen. + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSchemaSerializer.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSchemaSerializer.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSchemaSerializer.class new file mode 100755 index 0000000..229102b Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSchemaSerializer.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSchemaSerializer.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSchemaSerializer.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSchemaSerializer.java new file mode 100755 index 0000000..fa0dae5 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSchemaSerializer.java @@ -0,0 +1,154 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2011, 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.json; + +import static com.ibm.juno.core.serializer.SerializerProperties.*; +import static com.ibm.juno.core.utils.ClassUtils.*; + +import java.io.*; +import java.util.*; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.annotation.*; +import com.ibm.juno.core.filter.*; +import com.ibm.juno.core.serializer.*; + +/** + * Serializes POJO metadata to HTTP responses as JSON. + * + * + * <h6 class='topic'>Media types</h6> + * <p> + * Handles <code>Accept</code> types: <code>application/json+schema, text/json+schema</code> + * <p> + * Produces <code>Content-Type</code> types: <code>application/json</code> + * + * + * <h6 class='topic'>Description</h6> + * <p> + * Produces the JSON-schema for the JSON produced by the {@link JsonSerializer} class with the same properties. + * + * + * @author James Bognar ([email protected]) + */ +@Produces(value={"application/json+schema","text/json+schema"},contentType="application/json") +public final class JsonSchemaSerializer extends JsonSerializer { + + /** + * Constructor. + */ + public JsonSchemaSerializer() { + setProperty(SERIALIZER_detectRecursions, true); + setProperty(SERIALIZER_ignoreRecursions, true); + } + + //-------------------------------------------------------------------------------- + // Overridden methods + //-------------------------------------------------------------------------------- + + @Override /* JsonSerializer */ + protected void doSerialize(Object o, Writer out, SerializerContext ctx) throws IOException, SerializeException { + JsonSerializerContext jctx = (JsonSerializerContext)ctx; + ObjectMap schema = getSchema(ctx.getBeanContext().getClassMetaForObject(o), jctx, "root", null); + serializeAnything(jctx.getWriter(out), schema, null, jctx, "root", null); + } + + /* + * Creates a schema representation of the specified class type. + * + * @param eType The class type to get the schema of. + * @param ctx Serialize context used to prevent infinite loops. + * @param attrName The name of the current attribute. + * @return A schema representation of the specified class. + * @throws SerializeException If a problem occurred trying to convert the output. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + private ObjectMap getSchema(ClassMeta<?> eType, JsonSerializerContext ctx, String attrName, String[] pNames) throws SerializeException { + try { + + ObjectMap out = new ObjectMap(); + + if (eType == null) + eType = object(); + + ClassMeta<?> aType; // The actual type (will be null if recursion occurs) + ClassMeta<?> gType; // The generic type + + aType = ctx.push(attrName, eType, null); + + gType = eType.getFilteredClassMeta(); + String type = null; + + if (gType.isEnum() || gType.isCharSequence() || gType.isChar()) + type = "string"; + else if (gType.isNumber()) + type = "number"; + else if (gType.isBoolean()) + type = "boolean"; + else if (gType.isBean() || gType.isMap()) + type = "object"; + else if (gType.isCollection() || gType.isArray()) + type = "array"; + else + type = "any"; + + out.put("type", type); + out.put("description", eType.toString()); + PojoFilter f = eType.getPojoFilter(); + if (f != null) + out.put("filter", f); + + if (aType != null) { + if (gType.isEnum()) + out.put("enum", getEnumStrings((Class<Enum<?>>)gType.getInnerClass())); + else if (gType.isCollection() || gType.isArray()) { + ClassMeta componentType = gType.getElementType(); + if (gType.isCollection() && isParentClass(Set.class, gType.getInnerClass())) + out.put("uniqueItems", true); + out.put("items", getSchema(componentType, ctx, "items", pNames)); + } else if (gType.isBean()) { + ObjectMap properties = new ObjectMap(); + BeanMeta bm = ctx.getBeanContext().getBeanMeta(gType.getInnerClass()); + if (pNames != null) + bm = new BeanMetaFiltered(bm, pNames); + for (Iterator<BeanPropertyMeta<?>> i = bm.getPropertyMetas().iterator(); i.hasNext();) { + BeanPropertyMeta p = i.next(); + properties.put(p.getName(), getSchema(p.getClassMeta(), ctx, p.getName(), p.getProperties())); + } + out.put("properties", properties); + } + } + ctx.pop(); + return out; + } catch (StackOverflowError e) { + throw e; + } catch (Throwable e) { + throw new SerializeException("Exception occured trying to process object of type ''{0}''", eType).initCause(e); + } + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private List<String> getEnumStrings(Class<? extends Enum> c) { + List<String> l = new LinkedList<String>(); + try { + for (Object e : EnumSet.allOf(c)) + l.add(e.toString()); + } catch (Exception e) { + e.printStackTrace(); + } + return l; + } + + + @Override /* Lockable */ + public JsonSchemaSerializer lock() { + super.lock(); + return this; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializer$Readable.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializer$Readable.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializer$Readable.class new file mode 100755 index 0000000..5f79aa1 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializer$Readable.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializer$Simple.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializer$Simple.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializer$Simple.class new file mode 100755 index 0000000..6ac8d7f Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializer$Simple.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializer$SimpleReadable.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializer$SimpleReadable.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializer$SimpleReadable.class new file mode 100755 index 0000000..32b616d Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializer$SimpleReadable.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializer$SimpleReadableSafe.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializer$SimpleReadableSafe.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializer$SimpleReadableSafe.class new file mode 100755 index 0000000..10df6c7 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializer$SimpleReadableSafe.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializer.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializer.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializer.class new file mode 100755 index 0000000..c912a70 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializer.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializer.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializer.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializer.java new file mode 100755 index 0000000..275ea72 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializer.java @@ -0,0 +1,457 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2011, 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.json; + +import static com.ibm.juno.core.json.JsonSerializerProperties.*; +import static com.ibm.juno.core.serializer.SerializerProperties.*; + +import java.io.*; +import java.lang.reflect.*; +import java.util.*; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.annotation.*; +import com.ibm.juno.core.filter.*; +import com.ibm.juno.core.serializer.*; + +/** + * Serializes POJO models to JSON. + * + * + * <h6 class='topic'>Media types</h6> + * <p> + * Handles <code>Accept</code> types: <code>application/json, text/json</code> + * <p> + * Produces <code>Content-Type</code> types: <code>application/json</code> + * + * + * <h6 class='topic'>Description</h6> + * <p> + * The conversion is as follows... + * <ul> + * <li>Maps (e.g. {@link HashMap HashMaps}, {@link TreeMap TreeMaps}) are converted to JSON objects. + * <li>Collections (e.g. {@link HashSet HashSets}, {@link LinkedList LinkedLists}) and Java arrays are converted to JSON arrays. + * <li>{@link String Strings} are converted to JSON strings. + * <li>{@link Number Numbers} (e.g. {@link Integer}, {@link Long}, {@link Double}) are converted to JSON numbers. + * <li>{@link Boolean Booleans} are converted to JSON booleans. + * <li>{@code nulls} are converted to JSON nulls. + * <li>{@code arrays} are converted to JSON arrays. + * <li>{@code beans} are converted to JSON objects. + * </ul> + * <p> + * The types above are considered "JSON-primitive" object types. Any non-JSON-primitive object types are transformed + * into JSON-primitive object types through {@link com.ibm.juno.core.filter.Filter Filters} associated through the {@link BeanContextFactory#addFilters(Class...)} + * method. Several default filters are provided for transforming Dates, Enums, Iterators, etc... + * <p> + * This serializer provides several serialization options. Typically, one of the predefined DEFAULT serializers will be sufficient. + * However, custom serializers can be constructed to fine-tune behavior. + * + * + * <h6 class='topic'>Configurable properties</h6> + * <p> + * This class has the following properties associated with it: + * <ul> + * <li>{@link JsonSerializerProperties} + * <li>{@link SerializerProperties} + * <li>{@link BeanContextProperties} + * </ul> + * + * + * <h6 class='topic'>Behavior-specific subclasses</h6> + * <p> + * The following direct subclasses are provided for convenience: + * <ul> + * <li>{@link Simple} - Default serializer, single quotes, simple mode. + * <li>{@link SimpleReadable} - Default serializer, single quotes, simple mode, with whitespace. + * </ul> + * + * + * <h6 class='topic'>Examples</h6> + * <p class='bcode'> + * <jc>// Use one of the default serializers to serialize a POJO</jc> + * String json = JsonSerializer.<jsf>DEFAULT</jsf>.serialize(someObject); + * + * <jc>// Create a custom serializer for lax syntax using single quote characters</jc> + * JsonSerializer serializer = <jk>new</jk> JsonSerializer() + * .setProperty(JsonSerializerProperties.<jsf>JSON_simpleMode</jsf>, <jk>true</jk>) + * .setProperty(SerializerProperties.<jsf>SERIALIZER_quoteChar</jsf>, <js>'\''</js>); + * + * <jc>// Clone an existing serializer and modify it to use single-quotes</jc> + * JsonSerializer serializer = JsonSerializer.<jsf>DEFAULT</jsf>.clone() + * .setProperty(SerializerProperties.<jsf>SERIALIZER_quoteChar</jsf>, <js>'\''</js>); + * + * <jc>// Serialize a POJO to JSON</jc> + * String json = serializer.serialize(someObject); + * </p> + * + * + * @author James Bognar ([email protected]) + */ +@Produces({"application/json","text/json"}) +public class JsonSerializer extends WriterSerializer { + + /** Default serializer, all default settings.*/ + public static final JsonSerializer DEFAULT = new JsonSerializer().lock(); + + /** Default serializer, all default settings.*/ + public static final JsonSerializer DEFAULT_READABLE = new Readable().lock(); + + /** Default serializer, single quotes, simple mode. */ + public static final JsonSerializer DEFAULT_LAX = new Simple().lock(); + + /** Default serializer, single quotes, simple mode, with whitespace. */ + public static final JsonSerializer DEFAULT_LAX_READABLE = new SimpleReadable().lock(); + + /** + * Default serializer, single quotes, simple mode, with whitespace and recursion detection. + * Note that recursion detection introduces a small performance penalty. + */ + public static final JsonSerializer DEFAULT_LAX_READABLE_SAFE = new SimpleReadableSafe().lock(); + + /** Default serializer, with whitespace. */ + public static class Readable extends JsonSerializer { + /** Constructor */ + public Readable() { + setProperty(JSON_useWhitespace, true); + setProperty(SERIALIZER_useIndentation, true); + } + } + + /** Default serializer, single quotes, simple mode. */ + @Produces(value={"application/json+simple","text/json+simple"},contentType="application/json") + public static class Simple extends JsonSerializer { + /** Constructor */ + public Simple() { + setProperty(JSON_simpleMode, true); + setProperty(SERIALIZER_quoteChar, '\''); + } + } + + /** Default serializer, single quotes, simple mode, with whitespace. */ + public static class SimpleReadable extends Simple { + /** Constructor */ + public SimpleReadable() { + setProperty(JSON_useWhitespace, true); + setProperty(SERIALIZER_useIndentation, true); + } + } + + /** + * Default serializer, single quotes, simple mode, with whitespace and recursion detection. + * Note that recursion detection introduces a small performance penalty. + */ + public static class SimpleReadableSafe extends SimpleReadable { + /** Constructor */ + public SimpleReadableSafe() { + setProperty(SERIALIZER_detectRecursions, true); + } + } + + /** JSON serializer properties currently set on this serializer. */ + protected transient JsonSerializerProperties jsp = new JsonSerializerProperties(); + + + /** + * Workhorse method. Determines the type of object, and then calls the + * appropriate type-specific serialization method. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + SerializerWriter serializeAnything(JsonSerializerWriter out, Object o, ClassMeta<?> eType, JsonSerializerContext ctx, String attrName, BeanPropertyMeta pMeta) throws SerializeException { + try { + BeanContext bc = ctx.getBeanContext(); + + if (o == null) { + out.append("null"); + return out; + } + + if (eType == null) + eType = object(); + + boolean addClassAttr; // Add "_class" attribute to element? + ClassMeta<?> aType; // The actual type + ClassMeta<?> gType; // The generic type + + aType = ctx.push(attrName, o, eType); + boolean isRecursion = aType == null; + + // Handle recursion + if (aType == null) { + o = null; + aType = object(); + } + + gType = aType.getFilteredClassMeta(); + addClassAttr = (ctx.isAddClassAttrs() && ! eType.equals(aType)); + + // Filter if necessary + PojoFilter filter = aType.getPojoFilter(); // The filter + if (filter != null) { + o = filter.filter(o); + + // If the filter's getFilteredClass() method returns Object, we need to figure out + // the actual type now. + if (gType.isObject()) + gType = bc.getClassMetaForObject(o); + } + + String wrapperAttr = gType.getJsonMeta().getWrapperAttr(); + if (wrapperAttr != null) { + out.append('{').cr(ctx.indent).attr(wrapperAttr).append(':').s(); + ctx.indent++; + } + + // '\0' characters are considered null. + if (o == null || (gType.isChar() && ((Character)o).charValue() == 0)) + out.append("null"); + else if (gType.isNumber() || gType.isBoolean()) + out.append(o); + else if (gType.hasToObjectMapMethod()) + serializeMap(out, gType.toObjectMap(o), gType, ctx); + else if (gType.isBean()) + serializeBeanMap(out, bc.forBean(o), addClassAttr, ctx); + else if (gType.isUri() || (pMeta != null && (pMeta.isUri() || pMeta.isBeanUri()))) + out.q().appendUri(o).q(); + else if (gType.isMap()) { + if (o instanceof BeanMap) + serializeBeanMap(out, (BeanMap)o, addClassAttr, ctx); + else + serializeMap(out, (Map)o, eType, ctx); + } + else if (gType.isCollection()) { + if (addClassAttr) + serializeCollectionMap(out, (Collection)o, gType, ctx); + else + serializeCollection(out, (Collection) o, eType, ctx); + } + else if (gType.isArray()) { + if (addClassAttr) + serializeCollectionMap(out, toList(gType.getInnerClass(), o), gType, ctx); + else + serializeCollection(out, toList(gType.getInnerClass(), o), eType, ctx); + } + else + out.stringValue(o); + + if (wrapperAttr != null) { + ctx.indent--; + out.cr(ctx.indent-1).append('}'); + } + + if (! isRecursion) + ctx.pop(); + return out; + } catch (SerializeException e) { + throw e; + } catch (StackOverflowError e) { + throw e; + } catch (Throwable e) { + throw new SerializeException("Exception occured trying to process object of type ''{0}''", (o == null ? null : o.getClass().getName())).initCause(e); + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private SerializerWriter serializeMap(JsonSerializerWriter out, Map m, ClassMeta<?> type, JsonSerializerContext ctx) throws IOException, SerializeException { + + ClassMeta<?> keyType = type.getKeyType(), valueType = type.getValueType(); + + m = sort(ctx, m); + + int depth = ctx.getIndent(); + out.append('{'); + + Iterator mapEntries = m.entrySet().iterator(); + + while (mapEntries.hasNext()) { + Map.Entry e = (Map.Entry) mapEntries.next(); + Object value = e.getValue(); + + Object key = generalize(ctx, e.getKey(), keyType); + + out.cr(depth).attr(key).append(':').s(); + + serializeAnything(out, value, valueType, ctx, (key == null ? null : key.toString()), null); + + if (mapEntries.hasNext()) + out.append(',').s(); + } + + out.cr(depth-1).append('}'); + + return out; + } + + @SuppressWarnings({ "rawtypes" }) + private SerializerWriter serializeCollectionMap(JsonSerializerWriter out, Collection o, ClassMeta<?> type, JsonSerializerContext ctx) throws IOException, SerializeException { + int i = ctx.getIndent(); + out.append('{'); + out.cr(i).attr("_class").append(':').s().q().append(type.getInnerClass().getName()).q().append(',').s(); + out.cr(i).attr("items").append(':').s(); + ctx.indent++; + serializeCollection(out, o, type, ctx); + ctx.indent--; + out.cr(i-1).append('}'); + return out; + } + + @SuppressWarnings({ "rawtypes" }) + private SerializerWriter serializeBeanMap(JsonSerializerWriter out, BeanMap m, boolean addClassAttr, JsonSerializerContext ctx) throws IOException, SerializeException { + int depth = ctx.getIndent(); + out.append('{'); + + Iterator mapEntries = m.entrySet().iterator(); + + // Print out "_class" attribute on this bean if required. + if (addClassAttr) { + String attr = "_class"; + out.cr(depth).attr(attr).append(':').s().q().append(m.getClassMeta().getInnerClass().getName()).q(); + if (mapEntries.hasNext()) + out.append(',').s(); + } + + boolean addComma = false; + + while (mapEntries.hasNext()) { + BeanMapEntry p = (BeanMapEntry)mapEntries.next(); + BeanPropertyMeta pMeta = p.getMeta(); + + String key = p.getKey(); + Object value = null; + try { + value = p.getValue(); + } catch (StackOverflowError e) { + throw e; + } catch (Throwable t) { + ctx.addBeanGetterWarning(pMeta, t); + } + + if (canIgnoreValue(ctx, pMeta.getClassMeta(), key, value)) + continue; + + if (addComma) + out.append(',').s(); + + out.cr(depth).attr(key).append(':').s(); + + serializeAnything(out, value, pMeta.getClassMeta(), ctx, key, pMeta); + + addComma = true; + } + out.cr(depth-1).append('}'); + return out; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private SerializerWriter serializeCollection(JsonSerializerWriter out, Collection c, ClassMeta<?> type, JsonSerializerContext ctx) throws IOException, SerializeException { + + ClassMeta<?> elementType = type.getElementType(); + + c = sort(ctx, c); + + out.append('['); + int depth = ctx.getIndent(); + + for (Iterator i = c.iterator(); i.hasNext();) { + + Object value = i.next(); + + out.cr(depth); + + serializeAnything(out, value, elementType, ctx, "<iterator>", null); + + if (i.hasNext()) + out.append(',').s(); + } + out.cr(depth-1).append(']'); + return out; + } + + /** + * Returns the schema serializer based on the settings of this serializer. + * @return The schema serializer. + */ + public JsonSchemaSerializer getSchemaSerializer() { + JsonSchemaSerializer s = new JsonSchemaSerializer(); + s.beanContextFactory = this.beanContextFactory; + s.sp = this.sp; + s.jsp = this.jsp; + return s; + } + + //-------------------------------------------------------------------------------- + // Overridden methods + //-------------------------------------------------------------------------------- + + @Override /* Serializer */ + public JsonSerializerContext createContext(ObjectMap properties, Method javaMethod) { + return new JsonSerializerContext(getBeanContext(), sp, jsp, properties, javaMethod); + } + + @Override /* Serializer */ + protected void doSerialize(Object o, Writer out, SerializerContext ctx) throws IOException, SerializeException { + JsonSerializerContext jctx = (JsonSerializerContext)ctx; + serializeAnything(jctx.getWriter(out), o, null, jctx, "root", null); + } + + @Override /* CoreApi */ + public JsonSerializer setProperty(String property, Object value) throws LockedException { + checkLock(); + if (! jsp.setProperty(property, value)) + super.setProperty(property, value); + return this; + } + + @Override /* CoreApi */ + public JsonSerializer setProperties(ObjectMap properties) throws LockedException { + for (Map.Entry<String,Object> e : properties.entrySet()) + setProperty(e.getKey(), e.getValue()); + return this; + } + + @Override /* CoreApi */ + public JsonSerializer addNotBeanClasses(Class<?>...classes) throws LockedException { + super.addNotBeanClasses(classes); + return this; + } + + @Override /* CoreApi */ + public JsonSerializer addFilters(Class<?>...classes) throws LockedException { + super.addFilters(classes); + return this; + } + + @Override /* CoreApi */ + public <T> JsonSerializer addImplClass(Class<T> interfaceClass, Class<? extends T> implClass) throws LockedException { + super.addImplClass(interfaceClass, implClass); + return this; + } + + @Override /* CoreApi */ + public JsonSerializer setClassLoader(ClassLoader classLoader) throws LockedException { + super.setClassLoader(classLoader); + return this; + } + + @Override /* Lockable */ + public JsonSerializer lock() { + super.lock(); + return this; + } + + @Override /* Lockable */ + public JsonSerializer clone() { + try { + JsonSerializer c = (JsonSerializer)super.clone(); + c.jsp = jsp.clone(); + return c; + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); // Shouldn't happen + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializerContext.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializerContext.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializerContext.class new file mode 100755 index 0000000..27e8527 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializerContext.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializerContext.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializerContext.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializerContext.java new file mode 100755 index 0000000..0677055 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializerContext.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.json; + +import static com.ibm.juno.core.json.JsonSerializerProperties.*; + +import java.io.*; +import java.lang.reflect.*; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.serializer.*; + +/** + * Context object that lives for the duration of a single serialization of {@link JsonSerializer} and its subclasses. + * <p> + * See {@link SerializerContext} for details. + * + * @author James Bognar ([email protected]) + */ +public final class JsonSerializerContext extends SerializerContext { + + private final boolean simpleMode, useWhitespace, escapeSolidus; + + /** + * Constructor. + * @param beanContext The bean context being used by the serializer. + * @param sp Default general serializer properties. + * @param jsp Default JSON serializer properties. + * @param op Override properties. + * @param javaMethod Java method that invoked this serializer. + * When using the REST API, this is the Java method invoked by the REST call. + * Can be used to access annotations defined on the method or class. + */ + protected JsonSerializerContext(BeanContext beanContext, SerializerProperties sp, JsonSerializerProperties jsp, ObjectMap op, Method javaMethod) { + super(beanContext, sp, op, javaMethod); + if (op == null || op.isEmpty()) { + simpleMode = jsp.simpleMode; + useWhitespace = jsp.useWhitespace; + escapeSolidus = jsp.escapeSolidus; + } else { + simpleMode = op.getBoolean(JSON_simpleMode, jsp.simpleMode); + useWhitespace = op.getBoolean(JSON_useWhitespace, jsp.useWhitespace); + escapeSolidus = op.getBoolean(JSON_escapeSolidus, jsp.escapeSolidus); + } + } + + /** + * Returns the {@link JsonSerializerProperties#JSON_simpleMode} setting value in this context. + * @return The {@link JsonSerializerProperties#JSON_simpleMode} setting value in this context. + */ + public final boolean isSimpleMode() { + return simpleMode; + } + + /** + * Returns the {@link JsonSerializerProperties#JSON_useWhitespace} setting value in this context. + * @return The {@link JsonSerializerProperties#JSON_useWhitespace} setting value in this context. + */ + public final boolean isUseWhitespace() { + return useWhitespace; + } + + /** + * Returns the {@link JsonSerializerProperties#JSON_escapeSolidus} setting value in this context. + * @return The {@link JsonSerializerProperties#JSON_escapeSolidus} setting value in this context. + */ + public final boolean isEscapeSolidus() { + return escapeSolidus; + } + + /** + * Wraps the specified writer inside a {@link JsonSerializerWriter}. + * + * @param out The writer being wrapped. + * @return The wrapped writer. + */ + public JsonSerializerWriter getWriter(Writer out) { + if (out instanceof JsonSerializerWriter) + return (JsonSerializerWriter)out; + return new JsonSerializerWriter(out, isUseIndentation(), isUseWhitespace(), isEscapeSolidus(), getQuoteChar(), isSimpleMode(), getRelativeUriBase(), getAbsolutePathUriBase()); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializerProperties.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializerProperties.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializerProperties.class new file mode 100755 index 0000000..bdc0ee0 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializerProperties.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializerProperties.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializerProperties.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializerProperties.java new file mode 100755 index 0000000..8c4e8bf --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializerProperties.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.json; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.serializer.*; + +/** + * Configurable properties on the {@link JsonSerializer} class. + * <p> + * Use the {@link JsonSerializer#setProperty(String, Object)} method to set property values. + * <p> + * In addition to these properties, the following properties are also applicable for {@link JsonSerializer}. + * <ul> + * <li>{@link SerializerProperties} + * <li>{@link BeanContextProperties} + * </ul> + * + * @author James Bognar ([email protected]) + */ +public final class JsonSerializerProperties implements Cloneable { + + /** + * Simple JSON mode ({@link Boolean}, default=<jk>false</jk>). + * <p> + * If <jk>true</jk>, JSON attribute names will only be quoted when necessary. + * Otherwise, they are always quoted. + */ + public static final String JSON_simpleMode = "JsonSerializer.simpleMode"; + + /** + * Use whitespace in output ({@link Boolean}, default=<jk>false</jk>). + * <p> + * If <jk>true</jk>, whitespace is added to the output to improve readability. + */ + public static final String JSON_useWhitespace = "JsonSerializer.useWhitespace"; + + /** + * Prefix solidus <js>'/'</js> characters with escapes ({@link Boolean}, default=<jk>false</jk>). + * <p> + * If <jk>true</jk>, solidus (e.g. slash) characters should be escaped. + * The JSON specification allows for either format. + * However, if you're embedding JSON in an HTML script tag, this setting prevents + * confusion when trying to serialize <xt><\/script></xt>. + */ + public static final String JSON_escapeSolidus = "JsonSerializer.escapeSolidus"; + + boolean + simpleMode = false, + useWhitespace = false, + escapeSolidus = false; + + /** + * Sets the specified property value. + * @param property The property name. + * @param value The property value. + * @return <jk>true</jk> if property name was valid and property was set. + */ + public boolean setProperty(String property, Object value) { + BeanContext bc = BeanContext.DEFAULT; + if (property.equals(JSON_simpleMode)) + simpleMode = bc.convertToType(value, Boolean.class); + else if (property.equals(JSON_useWhitespace)) + useWhitespace = bc.convertToType(value, Boolean.class); + else if (property.equals(JSON_escapeSolidus)) + escapeSolidus = bc.convertToType(value, Boolean.class); + else + return false; + return true; + } + + + //-------------------------------------------------------------------------------- + // Overridden methods + //-------------------------------------------------------------------------------- + + @Override /* Cloneable */ + public JsonSerializerProperties clone() { + try { + return (JsonSerializerProperties)super.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); // Shouldn't happen + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializerWriter.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializerWriter.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializerWriter.class new file mode 100755 index 0000000..828cea2 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/json/JsonSerializerWriter.class differ
