SLING-7346: handle JSONResponse properties as json if possible.
diff --git a/src/main/java/org/apache/sling/servlets/post/JSONResponse.java b/src/main/java/org/apache/sling/servlets/post/JSONResponse.java
index e65e90c..8cffaf8 100644
--- a/src/main/java/org/apache/sling/servlets/post/JSONResponse.java
+++ b/src/main/java/org/apache/sling/servlets/post/JSONResponse.java
@@ -18,12 +18,19 @@
  */
 package org.apache.sling.servlets.post;
 
+
+import org.apache.sling.servlets.post.impl.JsonTicksConverter;
+
 import javax.json.Json;
 import javax.json.JsonArrayBuilder;
 import javax.json.JsonObject;
 import javax.json.JsonObjectBuilder;
+import javax.json.JsonStructure;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
+import java.io.StringReader;
+import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -51,6 +58,8 @@
 
     private Map<String, Object> json = new HashMap<>();
 
+    private Map<String, JsonStructure> jsonCached = new HashMap<>();
+
     private List<Map<String, Object>> changes = new ArrayList<>();
 
     private Throwable error;
@@ -78,9 +87,31 @@
         return this.error;
     }
 
+    /**
+     * This method accepts values that correspond  to json primitives or otherwise assumes that the toString() of the value
+     * can be parsed as json. If neither is the case it will throw an Exception.
+     *
+     * Assuming the above holds, it will put the value as json directly into the json value part of the response.
+     *
+     * @param name name of the property
+     * @param value value of the property - either of type {String, Boolean, Number, null}
+     *             or the toString() is parseable as json
+     * @throws JSONResponseException if the value is not usable
+     */
     @Override
     public void setProperty(String name, Object value) {
-        json.put(name, value);
+        if (value instanceof String || value instanceof Boolean || value instanceof Number || value == null) {
+            json.put(name, value);
+        }
+        else {
+            try {
+                String valueString = JsonTicksConverter.tickToDoubleQuote(value.toString());
+                jsonCached.put(name, Json.createReader(new StringReader(valueString)).read());
+                json.put(name, value);
+            } catch (Exception ex) {
+                throw new JSONResponseException(ex);
+            }
+        }
     }
 
     @Override
