Index: src/main/java/org/apache/jackrabbit/oak/commons/json/JsonObject.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/commons/json/JsonObject.java (revision 1869743) +++ src/main/java/org/apache/jackrabbit/oak/commons/json/JsonObject.java (working copy) @@ -17,32 +17,74 @@ package org.apache.jackrabbit.oak.commons.json; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; /** - * Simple JSON Object representation + * Simple JSON Object representation. + * + * It optionally supports respecting the order of properties, and the order children. */ public class JsonObject { - private Map props = new HashMap(); - private Map children = new HashMap(); + private Map props; + private Map children; /** + * Create a Json object that doesn't respect the order. + */ + public JsonObject() { + this(false); + } + + /** + * Create a Json object. + * + * @param respectOrder whether the object should respect the order + */ + public JsonObject(boolean respectOrder) { + props = map(respectOrder); + children = map(respectOrder); + } + + private static Map map(boolean respectOrder) { + return respectOrder ? new LinkedHashMap() : new HashMap(); + } + + /** + * Build a Json object from a String. + * + * @param json the json string + * @param respectOrder whether the object should respect the child order + * @return the json object + */ + public static JsonObject fromJson(String json, boolean respectOrder) { + JsopTokenizer tokenizer = new JsopTokenizer(json); + tokenizer.read('{'); + try { + return create(tokenizer, respectOrder); + } finally { + tokenizer.read(JsopReader.END); + } + } + + /** * Reads a JSON object from the given tokenizer. The opening '{' of the * object should already have been consumed from the tokenizer before * this method is called. * * @param t tokenizer + * @param respectOrder whether the order should be respected * @return JSON object */ - public static JsonObject create(JsopTokenizer t) { - JsonObject obj = new JsonObject(); + public static JsonObject create(JsopTokenizer t, boolean respectOrder) { + JsonObject obj = new JsonObject(respectOrder); if (!t.matches('}')) { do { String key = t.readString(); t.read(':'); if (t.matches('{')) { - obj.children.put(key, create(t)); + obj.children.put(key, create(t, respectOrder)); } else { obj.props.put(key, t.readRawValue().trim()); } @@ -49,21 +91,60 @@ } while (t.matches(',')); t.read('}'); } - return obj; + return obj; } + /** + * Reads a JSON object from the given tokenizer. The opening '{' of the + * object should already have been consumed from the tokenizer before + * this method is called. + * + * @param t tokenizer + * @return JSON object + */ + public static JsonObject create(JsopTokenizer t) { + return create(t, false); + } + + /** + * Write the object to a builder. + * + * @param buf the target + */ public void toJson(JsopBuilder buf) { toJson(buf, this); } + /** + * Get the (mutable) map of properties. + * + * @return the property map + */ public Map getProperties() { return props; } + /** + * Get the (mutable) map of children. + * + * @return the children map + */ public Map getChildren() { return children; } + /** + * Pretty-print the object. + * + * @return the pretty-printed string representation + */ + @Override + public String toString() { + JsopBuilder w = new JsopBuilder(); + toJson(w); + return JsopBuilder.prettyPrint(w.toString()); + } + private static void toJson(JsopBuilder buf, JsonObject obj) { buf.object(); for (String name : obj.props.keySet()) { @@ -75,4 +156,5 @@ } buf.endObject(); } + } \ No newline at end of file Index: src/test/java/org/apache/jackrabbit/oak/commons/json/JsonObjectTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/commons/json/JsonObjectTest.java (nonexistent) +++ src/test/java/org/apache/jackrabbit/oak/commons/json/JsonObjectTest.java (working copy) @@ -0,0 +1,69 @@ +/* + * 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.jackrabbit.oak.commons.json; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +/** + * Tests the JsonObject implementation. + */ +public class JsonObjectTest { + + @Test + public void fromJson() { + JsonObject a = JsonObject.fromJson(" { } ", false); + assertEquals("{}", a.toString()); + JsonObject b = JsonObject.fromJson("{\"az\": 1, \"b\": [2, 3], \"c\": null, \"d\": {}}", true); + assertEquals("{\n" + + " \"az\": 1,\n" + + " \"b\": [2, 3],\n" + + " \"c\": null,\n" + + " \"d\": {}\n" + + "}", b.toString()); + assertEquals("{az=1, b=[2, 3], c=null}", b.getProperties().toString()); + assertEquals("{d={}}", b.getChildren().toString()); + } + + @Test + public void newObjectNotRespectingOrder() { + JsonObject a = new JsonObject(); + a.getProperties().put("az", "1"); + a.getProperties().put("b", "2"); + // we expect it's a HashMap or similar, so hash order + assertEquals("{\n" + + " \"b\": 2,\n" + + " \"az\": 1\n" + + "}", a.toString()); + assertEquals("{b=2, az=1}", a.getProperties().toString()); + } + + @Test + public void newObjectRespectingOrder() { + JsonObject a = new JsonObject(true); + a.getProperties().put("az", "1"); + a.getProperties().put("b", "2"); + // we expect it's a HashMap + assertEquals("{\n" + + " \"az\": 1,\n" + + " \"b\": 2\n" + + "}", a.toString()); + assertEquals("{az=1, b=2}", a.getProperties().toString()); + } + +}