Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #4 from kohsuke/master

Support canonical write method
  • Loading branch information...
commit 9359d24689067404d9ac89b3289334fb13cd8b09 2 parents 2e4b413 + 35a1f2a
@aalmiray authored
View
52 src/main/java/net/sf/json/AbstractJSON.java
@@ -16,10 +16,14 @@
package net.sf.json;
+import java.io.IOException;
+import java.io.Writer;
import java.lang.ref.SoftReference;
+import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
+import java.util.TreeSet;
import net.sf.json.util.JSONUtils;
import net.sf.json.util.JsonEventListener;
@@ -32,7 +36,7 @@
*
* @author Andres Almiray <aalmiray@users.sourceforge.net>
*/
-abstract class AbstractJSON {
+abstract class AbstractJSON implements JSON {
private static class CycleSet extends ThreadLocal {
protected Object initialValue() {
return new SoftReference(new HashSet());
@@ -283,4 +287,50 @@ protected Object _processValue( Object value, JsonConfig jsonConfig ) {
private static Set getCycleSet() {
return cycleSet.getSet();
}
+
+ public final Writer write(Writer writer) throws IOException {
+ write(writer,NORMAL);
+ return writer;
+ }
+
+ public final Writer writeCanonical(Writer writer) throws IOException {
+ write(writer,CANONICAL);
+ return writer;
+ }
+
+ protected abstract void write(Writer w, WritingVisitor v) throws IOException;
+
+ interface WritingVisitor {
+ Collection keySet(JSONObject o);
+ void on(JSON o, Writer w) throws IOException;
+ void on(Object value, Writer w) throws IOException;
+ }
+
+ private static final WritingVisitor NORMAL = new WritingVisitor() {
+ public Collection keySet(JSONObject o) {
+ return o.keySet();
+ }
+
+ public void on(JSON o, Writer w) throws IOException {
+ o.write(w);
+ }
+
+ public void on(Object value, Writer w) throws IOException {
+ w.write(JSONUtils.valueToString(value));
+ }
+ };
+
+ private static final WritingVisitor CANONICAL = new WritingVisitor() {
+ public Collection keySet(JSONObject o) {
+ return new TreeSet(o.keySet()); // sort them alphabetically
+ }
+
+ public void on(JSON o, Writer w) throws IOException {
+ o.writeCanonical(w);
+ }
+
+ public void on(Object value, Writer w) throws IOException {
+ w.write(JSONUtils.valueToCanonicalString(value));
+ }
+ };
}
View
8 src/main/java/net/sf/json/JSON.java
@@ -15,6 +15,7 @@
*/
package net.sf.json;
+import java.io.IOException;
import java.io.Writer;
import java.io.Serializable;
@@ -85,5 +86,10 @@
* @return The writer.
* @throws JSONException
*/
- Writer write( Writer writer );
+ Writer write( Writer writer ) throws IOException;
+
+ /**
+ * Writes the canonicalized form of this JSON object.
+ */
+ Writer writeCanonical(Writer w) throws IOException;
}
View
20 src/main/java/net/sf/json/JSONArray.java
@@ -2281,16 +2281,7 @@ public String toString( int indentFactor, int indent ) {
return sb.toString();
}
- /**
- * Write the contents of the JSONArray as JSON text to a writer. For
- * compactness, no whitespace is added.
- * <p>
- * Warning: This method assumes that the data structure is acyclical.
- *
- * @return The writer.
- * @throws JSONException
- */
- public Writer write( Writer writer ) {
+ protected void write(Writer writer, WritingVisitor visitor) throws IOException {
try{
boolean b = false;
int len = size();
@@ -2302,17 +2293,14 @@ public Writer write( Writer writer ) {
writer.write( ',' );
}
Object v = this.elements.get( i );
- if( v instanceof JSONObject ){
- ((JSONObject) v).write( writer );
- }else if( v instanceof JSONArray ){
- ((JSONArray) v).write( writer );
+ if( v instanceof JSON ){
+ visitor.on( (JSON) v, writer );
}else{
- writer.write( JSONUtils.valueToString( v ) );
+ visitor.on( v, writer );
}
b = true;
}
writer.write( ']' );
- return writer;
}catch( IOException e ){
throw new JSONException( e );
}
View
14 src/main/java/net/sf/json/JSONNull.java
@@ -95,12 +95,12 @@ public String toString( int indentFactor, int indent ) {
return sb.toString();
}
- public Writer write( Writer writer ) {
- try{
- writer.write( toString() );
- return writer;
- }catch( IOException e ){
- throw new JSONException( e );
- }
+ public Writer write( Writer writer ) throws IOException {
+ writer.write( toString() );
+ return writer;
}
+
+ public Writer writeCanonical(Writer w) throws IOException {
+ return write(w);
+ }
}
View
51 src/main/java/net/sf/json/JSONObject.java
@@ -2510,40 +2510,31 @@ public Collection values() {
* @return The writer.
* @throws JSONException
*/
- public Writer write( Writer writer ) {
- try{
- if( isNullObject() ){
- writer.write( JSONNull.getInstance()
- .toString() );
- return writer;
- }
+ protected void write(Writer writer, WritingVisitor visitor) throws IOException {
+ if( isNullObject() ){
+ writer.write(JSONNull.getInstance().toString());
+ }
- boolean b = false;
- Iterator keys = keys();
- writer.write( '{' );
+ boolean b = false;
+ Iterator keys = visitor.keySet(this).iterator();
+ writer.write('{');
- while( keys.hasNext() ){
- if( b ){
- writer.write( ',' );
- }
- Object k = keys.next();
- writer.write( JSONUtils.quote( k.toString() ) );
- writer.write( ':' );
- Object v = this.properties.get( k );
- if( v instanceof JSONObject ){
- ((JSONObject) v).write( writer );
- }else if( v instanceof JSONArray ){
- ((JSONArray) v).write( writer );
- }else{
- writer.write( JSONUtils.valueToString( v ) );
- }
- b = true;
+ while( keys.hasNext() ){
+ if( b ){
+ writer.write(',');
}
- writer.write( '}' );
- return writer;
- }catch( IOException e ){
- throw new JSONException( e );
+ Object k = keys.next();
+ writer.write(JSONUtils.quote(k.toString()));
+ writer.write(':');
+ Object v = this.properties.get(k);
+ if( v instanceof JSON ){
+ visitor.on((JSON) v, writer);
+ }else{
+ visitor.on(v, writer);
+ }
+ b = true;
}
+ writer.write('}');
}
private JSONObject _accumulate( String key, Object value, JsonConfig jsonConfig ) {
View
68 src/main/java/net/sf/json/util/JSONUtils.java
@@ -548,7 +548,41 @@ public static String quote( String string ) {
return sb.toString();
}
- /**
+ /**
+ * Minimal escape form.
+ */
+ public static String quoteCanonical(String s) {
+ if (s == null || s.length() == 0) {
+ return "\"\"";
+ }
+
+ int len = s.length();
+ StringBuilder sb = new StringBuilder(len + 4);
+
+ sb.append('"');
+ for (int i = 0; i < len; i += 1) {
+ char c = s.charAt(i);
+ switch (c) {
+ case '\\':
+ case '"':
+ sb.append('\\');
+ sb.append(c);
+ break;
+ default:
+ if (c < ' ') {
+ String t = "000" + Integer.toHexString(c);
+ sb.append("\\u")
+ .append(t.substring(t.length() - 4));
+ } else {
+ sb.append(c);
+ }
+ }
+ }
+ sb.append('"');
+ return sb.toString();
+ }
+
+ /**
* Strips any single-quotes or double-quotes from both sides of the string.
*/
public static String stripQuotes( String input ) {
@@ -653,16 +687,7 @@ public static String valueToString( Object value ) {
return ((JSONFunction) value).toString();
}
if( value instanceof JSONString ){
- Object o;
- try{
- o = ((JSONString) value).toJSONString();
- }catch( Exception e ){
- throw new JSONException( e );
- }
- if( o instanceof String ){
- return (String) o;
- }
- throw new JSONException( "Bad value from toJSONString: " + o );
+ return ((JSONString) value).toJSONString();
}
if( value instanceof Number ){
return numberToString( (Number) value );
@@ -673,6 +698,25 @@ public static String valueToString( Object value ) {
return quote( value.toString() );
}
+ public static String valueToCanonicalString( Object value ) {
+ if( value == null || isNull( value ) ){
+ return "null";
+ }
+ if( value instanceof JSONFunction ){
+ return value.toString(); // there's really no canonical form for functions
+ }
+ if( value instanceof JSONString ){
+ return ((JSONString) value).toJSONString();
+ }
+ if( value instanceof Number ){
+ return numberToString( (Number) value ).toLowerCase();
+ }
+ if( value instanceof Boolean || value instanceof JSONObject || value instanceof JSONArray ){
+ return value.toString();
+ }
+ return quoteCanonical(value.toString());
+ }
+
/**
* Make a prettyprinted JSON text of an object value.
* <p>
@@ -692,7 +736,7 @@ public static String valueToString( Object value, int indentFactor, int indent )
return "null";
}
if( value instanceof JSONFunction ){
- return ((JSONFunction) value).toString();
+ return value.toString();
}
if( value instanceof JSONString ){
return ((JSONString) value).toJSONString();
View
90 src/main/jdk15/net/sf/json/JSONArray.java
@@ -91,7 +91,7 @@
*
* @author JSON.org
*/
-public final class JSONArray extends AbstractJSON implements JSON, List, Comparable {
+public final class JSONArray extends AbstractJSON implements JSON, List<Object>, Comparable {
/**
* Creates a JSONArray.<br>
* Inspects the object type to call the correct JSONArray factory method.
@@ -791,7 +791,7 @@ private static JSONArray _fromArray( double[] array, JsonConfig jsonConfig ) {
JSONArray jsonArray = new JSONArray();
try{
for( int i = 0; i < array.length; i++ ){
- Double d = new Double( array[i] );
+ Double d = array[i];
JSONUtils.testValidity( d );
jsonArray.addValue( d, jsonConfig );
fireElementAddedEvent( i, d, jsonConfig );
@@ -871,7 +871,7 @@ private static JSONArray _fromArray( float[] array, JsonConfig jsonConfig ) {
JSONArray jsonArray = new JSONArray();
try{
for( int i = 0; i < array.length; i++ ){
- Float f = new Float( array[i] );
+ Float f = array[i];
JSONUtils.testValidity( f );
jsonArray.addValue( f, jsonConfig );
fireElementAddedEvent( i, f, jsonConfig );
@@ -1199,11 +1199,11 @@ private static JSONArray _fromString( String string, JsonConfig jsonConfig ) {
private static void processArrayDimensions( JSONArray jsonArray, List dims, int index ) {
if( dims.size() <= index ){
- dims.add( new Integer( jsonArray.size() ) );
+ dims.add(jsonArray.size());
}else{
int i = ((Integer) dims.get( index )).intValue();
if( jsonArray.size() > i ){
- dims.set( index, new Integer( jsonArray.size() ) );
+ dims.set( index, jsonArray.size());
}
}
for( Iterator i = jsonArray.iterator(); i.hasNext(); ){
@@ -1219,7 +1219,7 @@ private static void processArrayDimensions( JSONArray jsonArray, List dims, int
/**
* The List where the JSONArray's properties are kept.
*/
- private List elements;
+ private List<Object> elements;
/**
* A flag for XML processing.
@@ -1230,7 +1230,7 @@ private static void processArrayDimensions( JSONArray jsonArray, List dims, int
* Construct an empty JSONArray.
*/
public JSONArray() {
- this.elements = new ArrayList();
+ this.elements = new ArrayList<Object>();
}
public void add( int index, Object value ) {
@@ -1258,9 +1258,9 @@ public boolean addAll( Collection collection, JsonConfig jsonConfig ) {
if( collection == null || collection.size() == 0 ){
return false;
}
- for( Iterator i = collection.iterator(); i.hasNext(); ){
- element( i.next(), jsonConfig );
- }
+ for (Object a : collection) {
+ element(a, jsonConfig);
+ }
return true;
}
@@ -1273,9 +1273,9 @@ public boolean addAll( int index, Collection collection, JsonConfig jsonConfig )
return false;
}
int offset = 0;
- for( Iterator i = collection.iterator(); i.hasNext(); ){
- this.elements.add( index + (offset++), processValue( i.next(), jsonConfig ) );
- }
+ for (Object a : collection) {
+ this.elements.add(index + (offset++), processValue(a, jsonConfig));
+ }
return true;
}
@@ -1382,7 +1382,7 @@ public JSONArray element( Collection value, JsonConfig jsonConfig ) {
* @return this.
*/
public JSONArray element( double value ) {
- Double d = new Double( value );
+ Double d = value;
JSONUtils.testValidity( d );
return element( d );
}
@@ -1670,7 +1670,7 @@ public JSONArray element( JSONObject value ) {
* @return this.
*/
public JSONArray element( long value ) {
- return element( JSONUtils.transformNumber( new Long( value ) ) );
+ return element( JSONUtils.transformNumber(value) );
}
/**
@@ -2441,44 +2441,28 @@ public String toString( int indentFactor, int indent ) {
return sb.toString();
}
- /**
- * Write the contents of the JSONArray as JSON text to a writer. For
- * compactness, no whitespace is added.
- * <p>
- * Warning: This method assumes that the data structure is acyclical.
- *
- * @return The writer.
- * @throws JSONException
- */
- public Writer write( Writer writer ) {
- try{
- boolean b = false;
- int len = size();
-
- writer.write( '[' );
-
- for( int i = 0; i < len; i += 1 ){
- if( b ){
- writer.write( ',' );
- }
- Object v = this.elements.get( i );
- if( v instanceof JSONObject ){
- ((JSONObject) v).write( writer );
- }else if( v instanceof JSONArray ){
- ((JSONArray) v).write( writer );
- }else{
- writer.write( JSONUtils.valueToString( v ) );
- }
- b = true;
- }
- writer.write( ']' );
- return writer;
- }catch( IOException e ){
- throw new JSONException( e );
- }
- }
-
- /**
+ protected void write(Writer writer, WritingVisitor visitor) throws IOException {
+ boolean b = false;
+ int len = size();
+
+ writer.write( '[' );
+
+ for( int i = 0; i < len; i += 1 ){
+ if( b ){
+ writer.write( ',' );
+ }
+ Object v = this.elements.get( i );
+ if( v instanceof JSON ){
+ visitor.on((JSON)v,writer);
+ }else{
+ visitor.on(v,writer);
+ }
+ b = true;
+ }
+ writer.write( ']' );
+ }
+
+ /**
* Adds a String without performing any conversion on it.
*/
protected JSONArray addString( String str ) {
View
25 src/main/jdk15/net/sf/json/JSONObject.java
@@ -34,6 +34,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.TreeMap;
import net.sf.ezmorph.Morpher;
import net.sf.ezmorph.array.ObjectArrayMorpher;
@@ -116,7 +117,7 @@
*
* @author JSON.org
*/
-public final class JSONObject extends AbstractJSON implements JSON, Map, Comparable {
+public final class JSONObject extends AbstractJSON implements JSON, Map<String,Object>, Comparable {
private static final Log log = LogFactory.getLog( JSONObject.class );
@@ -526,9 +527,9 @@ public static Object toBean( JSONObject jsonObject, Object root, JsonConfig json
.morph( Array.newInstance( innerType, 0 )
.getClass(), array );
}else if( !array.getClass()
- .equals( pd.getPropertyType() ) ){
+ .equals(pd.getPropertyType()) ){
if( !pd.getPropertyType()
- .equals( Object.class ) ){
+ .equals(Object.class) ){
Morpher morpher = JSONUtils.getMorpherRegistry()
.getMorpherFor( Array.newInstance( innerType, 0 )
.getClass() );
@@ -2372,7 +2373,7 @@ public String optString( String key, String defaultValue ) {
return o != null ? o.toString() : defaultValue;
}
- public Object put( Object key, Object value ) {
+ public Object put( String key, Object value ) {
if( key == null ){
throw new IllegalArgumentException( "key is null." );
}
@@ -2590,16 +2591,15 @@ public Collection values() {
* @return The writer.
* @throws JSONException
*/
- public Writer write( Writer writer ) {
+ protected void write(Writer writer, WritingVisitor visitor) throws IOException {
try{
if( isNullObject() ){
writer.write( JSONNull.getInstance()
- .toString() );
- return writer;
+ .toString());
}
boolean b = false;
- Iterator keys = keys();
+ Iterator keys = visitor.keySet(this).iterator();
writer.write( '{' );
while( keys.hasNext() ){
@@ -2610,17 +2610,14 @@ public Writer write( Writer writer ) {
writer.write( JSONUtils.quote( k.toString() ) );
writer.write( ':' );
Object v = this.properties.get( k );
- if( v instanceof JSONObject ){
- ((JSONObject) v).write( writer );
- }else if( v instanceof JSONArray ){
- ((JSONArray) v).write( writer );
+ if( v instanceof JSON ){
+ visitor.on( (JSON) v, writer );
}else{
- writer.write( JSONUtils.valueToString( v ) );
+ visitor.on( v, writer );
}
b = true;
}
writer.write( '}' );
- return writer;
}catch( IOException e ){
throw new JSONException( e );
}
View
2  src/test/java/net/sf/json/AbstractJSONTest.java
@@ -52,7 +52,7 @@ public void testToString_indentFactor_indent() {
assertEquals( expected, json.toString( getIndentFactor(), getIndent() ) );
}
- public void testWrite() {
+ public void testWrite() throws Exception {
StringWriter w = new StringWriter();
String expected = (String) getWriteExpectations()[0];
JSON json = (JSON) getWriteExpectations()[1];
View
3  src/test/java/net/sf/json/TestJSONArray.java
@@ -16,6 +16,7 @@
package net.sf.json;
import java.io.StringWriter;
+import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
@@ -1442,7 +1443,7 @@ public void testToList_String_multi() {
Assertions.assertEquals( expected, actual );
}
- public void testWrite() {
+ public void testWrite() throws IOException {
JSONArray jsonArray = JSONArray.fromObject( "[[],{},1,true,\"json\"]" );
StringWriter sw = new StringWriter();
jsonArray.write( sw );
View
21 src/test/java/net/sf/json/TestJSONObject.java
@@ -17,6 +17,8 @@
package net.sf.json;
import java.io.Serializable;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
@@ -1638,4 +1640,23 @@ public String processPropertyName( Class beanClass, String name ) {
public void test_fromJSONObject() {
}
+
+ public void testCanonicalWrite() throws Exception {
+ JSONArray a = new JSONArray();
+ a.add(true);
+// a.add(null);
+ a.add(1);
+ a.add(5.3);
+ JSONObject o = new JSONObject();
+ o.put("key1","1");
+ o.put("key2","2");
+ o.put("key3","3");
+ o.put("string","123\r\n\b\t\f\\\\u65E5\\u672C\\u8A9E");
+ a.add(o);
+
+ StringWriter sw = new StringWriter();
+ a.writeCanonical(sw);
+ System.out.println(sw.toString());
+ assertEquals(sw.toString(),"[true,1,5.3,{\"key1\":\"1\",\"key2\":\"2\",\"key3\":\"3\",\"string\":\"123\\u000d\\u000a\\u0008\\u0009\\u000c\\\\\\\\u65E5\\\\u672C\\\\u8A9E\"}]");
+ }
}
Please sign in to comment.
Something went wrong with that request. Please try again.