@@ -102,11 +133,42 @@
     JsonObject getJson() {
         JsonObjectBuilder jsonBuilder = Json.createObjectBuilder();
         for (Map.Entry<String, Object> entry : json.entrySet()) {
-            if (entry.getValue() != null) {
-                jsonBuilder.add(entry.getKey(), entry.getValue().toString());
+            Object value = entry.getValue();
+            if (value instanceof String) {
+                jsonBuilder.add(entry.getKey(), (String) entry.getValue());
+            }
+            else if (value instanceof Boolean) {
+                jsonBuilder.add(entry.getKey(), (Boolean) value);
+            }
+            else if (value instanceof BigInteger) {
+                jsonBuilder.add(entry.getKey(), (BigInteger) value);
+            }
+            else if (value instanceof BigDecimal) {
+                jsonBuilder.add(entry.getKey(), (BigDecimal) value);
+            }
+            else if (value instanceof Byte) {
+                jsonBuilder.add(entry.getKey(), (Byte) value);
+            }
+            else if (value instanceof Short) {
+                jsonBuilder.add(entry.getKey(), (Short) value);
+            }
+            else if (value instanceof Integer) {
+                jsonBuilder.add(entry.getKey(), (Integer) value);
+            }
+            else if (value instanceof Long) {
+                jsonBuilder.add(entry.getKey(), (Long) value);
+            }
+            else if (value instanceof Double) {
+                jsonBuilder.add(entry.getKey(), (Double) value);
+            }
+            else if (value instanceof Float) {
+                jsonBuilder.add(entry.getKey(), (Float) value);
+            }
+            else if (value == null) {
+                jsonBuilder.addNull(entry.getKey());
             }
             else {
-                jsonBuilder.addNull(entry.getKey());
+                jsonBuilder.add(entry.getKey(), jsonCached.get(entry.getKey()));
             }
         }
         if (this.error != null) {
diff --git a/src/main/java/org/apache/sling/servlets/post/impl/JsonTicksConverter.java b/src/main/java/org/apache/sling/servlets/post/impl/JsonTicksConverter.java
new file mode 100644
index 0000000..510e64b
--- /dev/null
+++ b/src/main/java/org/apache/sling/servlets/post/impl/JsonTicksConverter.java
@@ -0,0 +1,106 @@
+/*
+ * 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.sling.servlets.post.impl;
+
+/**
+ * Converts JSON with ticks to JSON with quotes.
+ * <p>Conversions:</p>
+ * <ul>
+ * <li>Converts ticks ' to " when used as quotation marks for names or string values</li>
+ * <li>Within names or string values quoted with ticks, ticks have to be escaped with <code>\'</code>.
+ *     This escaping sign is removed on the conversion, because in JSON ticks must not be escaped.</li>
+ * <li>Within names or string values quoted with ticks, double quotes may or may not be escaped.
+ *     After the conversion they are always escaped.</li>
+ * </ul>
+ */
+public final class JsonTicksConverter {
+    
+    private JsonTicksConverter() {
+        // static methods only
+    }
+    
+    public static String tickToDoubleQuote(final String input) {
+        final int len = input.length();
+        final StringBuilder output = new StringBuilder(len);
+        boolean quoted = false;
+        boolean tickQuoted = false;
+        boolean escaped = false;
+        boolean comment = false;
+        char lastChar = ' ';
+        for (int i = 0; i < len; i++) {
+            char in = input.charAt(i);
+            if (quoted || tickQuoted) {
+                if (escaped) {
+                    if (in != '\'') {
+                        output.append("\\");
+                    }
+                    if (in == '\\') {
+                        output.append("\\");
+                    }
+                    escaped = false;
+                }
+                else {
+                    if (in == '"') {
+                        if (quoted) {
+                            quoted = false;
+                        }
+                        else if (tickQuoted) {
+                            output.append("\\");
+                        }
+                    }
+                    else if (in == '\'') {
+                        if (tickQuoted) {
+                            in = '"';
+                            tickQuoted = false;
+                        }
+                    }
+                    else if (in == '\\') {
+                        escaped = true;
+                    }
+                }
+            }
+            else {
+                if (comment) {
+                    if (lastChar == '*' && in == '/') {
+                        comment = false;
+                    }
+                }
+                else {
+                    if (lastChar == '/' && in == '*') {
+                        comment = true;
+                    }
+                    else if (in == '\'') {
+                        in = '"';
+                        tickQuoted = true;
+                    }
+                    else if (in == '"') {
+                        quoted = true;
+                    }
+                }
+            }
+            if (in == '\\') {
+                continue;
+            }
+            output.append(in);
+            lastChar = in;
+        }
+        return output.toString();
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/servlets/post/JsonResponseTest.java b/src/test/java/org/apache/sling/servlets/post/JsonResponseTest.java
index d591628..c2b18ee 100644
--- a/src/test/java/org/apache/sling/servlets/post/JsonResponseTest.java
+++ b/src/test/java/org/apache/sling/servlets/post/JsonResponseTest.java
@@ -24,7 +24,9 @@
 
 import javax.json.Json;
 import javax.json.JsonArray;
+import javax.json.JsonNumber;
 import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
 import javax.json.JsonString;
 import javax.json.JsonValue;
 import javax.servlet.http.HttpServletResponse;
@@ -75,7 +77,7 @@
         MockSlingHttpServletResponse response = new MockSlingHttpServletResponse();
         res.send(response, true);
         JsonObject result = Json.createReader(new StringReader(response.getOutput().toString())).readObject();
-        assertProperty(result, HtmlResponse.PN_STATUS_CODE, Integer.toString(HttpServletResponse.SC_OK));
+        assertProperty(result, HtmlResponse.PN_STATUS_CODE, HttpServletResponse.SC_OK);
         assertEquals(JSONResponse.RESPONSE_CONTENT_TYPE, response.getContentType());
         assertEquals(JSONResponse.RESPONSE_CHARSET, response.getCharacterEncoding());
     }
@@ -88,7 +90,7 @@
         MockResponseWithHeader response = new MockResponseWithHeader();
         res.send(response, true);
         JsonObject result = Json.createReader(new StringReader(response.getOutput().toString())).readObject();
-        assertProperty(result, HtmlResponse.PN_STATUS_CODE, Integer.toString(HttpServletResponse.SC_CREATED));
+        assertProperty(result, HtmlResponse.PN_STATUS_CODE, HttpServletResponse.SC_CREATED);
         assertEquals(location, response.getHeader("Location"));
     }
 
@@ -102,7 +104,7 @@
             MockResponseWithHeader response = new MockResponseWithHeader();
             res.send(response, true);
             JsonObject result = Json.createReader(new StringReader(response.getOutput().toString())).readObject();
-            assertProperty(result, HtmlResponse.PN_STATUS_CODE, Integer.toString(status));
+            assertProperty(result, HtmlResponse.PN_STATUS_CODE, status);
             assertEquals(location, response.getHeader("Location"));
         }
     }
@@ -115,18 +117,40 @@
         assertEquals(0, obj.getJsonArray("changes").size());
     }
 
+    public void testSendWithJsonAsPropertyValue() throws Exception {
+        String testResponseJson = "{\"user\":\"testUser\",\"properties\":{\"id\":\"testId\", \"name\":\"test\"}}";
+        JsonObject customProperty = Json.createReader(new StringReader(testResponseJson)).readObject();
+        res.setProperty("response", customProperty);
+        MockResponseWithHeader response = new MockResponseWithHeader();
+        res.send(response, true);
+        JsonObject result = Json.createReader(new StringReader(response.getOutput().toString())).readObject();
+        assertProperty(result, "response", customProperty);
+    }
+
     private static JsonValue assertProperty(JsonObject obj, String key) {
         assertTrue("JSON object does not have property " + key, obj.containsKey(key));
         return obj.get(key);
     }
 
     @SuppressWarnings({"unchecked"})
-    private static JsonString assertProperty(JsonObject obj, String key, String expected) {
+    private static JsonValue assertProperty(JsonObject obj, String key, int expected) {
+        JsonNumber res = (JsonNumber) assertProperty(obj, key);
+        assertEquals(expected, res.intValue());
+        return res;
+    }
+
+    private static JsonValue assertProperty(JsonObject obj, String key, String expected) {
         JsonString res = (JsonString) assertProperty(obj, key);
         assertEquals(expected, res.getString());
         return res;
     }
 
+    private static JsonValue assertProperty(JsonObject obj, String key, JsonObject expected) {
+        JsonObject res = (JsonObject) assertProperty(obj, key);
+        assertEquals(expected, res);
+        return res;
+    }
+
     @SuppressWarnings({"unchecked"})
     private static <T> T assertInstanceOf(Object obj, Class<T> clazz) {
         try {