You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2016/08/01 00:08:04 UTC

[15/51] [partial] incubator-juneau git commit: Initial Juno contents from IBM JazzHub repo

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 (jbognar@us.ibm.com)
+ */
+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 (jbognar@us.ibm.com)
+ */
+@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 (jbognar@us.ibm.com)
+ */
+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 (jbognar@us.ibm.com)
+ */
+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 (jbognar@us.ibm.com)
+ */
+@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 (jbognar@us.ibm.com)
+ */
+@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 (jbognar@us.ibm.com)
+ */
+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 (jbognar@us.ibm.com)
+ */
+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>&lt;\/script&gt;</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