http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/jso/JavaSerializedObjectParser.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/jso/JavaSerializedObjectParser.java
 
b/juneau-core/src/main/java/org/apache/juneau/jso/JavaSerializedObjectParser.java
new file mode 100644
index 0000000..769aeaa
--- /dev/null
+++ 
b/juneau-core/src/main/java/org/apache/juneau/jso/JavaSerializedObjectParser.java
@@ -0,0 +1,55 @@
+/***************************************************************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 
or implied.  See the License for the
+ * specific language governing permissions and limitations under the License.
+ 
***************************************************************************************************************************/
+package org.apache.juneau.jso;
+
+import java.io.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.parser.*;
+
+/**
+ * Parses POJOs from HTTP responses as Java {@link ObjectInputStream 
ObjectInputStreams}.
+ *
+ *
+ * <h6 class='topic'>Media types</h6>
+ * <p>
+ *     Consumes <code>Content-Type</code> types: 
<code>application/x-java-serialized-object</code>
+ *
+ *
+ * @author James Bognar ([email protected])
+ */
+@Consumes("application/x-java-serialized-object")
+public final class JavaSerializedObjectParser extends InputStreamParser {
+
+       
//--------------------------------------------------------------------------------
+       // Overridden methods
+       
//--------------------------------------------------------------------------------
+
+       @SuppressWarnings("unchecked")
+       @Override /* InputStreamParser */
+       protected <T> T doParse(ParserSession session, ClassMeta<T> type) 
throws Exception {
+               ObjectInputStream ois = new 
ObjectInputStream(session.getInputStream());
+               return (T)ois.readObject();
+       }
+
+
+       @Override /* Lockable */
+       public JavaSerializedObjectParser clone() {
+               try {
+                       return (JavaSerializedObjectParser)super.clone();
+               } catch (CloneNotSupportedException e) {
+                       throw new RuntimeException(e); // Shouldn't happen
+               }
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/jso/JavaSerializedObjectSerializer.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/jso/JavaSerializedObjectSerializer.java
 
b/juneau-core/src/main/java/org/apache/juneau/jso/JavaSerializedObjectSerializer.java
new file mode 100644
index 0000000..c66ab25
--- /dev/null
+++ 
b/juneau-core/src/main/java/org/apache/juneau/jso/JavaSerializedObjectSerializer.java
@@ -0,0 +1,56 @@
+/***************************************************************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 
or implied.  See the License for the
+ * specific language governing permissions and limitations under the License.
+ 
***************************************************************************************************************************/
+package org.apache.juneau.jso;
+
+import java.io.*;
+
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.serializer.*;
+
+/**
+ * Serializes POJOs to HTTP responses as Java {@link ObjectOutputStream 
ObjectOutputStreams}.
+ *
+ *
+ * <h6 class='topic'>Media types</h6>
+ * <p>
+ *     Handles <code>Accept</code> types: 
<code>application/x-java-serialized-object</code>
+ * <p>
+ *     Produces <code>Content-Type</code> types: 
<code>application/x-java-serialized-object</code>
+ *
+ *
+ * @author James Bognar ([email protected])
+ */
+@Produces("application/x-java-serialized-object")
+public final class JavaSerializedObjectSerializer extends 
OutputStreamSerializer {
+
+       
//--------------------------------------------------------------------------------
+       // Overridden methods
+       
//--------------------------------------------------------------------------------
+
+       @Override /* OutputStreamSerializer */
+       protected void doSerialize(SerializerSession session, Object o) throws 
Exception {
+               ObjectOutputStream oos = new 
ObjectOutputStream(session.getOutputStream());
+               oos.writeObject(o);
+               oos.flush();
+               oos.close();
+       }
+
+       @Override /* Serializer */
+       public JavaSerializedObjectSerializer clone() {
+               try {
+                       return (JavaSerializedObjectSerializer)super.clone();
+               } catch (CloneNotSupportedException e) {
+                       throw new RuntimeException(e); // Shouldn't happen
+               }
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/jso/package.html
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/jso/package.html 
b/juneau-core/src/main/java/org/apache/juneau/jso/package.html
new file mode 100644
index 0000000..176f869
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/jso/package.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<!--
+/***************************************************************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *  
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 
or implied.  See the License for the
+ * specific language governing permissions and limitations under the License.
+ *
+ 
***************************************************************************************************************************/
+ -->
+<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/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/json/JsonClassMeta.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/json/JsonClassMeta.java 
b/juneau-core/src/main/java/org/apache/juneau/json/JsonClassMeta.java
new file mode 100644
index 0000000..80d64d8
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/json/JsonClassMeta.java
@@ -0,0 +1,59 @@
+/***************************************************************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 
or implied.  See the License for the
+ * specific language governing permissions and limitations under the License.
+ 
***************************************************************************************************************************/
+package org.apache.juneau.json;
+
+import org.apache.juneau.internal.*;
+import org.apache.juneau.json.annotation.*;
+
+/**
+ * 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/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java 
b/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java
new file mode 100644
index 0000000..2ee9ace
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java
@@ -0,0 +1,823 @@
+/***************************************************************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 
or implied.  See the License for the
+ * specific language governing permissions and limitations under the License.
+ 
***************************************************************************************************************************/
+package org.apache.juneau.json;
+
+import static org.apache.juneau.json.JsonParserContext.*;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.transform.*;
+
+/**
+ * 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 class='spaced-list'>
+ *             <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 class='spaced-list'>
+ *             <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 ContextFactory} 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 class='spaced-list'>
+ *             <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 JsonParserContext}
+ * </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();
+
+       private <T> T parseAnything(JsonParserSession session, ClassMeta<T> nt, 
ParserReader r, Object outer) throws Exception {
+
+               BeanContext bc = session.getBeanContext();
+               if (nt == null)
+                       nt = (ClassMeta<T>)object();
+               PojoTransform<T,Object> transform = 
(PojoTransform<T,Object>)nt.getPojoTransform();
+               ClassMeta<?> ft = nt.getTransformedClassMeta();
+               session.setCurrentClass(ft);
+               String wrapperAttr = ft.getJsonMeta().getWrapperAttr();
+
+               Object o = null;
+
+               skipCommentsAndSpace(session, r);
+               if (wrapperAttr != null)
+                       skipWrapperAttrStart(session, r, wrapperAttr);
+               int c = r.peek();
+               if (c == -1) {
+                       // Let o be null.
+               } else if ((c == ',' || c == '}' || c == ']')) {
+                       if (session.isStrictMode())
+                               throw new ParseException(session, "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(session, "null", r);
+               } else if (ft.isObject()) {
+                       if (c == '{') {
+                               ObjectMap m2 = new ObjectMap(bc);
+                               parseIntoMap2(session, r, m2, string(), 
object());
+                               o = m2.cast();
+                       } else if (c == '[')
+                               o = parseIntoCollection2(session, r, new 
ObjectList(bc), object());
+                       else if (c == '\'' || c == '"') {
+                               o = parseString(session, r);
+                               if (ft.isChar())
+                                       o = o.toString().charAt(0);
+                       }
+                       else if (c >= '0' && c <= '9' || c == '-')
+                               o = parseNumber(session, r, null);
+                       else if (c == 't') {
+                               parseKeyword(session, "true", r);
+                               o = Boolean.TRUE;
+                       } else {
+                               parseKeyword(session, "false", r);
+                               o = Boolean.FALSE;
+                       }
+               } else if (ft.isBoolean()) {
+                       o = parseBoolean(session, r);
+               } else if (ft.isCharSequence()) {
+                       o = parseString(session, r);
+               } else if (ft.isChar()) {
+                       o = parseString(session, r).charAt(0);
+               } else if (ft.isNumber()) {
+                       o = parseNumber(session, r, (Class<? extends 
Number>)ft.getInnerClass());
+               } else if (ft.isMap()) {
+                       Map m = (ft.canCreateNewInstance(outer) ? 
(Map)ft.newInstance(outer) : new ObjectMap(bc));
+                       o = parseIntoMap2(session, r, m, ft.getKeyType(), 
ft.getValueType());
+               } else if (ft.isCollection()) {
+                       if (c == '{') {
+                               ObjectMap m = new ObjectMap(bc);
+                               parseIntoMap2(session, r, m, string(), 
object());
+                               o = m.cast();
+                       } else {
+                               Collection l = (ft.canCreateNewInstance(outer) 
? (Collection)ft.newInstance() : new ObjectList(bc));
+                               o = parseIntoCollection2(session, r, l, 
ft.getElementType());
+                       }
+               } else if (ft.canCreateNewInstanceFromObjectMap(outer)) {
+                       ObjectMap m = new ObjectMap(bc);
+                       parseIntoMap2(session, r, m, string(), object());
+                       o = ft.newInstanceFromObjectMap(outer, m);
+               } else if (ft.canCreateNewBean(outer)) {
+                       BeanMap m = bc.newBeanMap(outer, ft.getInnerClass());
+                       o = parseIntoBeanMap2(session, r, m).getBean();
+               } else if (ft.canCreateNewInstanceFromString(outer) && (c == 
'\'' || c == '"')) {
+                       o = ft.newInstanceFromString(outer, 
parseString(session, r));
+               } else if (ft.canCreateNewInstanceFromNumber(outer) && 
StringUtils.isFirstNumberChar((char)c)) {
+                       o = ft.newInstanceFromNumber(outer, 
parseNumber(session, r, ft.getNewInstanceFromNumberClass()));
+               } else if (ft.isArray()) {
+                       if (c == '{') {
+                               ObjectMap m = new ObjectMap(bc);
+                               parseIntoMap2(session, r, m, string(), 
object());
+                               o = m.cast();
+                       } else {
+                               ArrayList l = 
(ArrayList)parseIntoCollection2(session, r, new ArrayList(), 
ft.getElementType());
+                               o = bc.toArray(ft, l);
+                       }
+               } else if (c == '{') {
+                       Map m = new ObjectMap(bc);
+                       parseIntoMap2(session, r, m, ft.getKeyType(), 
ft.getValueType());
+                       if (m.containsKey("_class"))
+                               o = ((ObjectMap)m).cast();
+                       else
+                               throw new ParseException(session, "Class 
''{0}'' could not be instantiated.  Reason: ''{1}''", 
ft.getInnerClass().getName(), ft.getNotABeanReason());
+               } else if (ft.canCreateNewInstanceFromString(outer) && ! 
session.isStrictMode()) {
+                       o = ft.newInstanceFromString(outer, 
parseString(session, r));
+               } else {
+                       throw new ParseException(session, "Unrecognized syntax 
for class type ''{0}'', starting character ''{1}''", ft, (char)c);
+               }
+
+               if (wrapperAttr != null)
+                       skipWrapperAttrEnd(session, r);
+
+               if (transform != null && o != null)
+                       o = transform.normalize(o, nt);
+
+               if (outer != null)
+                       setParent(nt, o, outer);
+
+               return (T)o;
+       }
+
+       private Number parseNumber(JsonParserSession session, ParserReader r, 
Class<? extends Number> type) throws Exception {
+               int c = r.peek();
+               if (c == '\'' || c == '"')
+                       return parseNumber(session, parseString(session, r), 
type);
+               return parseNumber(session, StringUtils.parseNumberString(r), 
type);
+       }
+
+       private Number parseNumber(JsonParserSession session, String s, Class<? 
extends Number> type) throws Exception {
+               if (session.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(JsonParserSession session, ParserReader r) 
throws Exception {
+               int c = r.peek();
+               if (c == '\'' || c == '"')
+                       return Boolean.valueOf(parseString(session, r));
+               if (c == 't') {
+                       parseKeyword(session, "true", r);
+                       return Boolean.TRUE;
+               }
+               parseKeyword(session, "false", r);
+               return Boolean.FALSE;
+       }
+
+
+       private <K,V> Map<K,V> parseIntoMap2(JsonParserSession session, 
ParserReader r, Map<K,V> m, ClassMeta<K> keyType, ClassMeta<V> valueType) 
throws Exception {
+
+               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(session, 
r.unread());
+                               } else if (! Character.isWhitespace(c)) {
+                                       currAttr = parseFieldName(session, 
r.unread());
+                                       state = S3;
+                               }
+                       } else if (state == S3) {
+                               if (c == ':')
+                                       state = S4;
+                       } else if (state == S4) {
+                               if (c == '/') {
+                                       skipCommentsAndSpace(session, 
r.unread());
+                               } else if (! Character.isWhitespace(c)) {
+                                       K key = convertAttrToType(session, m, 
currAttr, keyType);
+                                       V value = parseAnything(session, 
valueType, r.unread(), m);
+                                       setName(valueType, value, key);
+                                       m.put(key, value);
+                                       state = S5;
+                               }
+                       } else if (state == S5) {
+                               if (c == ',')
+                                       state = S1;
+                               else if (c == '/')
+                                       skipCommentsAndSpace(session, 
r.unread());
+                               else if (c == '}') {
+                                       return m;
+                               }
+                       }
+               }
+               if (state == S0)
+                       throw new ParseException(session, "Expected '{' at 
beginning of JSON object.");
+               if (state == S1)
+                       throw new ParseException(session, "Could not find 
attribute name on JSON object.");
+               if (state == S3)
+                       throw new ParseException(session, "Could not find ':' 
following attribute name on JSON object.");
+               if (state == S4)
+                       throw new ParseException(session, "Expected one of the 
following characters: {,[,',\",LITERAL.");
+               if (state == S5)
+                       throw new ParseException(session, "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(JsonParserSession session, ParserReader 
r) throws Exception {
+               int c = r.peek();
+               if (c == '\'' || c == '"')
+                       return parseString(session, r);
+               if (session.isStrictMode())
+                       throw new ParseException(session, "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(session, "Could not find the end of 
the field name.");
+       }
+
+       private <E> Collection<E> parseIntoCollection2(JsonParserSession 
session, ParserReader r, Collection<E> l, ClassMeta<E> elementType) throws 
Exception {
+
+               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(session, 
r.unread());
+                               } else if (! Character.isWhitespace(c)) {
+                                       l.add(parseAnything(session, 
elementType, r.unread(), l));
+                                       state = S2;
+                               }
+                       } else if (state == S2) {
+                               if (c == ',') {
+                                       state = S1;
+                               } else if (c == '/') {
+                                       skipCommentsAndSpace(session, 
r.unread());
+                               } else if (c == ']') {
+                                       return l;
+                               }
+                       }
+               }
+               if (state == S0)
+                       throw new ParseException(session, "Expected '[' at 
beginning of JSON array.");
+               if (state == S1)
+                       throw new ParseException(session, "Expected one of the 
following characters: {,[,',\",LITERAL.");
+               if (state == S2)
+                       throw new ParseException(session, "Expected ',' or 
']'.");
+
+               return null;  // Unreachable.
+       }
+
+       private Object[] parseArgs(JsonParserSession session, ParserReader r, 
ClassMeta<?>[] argTypes) throws Exception {
+
+               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(session, 
r.unread());
+                               } else if (! Character.isWhitespace(c)) {
+                                       o[i] = parseAnything(session, 
argTypes[i], r.unread(), session.getOuter());
+                                       i++;
+                                       state = S2;
+                               }
+                       } else if (state == S2) {
+                               if (c == ',') {
+                                       state = S1;
+                               } else if (c == '/') {
+                                       skipCommentsAndSpace(session, 
r.unread());
+                               } else if (c == ']') {
+                                       return o;
+                               }
+                       }
+               }
+               if (state == S0)
+                       throw new ParseException(session, "Expected '[' at 
beginning of JSON array.");
+               if (state == S1)
+                       throw new ParseException(session, "Expected one of the 
following characters: {,[,',\",LITERAL.");
+               if (state == S2)
+                       throw new ParseException(session, "Expected ',' or 
']'.");
+
+               return null;  // Unreachable.
+       }
+
+       private <T> BeanMap<T> parseIntoBeanMap2(JsonParserSession session, 
ParserReader r, BeanMap<T> m) throws Exception {
+
+               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(session, 
r.unread());
+                               } else if (! Character.isWhitespace(c)) {
+                                       r.unread();
+                                       currAttrLine= r.getLine();
+                                       currAttrCol = r.getColumn();
+                                       currAttr = parseFieldName(session, r);
+                                       state = S3;
+                               }
+                       } else if (state == S3) {
+                               if (c == ':')
+                                       state = S4;
+                       } else if (state == S4) {
+                               if (c == '/') {
+                                       skipCommentsAndSpace(session, 
r.unread());
+                               } else if (! Character.isWhitespace(c)) {
+                                       if (! currAttr.equals("_class")) {
+                                               BeanPropertyMeta pMeta = 
m.getPropertyMeta(currAttr);
+                                               
session.setCurrentProperty(pMeta);
+                                               if (pMeta == null) {
+                                                       if 
(m.getMeta().isSubTyped()) {
+                                                               Object value = 
parseAnything(session, object(), r.unread(), m.getBean(false));
+                                                               m.put(currAttr, 
value);
+                                                       } else {
+                                                               
onUnknownProperty(session, currAttr, m, currAttrLine, currAttrCol);
+                                                               
parseAnything(session, object(), r.unread(), m.getBean(false)); // Read content 
anyway to ignore it
+                                                       }
+                                               } else {
+                                                       ClassMeta<?> cm = 
pMeta.getClassMeta();
+                                                       Object value = 
parseAnything(session, cm, r.unread(), m.getBean(false));
+                                                       setName(cm, value, 
currAttr);
+                                                       pMeta.set(m, value);
+                                               }
+                                               
session.setCurrentProperty(null);
+                                       }
+                                       state = S5;
+                               }
+                       } else if (state == S5) {
+                               if (c == ',')
+                                       state = S1;
+                               else if (c == '/')
+                                       skipCommentsAndSpace(session, 
r.unread());
+                               else if (c == '}') {
+                                       return m;
+                               }
+                       }
+               }
+               if (state == S0)
+                       throw new ParseException(session, "Expected '{' at 
beginning of JSON object.");
+               if (state == S1)
+                       throw new ParseException(session, "Could not find 
attribute name on JSON object.");
+               if (state == S3)
+                       throw new ParseException(session, "Could not find ':' 
following attribute name on JSON object.");
+               if (state == S4)
+                       throw new ParseException(session, "Expected one of the 
following characters: {,[,',\",LITERAL.");
+               if (state == S5)
+                       throw new ParseException(session, "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(JsonParserSession session, ParserReader r) 
throws Exception  {
+               r.mark();
+               int qc = r.read();              // The quote character being 
used (" or ')
+               if (qc != '"' && session.isStrictMode()) {
+                       String msg = (qc == '\'' ? "Invalid quote character 
\"{0}\" being used." : "Did not find quote character marking beginning of 
string.  Character=\"{0}\"");
+                       throw new ParseException(session, 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(session, "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(session, "Could not find 
expected end character ''{0}''.", (char)qc);
+
+               // Look for concatenated string (i.e. whitespace followed by +).
+               skipCommentsAndSpace(session, r);
+               if (r.peek() == '+') {
+                       if (session.isStrictMode())
+                               throw new ParseException(session, "String 
concatenation detected.");
+                       r.read();       // Skip past '+'
+                       skipCommentsAndSpace(session, r);
+                       s += parseString(session, r);
+               }
+               return session.trim(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(JsonParserSession session, String keyword, 
ParserReader r) throws Exception {
+               try {
+                       String s = r.read(keyword.length());
+                       if (s.equals(keyword))
+                               return;
+                       throw new ParseException(session, "Unrecognized 
syntax.");
+               } catch (IndexOutOfBoundsException e) {
+                       throw new ParseException(session, "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(JsonParserSession session, 
ParserReader r) throws Exception {
+               int c = 0;
+               while ((c = r.read()) != -1) {
+                       if (! Character.isWhitespace(c)) {
+                               if (c == '/') {
+                                       if (session.isStrictMode())
+                                               throw new 
ParseException(session, "Javascript comment detected.");
+                                       skipComments(session, 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(JsonParserSession session, 
ParserReader r, String wrapperAttr) throws Exception {
+
+               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(session, 
r.unread());
+                               } else if (! Character.isWhitespace(c)) {
+                                       currAttr = parseFieldName(session, 
r.unread());
+                                       if (! currAttr.equals(wrapperAttr))
+                                               throw new 
ParseException(session, "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(session, 
r.unread());
+                               } else if (! Character.isWhitespace(c)) {
+                                       r.unread();
+                                       return;
+                               }
+                       }
+               }
+               if (state == S0)
+                       throw new ParseException(session, "Expected '{' at 
beginning of JSON object.");
+               if (state == S1)
+                       throw new ParseException(session, "Could not find 
attribute name on JSON object.");
+               if (state == S3)
+                       throw new ParseException(session, "Could not find ':' 
following attribute name on JSON object.");
+               if (state == S4)
+                       throw new ParseException(session, "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(JsonParserSession session, ParserReader 
r) throws ParseException, IOException {
+               int c = 0;
+               while ((c = r.read()) != -1) {
+                       if (! Character.isWhitespace(c)) {
+                               if (c == '/') {
+                                       if (session.isStrictMode())
+                                               throw new 
ParseException(session, "Javascript comment detected.");
+                                       skipComments(session, r);
+                               } else if (c == '}') {
+                                       return;
+                               } else {
+                                       throw new ParseException(session, 
"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(JsonParserSession session, ParserReader r) 
throws ParseException, IOException {
+               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(session, "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(JsonParserSession session, ParserReader r) 
throws Exception {
+               skipCommentsAndSpace(session, r);
+               int c = r.read();
+               if (c != -1 && c != ';')  // var x = {...}; expressions can end 
with a semicolon.
+                       throw new ParseException(session, "Remainder after 
parse: ''{0}''.", (char)c);
+       }
+
+
+       
//--------------------------------------------------------------------------------
+       // Overridden methods
+       
//--------------------------------------------------------------------------------
+
+       @Override /* Parser */
+       public JsonParserSession createSession(Object input, ObjectMap op, 
Method javaMethod, Object outer) {
+               return new 
JsonParserSession(getContext(JsonParserContext.class), getBeanContext(), input, 
op, javaMethod, outer);
+       }
+
+       @Override /* Parser */
+       protected <T> T doParse(ParserSession session, ClassMeta<T> type) 
throws Exception {
+               JsonParserSession s = (JsonParserSession)session;
+               type = s.getBeanContext().normalizeClassMeta(type);
+               ParserReader r = s.getReader();
+               if (r == null)
+                       return null;
+               T o = parseAnything(s, type, r, s.getOuter());
+               validateEnd(s, r);
+               return o;
+       }
+
+       @Override /* ReaderParser */
+       protected <K,V> Map<K,V> doParseIntoMap(ParserSession session, Map<K,V> 
m, Type keyType, Type valueType) throws Exception {
+               JsonParserSession s = (JsonParserSession)session;
+               ParserReader r = s.getReader();
+               m = parseIntoMap2(s, r, m, 
s.getBeanContext().getClassMeta(keyType), 
s.getBeanContext().getClassMeta(valueType));
+               validateEnd(s, r);
+               return m;
+       }
+
+       @Override /* ReaderParser */
+       protected <E> Collection<E> doParseIntoCollection(ParserSession 
session, Collection<E> c, Type elementType) throws Exception {
+               JsonParserSession s = (JsonParserSession)session;
+               ParserReader r = s.getReader();
+               c = parseIntoCollection2(s, r, c, 
s.getBeanContext().getClassMeta(elementType));
+               validateEnd(s, r);
+               return c;
+       }
+
+       @Override /* ReaderParser */
+       protected Object[] doParseArgs(ParserSession session, ClassMeta<?>[] 
argTypes) throws Exception {
+               JsonParserSession s = (JsonParserSession)session;
+               ParserReader r = s.getReader();
+               Object[] a = parseArgs(s, r, argTypes);
+               validateEnd(s, r);
+               return a;
+       }
+
+       @Override /* Parser */
+       public JsonParser setProperty(String property, Object value) throws 
LockedException {
+               super.setProperty(property, value);
+               return this;
+       }
+
+       @Override /* CoreApi */
+       public JsonParser setProperties(ObjectMap properties) throws 
LockedException {
+               super.setProperties(properties);
+               return this;
+       }
+
+       @Override /* CoreApi */
+       public JsonParser addNotBeanClasses(Class<?>...classes) throws 
LockedException {
+               super.addNotBeanClasses(classes);
+               return this;
+       }
+
+       @Override /* CoreApi */
+       public JsonParser addTransforms(Class<?>...classes) throws 
LockedException {
+               super.addTransforms(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/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/json/JsonParserContext.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/json/JsonParserContext.java 
b/juneau-core/src/main/java/org/apache/juneau/json/JsonParserContext.java
new file mode 100644
index 0000000..4cd81b6
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/json/JsonParserContext.java
@@ -0,0 +1,66 @@
+/***************************************************************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 
or implied.  See the License for the
+ * specific language governing permissions and limitations under the License.
+ 
***************************************************************************************************************************/
+package org.apache.juneau.json;
+
+import org.apache.juneau.*;
+import org.apache.juneau.parser.*;
+
+/**
+ * Configurable properties on the {@link JsonParser} class.
+ * <p>
+ * Context properties are set by calling {@link 
ContextFactory#setProperty(String, Object)} on the context factory
+ * returned {@link CoreApi#getContextFactory()}.
+ * <p>
+ * The following convenience methods are also provided for setting context 
properties:
+ * <ul>
+ *     <li>{@link JsonParser#setProperty(String,Object)}
+ *     <li>{@link JsonParser#setProperties(ObjectMap)}
+ *     <li>{@link JsonParser#addNotBeanClasses(Class[])}
+ *     <li>{@link JsonParser#addTransforms(Class[])}
+ *     <li>{@link JsonParser#addImplClass(Class,Class)}
+ * </ul>
+ * <p>
+ * See {@link ContextFactory} for more information about context properties.
+ *
+ * @author James Bognar ([email protected])
+ */
+public final class JsonParserContext extends ParserContext {
+
+       /**
+        * Set strict mode ({@link Boolean}, default=<jk>false</jk>).
+        * <p>
+        * When in strict mode, parser throws exceptions on the following 
invalid JSON syntax:
+        * <ul class='spaced-list'>
+        *      <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";
+
+       final boolean strictMode;
+
+       /**
+        * Constructor.
+        * <p>
+        * Typically only called from {@link ContextFactory#getContext(Class)}.
+        *
+        * @param cf The factory that created this context.
+        */
+       public JsonParserContext(ContextFactory cf) {
+               super(cf);
+               this.strictMode = cf.getProperty(JSON_strictMode, 
boolean.class, false);
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/json/JsonParserSession.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/json/JsonParserSession.java 
b/juneau-core/src/main/java/org/apache/juneau/json/JsonParserSession.java
new file mode 100644
index 0000000..bee806a
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/json/JsonParserSession.java
@@ -0,0 +1,96 @@
+/***************************************************************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 
or implied.  See the License for the
+ * specific language governing permissions and limitations under the License.
+ 
***************************************************************************************************************************/
+package org.apache.juneau.json;
+
+import static org.apache.juneau.json.JsonParserContext.*;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.parser.*;
+
+/**
+ * Session object that lives for the duration of a single use of {@link 
JsonParser}.
+ * <p>
+ * This class is NOT thread safe.  It is meant to be discarded after one-time 
use.
+ *
+ * @author James Bognar ([email protected])
+ */
+public final class JsonParserSession extends ParserSession {
+
+       private final boolean strictMode;
+       private ParserReader reader;
+
+       /**
+        * Create a new session using properties specified in the context.
+        *
+        * @param ctx The context creating this session object.
+        *      The context contains all the configuration settings for this 
object.
+        * @param beanContext The bean context being used.
+        * @param input The input.  Can be any of the following types:
+        *      <ul>
+        *              <li><jk>null</jk>
+        *              <li>{@link Reader}
+        *              <li>{@link CharSequence}
+        *              <li>{@link InputStream} containing UTF-8 encoded text.
+        *              <li>{@link File} containing system encoded text.
+        *      </ul>
+        * @param op The override properties.
+        *      These override any context properties defined in the context.
+        * @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 JsonParserSession(JsonParserContext ctx, BeanContext 
beanContext, Object input, ObjectMap op, Method javaMethod, Object outer) {
+               super(ctx, beanContext, input, op, javaMethod, outer);
+               if (op == null || op.isEmpty()) {
+                       strictMode = ctx.strictMode;
+               } else {
+                       strictMode = op.getBoolean(JSON_strictMode, 
ctx.strictMode);
+               }
+       }
+
+       /**
+        * Returns the {@link JsonParserContext#JSON_strictMode} setting value 
for this session.
+        *
+        * @return The {@link JsonParserContext#JSON_strictMode} setting value 
for this session.
+        */
+       public final boolean isStrictMode() {
+               return strictMode;
+       }
+
+       @Override /* ParserSession */
+       public ParserReader getReader() throws Exception {
+               if (reader == null) {
+                       Object input = getInput();
+                       if (input == null)
+                               return null;
+                       if (input instanceof CharSequence)
+                               reader = new ParserReader((CharSequence)input);
+                       else
+                               reader = new ParserReader(super.getReader());
+               }
+               return reader;
+       }
+
+       @Override /* ParserSession */
+       public Map<String,Object> getLastLocation() {
+               Map<String,Object> m = super.getLastLocation();
+               if (reader != null) {
+                       m.put("line", reader.getLine());
+                       m.put("column", reader.getColumn());
+               }
+               return m;
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/json/JsonSchemaSerializer.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/json/JsonSchemaSerializer.java 
b/juneau-core/src/main/java/org/apache/juneau/json/JsonSchemaSerializer.java
new file mode 100644
index 0000000..1043031
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/json/JsonSchemaSerializer.java
@@ -0,0 +1,157 @@
+/***************************************************************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 
or implied.  See the License for the
+ * specific language governing permissions and limitations under the License.
+ 
***************************************************************************************************************************/
+package org.apache.juneau.json;
+
+import static org.apache.juneau.internal.ClassUtils.*;
+import static org.apache.juneau.serializer.SerializerContext.*;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.transform.*;
+
+/**
+ * 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);
+       }
+
+       /**
+        * Constructor.
+        *
+        * @param config Initialize with the specified config property store.
+        */
+       public JsonSchemaSerializer(ContextFactory config) {
+               getContextFactory().copyFrom(config);
+               setProperty(SERIALIZER_detectRecursions, true);
+               setProperty(SERIALIZER_ignoreRecursions, true);
+       }
+
+       
//--------------------------------------------------------------------------------
+       // Overridden methods
+       
//--------------------------------------------------------------------------------
+
+       @Override /* JsonSerializer */
+       protected void doSerialize(SerializerSession session, Object o) throws 
Exception {
+               JsonSerializerSession s = (JsonSerializerSession)session;
+               ObjectMap schema = getSchema(s, 
s.getBeanContext().getClassMetaForObject(o), "root", null);
+               serializeAnything(s, s.getWriter(), schema, null, "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(JsonSerializerSession session, ClassMeta<?> 
eType, String attrName, String[] pNames) throws Exception {
+               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 = session.push(attrName, eType, null);
+
+               gType = eType.getTransformedClassMeta();
+               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());
+               PojoTransform f = eType.getPojoTransform();
+               if (f != null)
+                       out.put("transform", 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(session, 
componentType, "items", pNames));
+                       } else if (gType.isBean()) {
+                               ObjectMap properties = new ObjectMap();
+                               BeanMeta bm = 
session.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(session, p.getClassMeta(), p.getName(), p.getProperties()));
+                               }
+                               out.put("properties", properties);
+                       }
+               }
+               session.pop();
+               return out;
+       }
+
+       @SuppressWarnings({ "unchecked", "rawtypes" })
+       private List<String> getEnumStrings(Class<? extends Enum> c) {
+               List<String> l = new LinkedList<String>();
+               for (Object e : EnumSet.allOf(c))
+                       l.add(e.toString());
+               return l;
+       }
+
+
+       @Override /* Lockable */
+       public JsonSchemaSerializer lock() {
+               super.lock();
+               return this;
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializer.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializer.java 
b/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializer.java
new file mode 100644
index 0000000..7d431b2
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializer.java
@@ -0,0 +1,424 @@
+/***************************************************************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 
or implied.  See the License for the
+ * specific language governing permissions and limitations under the License.
+ 
***************************************************************************************************************************/
+package org.apache.juneau.json;
+
+import static org.apache.juneau.json.JsonSerializerContext.*;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.transform.*;
+
+/**
+ * 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 class='spaced-list'>
+ *             <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 
org.apache.juneau.transform.Transform Transforms} associated through the {@link 
CoreApi#addTransforms(Class...)}
+ *             method.  Several default transforms 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 JsonSerializerContext}
+ *     <li>{@link SerializerContext}
+ *     <li>{@link BeanContext}
+ * </ul>
+ *
+ *
+ * <h6 class='topic'>Behavior-specific subclasses</h6>
+ * <p>
+ *     The following direct subclasses are provided for convenience:
+ * <ul class='spaced-list'>
+ *     <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(JsonSerializerContext.<jsf>JSON_simpleMode</jsf>, 
<jk>true</jk>)
+ *             .setProperty(SerializerContext.<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(SerializerContext.<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);
+               }
+       }
+
+       /**
+        * Workhorse method. Determines the type of object, and then calls the
+        * appropriate type-specific serialization method.
+        */
+       @SuppressWarnings({ "rawtypes", "unchecked" })
+       SerializerWriter serializeAnything(JsonSerializerSession session, 
JsonWriter out, Object o, ClassMeta<?> eType, String attrName, BeanPropertyMeta 
pMeta) throws Exception {
+               BeanContext bc = session.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 = session.push(attrName, o, eType);
+               boolean isRecursion = aType == null;
+
+               // Handle recursion
+               if (aType == null) {
+                       o = null;
+                       aType = object();
+               }
+
+               gType = aType.getTransformedClassMeta();
+               addClassAttr = (session.isAddClassAttrs() && ! 
eType.equals(aType));
+
+               // Transform if necessary
+               PojoTransform transform = aType.getPojoTransform();             
                // The transform
+               if (transform != null) {
+                       o = transform.transform(o);
+
+                       // If the transform's getTransformedClass() 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(session.indent).attr(wrapperAttr).append(':').s();
+                       session.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(session, out, gType.toObjectMap(o), gType);
+               else if (gType.isBean())
+                       serializeBeanMap(session, out, bc.forBean(o), 
addClassAttr);
+               else if (gType.isUri() || (pMeta != null && (pMeta.isUri() || 
pMeta.isBeanUri())))
+                       out.q().appendUri(o).q();
+               else if (gType.isMap()) {
+                       if (o instanceof BeanMap)
+                               serializeBeanMap(session, out, (BeanMap)o, 
addClassAttr);
+                       else
+                               serializeMap(session, out, (Map)o, eType);
+               }
+               else if (gType.isCollection()) {
+                       if (addClassAttr)
+                               serializeCollectionMap(session, out, 
(Collection)o, gType);
+                       else
+                               serializeCollection(session, out, (Collection) 
o, eType);
+               }
+               else if (gType.isArray()) {
+                       if (addClassAttr)
+                               serializeCollectionMap(session, out, 
toList(gType.getInnerClass(), o), gType);
+                       else
+                               serializeCollection(session, out, 
toList(gType.getInnerClass(), o), eType);
+               }
+               else
+                       out.stringValue(session.toString(o));
+
+               if (wrapperAttr != null) {
+                       session.indent--;
+                       out.cr(session.indent-1).append('}');
+               }
+
+               if (! isRecursion)
+                       session.pop();
+               return out;
+       }
+
+       @SuppressWarnings({ "rawtypes", "unchecked" })
+       private SerializerWriter serializeMap(JsonSerializerSession session, 
JsonWriter out, Map m, ClassMeta<?> type) throws Exception {
+
+               ClassMeta<?> keyType = type.getKeyType(), valueType = 
type.getValueType();
+
+               m = session.sort(m);
+
+               int depth = session.getIndent();
+               out.append('{');
+
+               Iterator mapEntries = m.entrySet().iterator();
+
+               while (mapEntries.hasNext()) {
+                       Map.Entry e = (Map.Entry) mapEntries.next();
+                       Object value = e.getValue();
+
+                       Object key = session.generalize(e.getKey(), keyType);
+
+                       
out.cr(depth).attr(session.toString(key)).append(':').s();
+
+                       serializeAnything(session, out, value, valueType, (key 
== null ? null : session.toString(key)), null);
+
+                       if (mapEntries.hasNext())
+                               out.append(',').s();
+               }
+
+               out.cr(depth-1).append('}');
+
+               return out;
+       }
+
+       @SuppressWarnings({ "rawtypes" })
+       private SerializerWriter serializeCollectionMap(JsonSerializerSession 
session, JsonWriter out, Collection o, ClassMeta<?> type) throws Exception {
+               int i = session.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();
+               session.indent++;
+               serializeCollection(session, out, o, type);
+               session.indent--;
+               out.cr(i-1).append('}');
+               return out;
+       }
+
+       @SuppressWarnings({ "rawtypes" })
+       private SerializerWriter serializeBeanMap(JsonSerializerSession 
session, JsonWriter out, BeanMap<?> m, boolean addClassAttr) throws Exception {
+               int depth = session.getIndent();
+               out.append('{');
+
+               boolean addComma = false;
+
+               for (BeanPropertyValue p : m.getValues(addClassAttr, 
session.isTrimNulls())) {
+                       BeanPropertyMeta pMeta = p.getMeta();
+                       String key = p.getName();
+                       Object value = p.getValue();
+                       Throwable t = p.getThrown();
+                       if (t != null)
+                               session.addBeanGetterWarning(pMeta, t);
+
+                       if (session.canIgnoreValue(pMeta.getClassMeta(), key, 
value))
+                               continue;
+
+                       if (addComma)
+                               out.append(',').s();
+
+                       out.cr(depth).attr(key).append(':').s();
+
+                       serializeAnything(session, out, value, 
pMeta.getClassMeta(), key, pMeta);
+
+                       addComma = true;
+               }
+               out.cr(depth-1).append('}');
+               return out;
+       }
+
+       @SuppressWarnings({"rawtypes", "unchecked"})
+       private SerializerWriter serializeCollection(JsonSerializerSession 
session, JsonWriter out, Collection c, ClassMeta<?> type) throws Exception {
+
+               ClassMeta<?> elementType = type.getElementType();
+
+               c = session.sort(c);
+
+               out.append('[');
+               int depth = session.getIndent();
+
+               for (Iterator i = c.iterator(); i.hasNext();) {
+
+                       Object value = i.next();
+
+                       out.cr(depth);
+
+                       serializeAnything(session, out, value, elementType, 
"<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(getContextFactory());
+               return s;
+       }
+
+       
//--------------------------------------------------------------------------------
+       // Overridden methods
+       
//--------------------------------------------------------------------------------
+
+       @Override /* Serializer */
+       public JsonSerializerSession createSession(Object output, ObjectMap 
properties, Method javaMethod) {
+               return new 
JsonSerializerSession(getContext(JsonSerializerContext.class), 
getBeanContext(), output, properties, javaMethod);
+       }
+
+       @Override /* Serializer */
+       protected void doSerialize(SerializerSession session, Object o) throws 
Exception {
+               JsonSerializerSession s = (JsonSerializerSession)session;
+               serializeAnything(s, s.getWriter(), o, null, "root", null);
+       }
+
+       @Override /* CoreApi */
+       public JsonSerializer setProperty(String property, Object value) throws 
LockedException {
+               super.setProperty(property, value);
+               return this;
+       }
+
+       @Override /* CoreApi */
+       public JsonSerializer setProperties(ObjectMap properties) throws 
LockedException {
+               super.setProperties(properties);
+               return this;
+       }
+
+       @Override /* CoreApi */
+       public JsonSerializer addNotBeanClasses(Class<?>...classes) throws 
LockedException {
+               super.addNotBeanClasses(classes);
+               return this;
+       }
+
+       @Override /* CoreApi */
+       public JsonSerializer addTransforms(Class<?>...classes) throws 
LockedException {
+               super.addTransforms(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();
+                       return c;
+               } catch (CloneNotSupportedException e) {
+                       throw new RuntimeException(e); // Shouldn't happen
+               }
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerContext.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerContext.java 
b/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerContext.java
new file mode 100644
index 0000000..50e08af
--- /dev/null
+++ 
b/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerContext.java
@@ -0,0 +1,83 @@
+/***************************************************************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 
or implied.  See the License for the
+ * specific language governing permissions and limitations under the License.
+ 
***************************************************************************************************************************/
+package org.apache.juneau.json;
+
+import org.apache.juneau.*;
+import org.apache.juneau.serializer.*;
+
+/**
+ * Configurable properties on the {@link JsonSerializer} class.
+ * <p>
+ * Context properties are set by calling {@link 
ContextFactory#setProperty(String, Object)} on the context factory
+ * returned {@link CoreApi#getContextFactory()}.
+ * <p>
+ * The following convenience methods are also provided for setting context 
properties:
+ * <ul>
+ *     <li>{@link JsonSerializer#setProperty(String,Object)}
+ *     <li>{@link JsonSerializer#setProperties(ObjectMap)}
+ *     <li>{@link JsonSerializer#addNotBeanClasses(Class[])}
+ *     <li>{@link JsonSerializer#addTransforms(Class[])}
+ *     <li>{@link JsonSerializer#addImplClass(Class,Class)}
+ * </ul>
+ * <p>
+ * See {@link ContextFactory} for more information about context properties.
+ *
+ * @author James Bognar ([email protected])
+ */
+public final class JsonSerializerContext extends SerializerContext {
+
+       /**
+        * 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";
+
+
+       final boolean
+               simpleMode,
+               useWhitespace,
+               escapeSolidus;
+
+       /**
+        * Constructor.
+        * <p>
+        * Typically only called from {@link ContextFactory#getContext(Class)}.
+        *
+        * @param cf The factory that created this context.
+        */
+       public JsonSerializerContext(ContextFactory cf) {
+               super(cf);
+               simpleMode = cf.getProperty(JSON_simpleMode, boolean.class, 
false);
+               useWhitespace = cf.getProperty(JSON_useWhitespace, 
boolean.class, false);
+               escapeSolidus = cf.getProperty(JSON_escapeSolidus, 
boolean.class, false);
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerSession.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerSession.java 
b/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerSession.java
new file mode 100644
index 0000000..ec0fff3
--- /dev/null
+++ 
b/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerSession.java
@@ -0,0 +1,91 @@
+/***************************************************************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 
or implied.  See the License for the
+ * specific language governing permissions and limitations under the License.
+ 
***************************************************************************************************************************/
+package org.apache.juneau.json;
+
+import static org.apache.juneau.json.JsonSerializerContext.*;
+
+import java.lang.reflect.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.serializer.*;
+
+/**
+ * Session object that lives for the duration of a single use of {@link 
JsonSerializer}.
+ * <p>
+ * This class is NOT thread safe.  It is meant to be discarded after one-time 
use.
+ *
+ * @author James Bognar ([email protected])
+ */
+public final class JsonSerializerSession extends SerializerSession {
+
+       private final boolean simpleMode, useWhitespace, escapeSolidus;
+
+       /**
+        * Create a new session using properties specified in the context.
+        *
+        * @param ctx The context creating this session object.
+        *      The context contains all the configuration settings for this 
object.
+        * @param beanContext The bean context being used.
+        * @param output The output object.  See {@link 
JsonSerializerSession#getWriter()} for valid class types.
+        * @param op The override properties.
+        *      These override any context properties defined in the context.
+        * @param javaMethod The java method that called this parser, usually 
the method in a REST servlet.
+        */
+       protected JsonSerializerSession(JsonSerializerContext ctx, BeanContext 
beanContext, Object output, ObjectMap op, Method javaMethod) {
+               super(ctx, beanContext, output, op, javaMethod);
+               if (op == null || op.isEmpty()) {
+                       simpleMode = ctx.simpleMode;
+                       useWhitespace = ctx.useWhitespace;
+                       escapeSolidus = ctx.escapeSolidus;
+               } else {
+                       simpleMode = op.getBoolean(JSON_simpleMode, 
ctx.simpleMode);
+                       useWhitespace = op.getBoolean(JSON_useWhitespace, 
ctx.useWhitespace);
+                       escapeSolidus = op.getBoolean(JSON_escapeSolidus, 
ctx.escapeSolidus);
+               }
+       }
+
+       /**
+        * Returns the {@link JsonSerializerContext#JSON_simpleMode} setting 
value for this session.
+        *
+        * @return The {@link JsonSerializerContext#JSON_simpleMode} setting 
value for this session.
+        */
+       public final boolean isSimpleMode() {
+               return simpleMode;
+       }
+
+       /**
+        * Returns the {@link JsonSerializerContext#JSON_useWhitespace} setting 
value for this session.
+        *
+        * @return The {@link JsonSerializerContext#JSON_useWhitespace} setting 
value for this session.
+        */
+       public final boolean isUseWhitespace() {
+               return useWhitespace;
+       }
+
+       /**
+        * Returns the {@link JsonSerializerContext#JSON_escapeSolidus} setting 
value for this session.
+        *
+        * @return The {@link JsonSerializerContext#JSON_escapeSolidus} setting 
value for this session.
+        */
+       public final boolean isEscapeSolidus() {
+               return escapeSolidus;
+       }
+
+       @Override /* ParserSession */
+       public JsonWriter getWriter() throws Exception {
+               Object output = getOutput();
+               if (output instanceof JsonWriter)
+                       return (JsonWriter)output;
+               return new JsonWriter(super.getWriter(), isUseIndentation(), 
isUseWhitespace(), isEscapeSolidus(), getQuoteChar(), isSimpleMode(), 
isTrimStrings(), getRelativeUriBase(), getAbsolutePathUriBase());
+       }
+}

Reply via email to