You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@shindig.apache.org by et...@apache.org on 2009/02/12 06:19:38 UTC
svn commit: r743623 - in /incubator/shindig/trunk/java/common/src:
main/java/org/apache/shindig/common/ test/java/org/apache/shindig/common/
Author: etnu
Date: Thu Feb 12 05:19:37 2009
New Revision: 743623
URL: http://svn.apache.org/viewvc?rev=743623&view=rev
Log:
Added pojo support to JsonSerializer. We can't quite replace all usage of ad-hoc json serialization because REST and JSON-RPC disagree about some property names in the social api code.
Added:
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/JsonProperty.java
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/common/JsonAssert.java
Modified:
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/JsonSerializer.java
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/common/JsonSerializerTest.java
Added: incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/JsonProperty.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/JsonProperty.java?rev=743623&view=auto
==============================================================================
--- incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/JsonProperty.java (added)
+++ incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/JsonProperty.java Thu Feb 12 05:19:37 2009
@@ -0,0 +1,34 @@
+/*
+ * 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.shindig.common;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation for specifying a property name other than the default when using JsonSerializer.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface JsonProperty {
+ String value();
+}
Modified: incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/JsonSerializer.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/JsonSerializer.java?rev=743623&r1=743622&r2=743623&view=diff
==============================================================================
--- incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/JsonSerializer.java (original)
+++ incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/JsonSerializer.java Thu Feb 12 05:19:37 2009
@@ -18,13 +18,21 @@
*/
package org.apache.shindig.common;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+
+import org.joda.time.DateTime;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.Collection;
+import java.util.Date;
import java.util.Iterator;
import java.util.Map;
+import java.util.Set;
/**
* Serializes a JSONObject.
@@ -44,8 +52,23 @@
'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
};
+ private static final Set<String> EXCLUDE_METHODS
+ = ImmutableSet.of("getClass", "getDeclaringClass");
+
+ private static final Map<Class<?>, Map<String, Method>> getters = Maps.newConcurrentHashMap();
+
private JsonSerializer() {}
+ public static String serialize(Object object) {
+ StringBuilder buf = new StringBuilder(1024);
+ try {
+ append(buf, object);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return buf.toString();
+ }
+
/**
* Serialize a JSONObject. Does not guard against cyclical references.
*/
@@ -125,26 +148,66 @@
public static void append(Appendable buf, Object value) throws IOException {
if (value == null) {
buf.append("null");
- } else if (value instanceof JSONObject) {
- appendJsonObject(buf, (JSONObject)value);
- } else if (value instanceof String) {
- appendString(buf, (String)value);
- } else if (value instanceof Number || value instanceof Boolean) {
+ } else if (value instanceof Number ||
+ value instanceof Boolean) {
+ // Primitives
buf.append(value.toString());
+ } else if (value instanceof CharSequence ||
+ value instanceof DateTime ||
+ value instanceof Date ||
+ value.getClass().isEnum()) {
+ // String-like Primitives
+ appendString(buf, value.toString());
+ } else if (value instanceof JSONObject) {
+ appendJsonObject(buf, (JSONObject) value);
} else if (value instanceof JSONArray) {
buf.append(value.toString());
} else if (value instanceof Map) {
- appendMap(buf, (Map<String, Object>)value);
+ appendMap(buf, (Map<String, Object>) value);
} else if (value instanceof Collection) {
- appendCollection(buf, (Collection<Object>)value);
+ appendCollection(buf, (Collection<Object>) value);
} else if (value.getClass().isArray()) {
- appendArray(buf, (Object[])value);
+ appendArray(buf, (Object[]) value);
} else {
- appendString(buf, value.toString());
+ // Try getter conversion
+ appendPojo(buf, value);
}
}
/**
+ * Appends a java object using getters
+ *
+ * @throws IOException If {@link Appendable#append(char)} throws an exception.
+ */
+ public static void appendPojo(Appendable buf, Object pojo) throws IOException {
+ Map<String, Method> methods = getGetters(pojo);
+ buf.append('{');
+ boolean firstDone = false;
+ for (Map.Entry<String, Method> entry : methods.entrySet()) {
+ if (firstDone) {
+ buf.append(',');
+ } else {
+ firstDone = true;
+ }
+ appendString(buf, entry.getKey());
+ buf.append(':');
+ try {
+ append(buf, entry.getValue().invoke(pojo));
+ } catch (IllegalArgumentException e) {
+ // Shouldn't be possible.
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ // Bad class.
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ // Bad class.
+ throw new RuntimeException(e);
+ }
+ }
+ buf.append('}');
+ }
+
+ /**
* Appends an array to the buffer.
*
* @throws IOException If {@link Appendable#append(char)} throws an exception.
@@ -337,4 +400,38 @@
}
buf.append('"');
}
+
+ private static Map<String, Method> getGetters(Object pojo) {
+ Class<?> clazz = pojo.getClass();
+
+ Map<String, Method> methods = getters.get(clazz);
+ if (methods != null) {
+ return methods;
+ }
+ // Ensure consistent method ordering by using a linked hash map.
+ methods = Maps.newHashMap();
+
+ for (Method method : clazz.getMethods()) {
+ String name = getPropertyName(method);
+ if (name != null) {
+ methods.put(name, method);
+ }
+ }
+
+ getters.put(clazz, methods);
+ return methods;
+ }
+
+ private static String getPropertyName(Method method) {
+ JsonProperty property = method.getAnnotation(JsonProperty.class);
+ if (property == null) {
+ String name = method.getName();
+ if (name.startsWith("get") && (!EXCLUDE_METHODS.contains(name))) {
+ return name.substring(3, 4).toLowerCase() + name.substring(4);
+ }
+ return null;
+ } else {
+ return property.value();
+ }
+ }
}
Added: incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/common/JsonAssert.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/common/JsonAssert.java?rev=743623&view=auto
==============================================================================
--- incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/common/JsonAssert.java (added)
+++ incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/common/JsonAssert.java Thu Feb 12 05:19:37 2009
@@ -0,0 +1,86 @@
+/*
+ * 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.shindig.common;
+
+import static org.junit.Assert.assertEquals;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+public final class JsonAssert {
+ private JsonAssert() {}
+
+ public static void assertJsonArrayEquals(JSONArray left, JSONArray right) throws Exception {
+ if (left.length() != right.length()) {
+ assertEquals("Arrays are not of equal length", left.toString(), right.toString());
+ }
+
+ for (int i = 0; i < left.length(); ++i) {
+ Object leftValue = left.opt(i);
+ Object rightValue = right.opt(i);
+
+ assertEquals(left.toString() + " != " + right.toString(),
+ leftValue.getClass(), rightValue.getClass());
+
+ if (leftValue instanceof JSONObject) {
+ assertJsonObjectEquals((JSONObject) leftValue, (JSONObject) rightValue);
+ } else if (leftValue instanceof JSONArray) {
+ assertJsonArrayEquals((JSONArray) leftValue, (JSONArray) rightValue);
+ } else {
+ assertEquals(leftValue, rightValue);
+ }
+ }
+ }
+
+ public static void assertJsonObjectEquals(JSONObject left, JSONObject right) throws Exception {
+ if (left.length() != right.length()) {
+ assertEquals("Objects are not of equal size", left.toString(2), right.toString(2));
+ }
+
+ for (String name : JSONObject.getNames(left)) {
+ Object leftValue = left.opt(name);
+ Object rightValue = right.opt(name);
+
+ assertEquals(left.toString() + " != " + right.toString(),
+ leftValue.getClass(), rightValue.getClass());
+
+ if (leftValue instanceof JSONObject) {
+ assertJsonObjectEquals((JSONObject) leftValue, (JSONObject) rightValue);
+ } else if (leftValue instanceof JSONArray) {
+ assertJsonArrayEquals((JSONArray) leftValue, (JSONArray) rightValue);
+ } else {
+ assertEquals(leftValue, rightValue);
+ }
+ }
+ }
+
+ public static void assertJsonEquals(String left, String right) throws Exception {
+ switch (left.charAt(0)) {
+ case '{':
+ assertJsonObjectEquals(new JSONObject(left), new JSONObject(right));
+ break;
+ case '[':
+ assertJsonArrayEquals(new JSONArray(left), new JSONArray(right));
+ break;
+ default:
+ assertEquals(left, right);
+ break;
+ }
+ }
+}
Modified: incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/common/JsonSerializerTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/common/JsonSerializerTest.java?rev=743623&r1=743622&r2=743623&view=diff
==============================================================================
--- incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/common/JsonSerializerTest.java (original)
+++ incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/common/JsonSerializerTest.java Thu Feb 12 05:19:37 2009
@@ -18,17 +18,18 @@
*/
package org.apache.shindig.common;
+import static org.apache.shindig.common.JsonAssert.assertJsonEquals;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import com.google.common.collect.Maps;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
import org.apache.commons.lang.StringUtils;
import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.Test;
+import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
@@ -44,58 +45,100 @@
@Test
public void serializeSimpleJsonObject() throws Exception {
- JSONObject json = new JSONObject("{\"foo\":\"bar\"}");
- assertTrue("Did not produce results matching reference implementation.",
- jsonEquals(json.toString(), JsonSerializer.serialize(json)));
+ String json = "{foo:'bar'}";
+ assertJsonEquals(json, JsonSerializer.serialize(new JSONObject(json)));
}
@Test
public void serializeSimpleMap() throws Exception {
Map<String, String> map = ImmutableMap.of("hello", "world", "foo", "bar");
- assertTrue("Did not produce results matching reference implementation.",
- jsonEquals(new JSONObject(map).toString(), JsonSerializer.serialize(map)));
+ assertJsonEquals("{hello:'world',foo:'bar'}", JsonSerializer.serialize(map));
}
@Test
public void serializeSimpleCollection() throws Exception {
Collection<String> collection = Arrays.asList("foo", "bar", "baz");
- assertEquals("[\"foo\",\"bar\",\"baz\"]", JsonSerializer.serialize(collection));
+ assertJsonEquals("['foo','bar','baz']", JsonSerializer.serialize(collection));
}
@Test
public void serializeArray() throws Exception {
String[] array = new String[] {"foo", "bar", "baz"};
- assertEquals("[\"foo\",\"bar\",\"baz\"]", JsonSerializer.serialize(array));
+ assertJsonEquals("['foo','bar','baz']", JsonSerializer.serialize(array));
}
@Test
public void serializeJsonArray() throws Exception {
JSONArray array = new JSONArray(new String[] {"foo", "bar", "baz"});
- assertEquals("[\"foo\",\"bar\",\"baz\"]", JsonSerializer.serialize(array));
+ assertJsonEquals("['foo','bar','baz']", JsonSerializer.serialize(array));
+ }
+
+ @Test
+ public void serializePrimitives() throws Exception {
+ assertEquals("\"hello\"", JsonSerializer.serialize("hello"));
+ assertEquals("100", JsonSerializer.serialize(100));
+ assertEquals("125.0", JsonSerializer.serialize(125.0f));
+ assertEquals("126.0", JsonSerializer.serialize(126.0));
+ assertEquals("1", JsonSerializer.serialize(1L));
+ assertEquals("\"RUNTIME\"", JsonSerializer.serialize(RetentionPolicy.RUNTIME));
+ assertEquals("\"string buf\"",
+ JsonSerializer.serialize(new StringBuilder().append("string").append(' ').append("buf")));
+ }
+
+ public static class JsonPojo {
+ public String getString() {
+ return "string-value";
+ }
+
+ @SuppressWarnings("unused")
+ private String getPrivateString() {
+ throw new UnsupportedOperationException();
+ }
+
+ public int getInteger() {
+ return 100;
+ }
+
+ @JsonProperty("simple!")
+ public int getSimpleName() {
+ return 3;
+ }
+
+ }
+
+ @Test
+ public void serializePojo() throws Exception {
+ JsonPojo pojo = new JsonPojo();
+
+ assertJsonEquals("{string:'string-value',integer:100,'simple!':3}",
+ JsonSerializer.serialize(pojo));
}
@Test
public void serializeMixedObjects() throws Exception {
Map<String, ?> map = ImmutableMap.of(
- "integer", Integer.valueOf(100),
- "double", Double.valueOf(233333333333.7d),
- "boolean", Boolean.TRUE,
+ "int", Integer.valueOf(3),
+ "double", Double.valueOf(2.7d),
+ "bool", Boolean.TRUE,
"map", ImmutableMap.of("hello", "world", "foo", "bar"),
"string", "hello!");
- assertTrue("Did not produce results matching reference implementation.",
- jsonEquals(new JSONObject(map).toString(), JsonSerializer.serialize(map)));
+ assertJsonEquals(
+ "{int:3,double:2.7,bool:true,map:{hello:'world',foo:'bar'},string:'hello!'}",
+ JsonSerializer.serialize(map));
}
@Test
public void serializeMixedArray() throws Exception {
Collection<Object> data = Arrays.asList(
- "integer", Integer.valueOf(100),
- "double", Double.valueOf(233333333333.7d),
- "boolean", Boolean.TRUE,
+ Integer.valueOf(3),
+ Double.valueOf(2.7d),
+ Boolean.TRUE,
Arrays.asList("one", "two", "three"),
new JSONArray(new String[] {"foo", "bar"}),
- "string", "hello!");
- assertEquals(new JSONArray(data).toString(), JsonSerializer.serialize(data));
+ "hello!");
+ assertJsonEquals(
+ "[3,2.7,true,['one','two','three'],['foo','bar'],'hello!']",
+ JsonSerializer.serialize(data));
}
@Test
@@ -199,37 +242,6 @@
return data;
}
- private static boolean jsonEquals(JSONObject left, JSONObject right) {
- if (left.length() != right.length()) {
- return false;
- }
- for (String name : JSONObject.getNames(left)) {
- Object leftValue = left.opt(name);
- Object rightValue = right.opt(name);
- if (leftValue instanceof JSONObject) {
- if (!jsonEquals((JSONObject)leftValue, (JSONObject)rightValue)) {
- return false;
- }
- } else if (leftValue instanceof JSONArray) {
- JSONArray leftArray = (JSONArray)leftValue;
- JSONArray rightArray = (JSONArray)rightValue;
- for (int i = 0; i < leftArray.length(); ++i) {
- if (!(leftArray.opt(i).equals(rightArray.opt(i)))) {
- return false;
- }
- }
- } else if (!leftValue.equals(rightValue)) {
- System.out.println("Not a match: " + leftValue + " != " + rightValue);
- return false;
- }
- }
- return true;
- }
-
- private static boolean jsonEquals(String reference, String comparison) throws Exception {
- return jsonEquals(new JSONObject(reference), new JSONObject(comparison));
- }
-
@SuppressWarnings("unchecked")
public static void main(String[] args) throws Exception {
int iterations = args.length > 0 ? Integer.parseInt(args[0]) : 1000;
@@ -240,13 +252,8 @@
Map<String, Object> data = (Map<String, Object>)method.invoke(null);
System.out.println("Running: " + method.getName());
- String jsonOrg = runJsonOrgTest(data, iterations);
- String serializer = runSerializerTest(data, iterations);
- // String netSfJson = runNetSfJsonTest(data, iterations);
-
- if (!jsonEquals(jsonOrg, serializer)) {
- System.out.println("Serializer did not produce results matching the reference impl.");
- }
+ runJsonOrgTest(data, iterations);
+ runSerializerTest(data, iterations);
// if (!jsonEquals(jsonOrg, netSfJson)) {
// System.out.println("net.sf.json did not produce results matching the reference impl.");