diff --git a/core/src/main/java/org/jclouds/util/Patterns.java b/core/src/main/java/org/jclouds/util/Patterns.java
index b0b5052934..2e9a38da9e 100644
--- a/core/src/main/java/org/jclouds/util/Patterns.java
+++ b/core/src/main/java/org/jclouds/util/Patterns.java
@@ -39,6 +39,7 @@ public class Patterns {
public static final Pattern PATTERN_THAT_BREAKS_URI = Pattern.compile("[a-z0-9]+://.*/.*@.*");
public static final Pattern JSON_STRING_PATTERN = Pattern.compile("^[^\"\\{\\[].*[^\\{\\[\"]$");
public static final Pattern JSON_NUMBER_PATTERN = Pattern.compile("^[0-9]*\\.?[0-9]*$");
+ public static final Pattern JSON_BOOLEAN_PATTERN = Pattern.compile("^(true|false)$");
public static final Pattern PLUS_PATTERN = Pattern.compile("\\+");
public static final Pattern STAR_PATTERN = Pattern.compile("\\*");
public static final Pattern _7E_PATTERN = Pattern.compile("%7E");
diff --git a/core/src/test/java/org/jclouds/json/GsonLiteralTest.java b/core/src/test/java/org/jclouds/json/GsonLiteralTest.java
new file mode 100644
index 0000000000..fcb62bee2a
--- /dev/null
+++ b/core/src/test/java/org/jclouds/json/GsonLiteralTest.java
@@ -0,0 +1,193 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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.jclouds.json;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.testng.Assert.assertEquals;
+
+import java.lang.reflect.Type;
+import java.util.Map;
+
+import org.jclouds.json.internal.JsonLiteral;
+import org.jclouds.util.Patterns;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import com.google.gson.internal.Streams;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonWriter;
+
+/**
+ * Shows how we currently allow users to specify json literal types. Note this requires patches to
+ * {@link Streams} and {@link JsonWriter} in order to register and render JsonLiteral elements.
+ *
+ * @author Adrian Cole
+ * @see
+ * @see JsonLiteral
+ */
+@Test(testName = "GsonLiteralTest")
+public class GsonLiteralTest {
+
+ /**
+ * User supplied type that holds json literally. Ex. number as {@code 8}, boolean as {@code true}
+ * , string as {@code "value"}, object as {@code , list {@code []}.
+ */
+ static class RawJson implements CharSequence {
+
+ private final String value;
+
+ public RawJson(double value) {
+ this.value = value + "";
+ }
+
+ public RawJson(int value) {
+ this.value = value + "";
+ }
+
+ public RawJson(long value) {
+ this.value = value + "";
+ }
+
+ public RawJson(boolean value) {
+ this.value = value + "";
+ }
+
+ public RawJson(String value) {
+ this.value = quoteStringIfNotNumberOrBoolean(checkNotNull(value, "value"));
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(value);
+ }
+
+ @Override
+ public boolean equals(Object that) {
+ if (that == null)
+ return false;
+ return Objects.equal(this.toString(), that.toString());
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+
+ static String quoteStringIfNotNumberOrBoolean(String in) {
+ if (Patterns.JSON_STRING_PATTERN.matcher(in).find() && !Patterns.JSON_NUMBER_PATTERN.matcher(in).find()
+ && !Patterns.JSON_BOOLEAN_PATTERN.matcher(in).find()) {
+ return "\"" + in + "\"";
+ }
+ return in;
+ }
+
+ @Override
+ public char charAt(int index) {
+ return value.charAt(index);
+ }
+
+ @Override
+ public int length() {
+ return value.length();
+ }
+
+ @Override
+ public CharSequence subSequence(int start, int end) {
+ return value.subSequence(start, end);
+ }
+
+ }
+
+ /**
+ * writes or reads the literal directly and without formatting it in any way. Note this only
+ * works as {@link Streams} and {@link JsonWriter} are patched to register and render JsonLiteral
+ * elements.
+ */
+ public static class RawJsonAdapter implements JsonSerializer, JsonDeserializer {
+
+ public JsonElement serialize(RawJson src, Type typeOfSrc, JsonSerializationContext context) {
+ return new JsonLiteral(src);
+ }
+
+ public RawJson deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
+ throws JsonParseException {
+ return new RawJson(json.toString());
+ }
+
+ }
+
+ // register the type adapter so that gson can serialize/deserialize to it
+ private Gson gson = new GsonBuilder().registerTypeAdapter(RawJson.class, new RawJsonAdapter()).create();
+ Type type = new TypeToken