I recently ran into the problem of needing to store a JSON object in a key, value store. I pasted a little utility class that does the job below. Here’s an example run against some nasty JSON,
ORIGINAL:
{ string: 'aString', integer: -1, nested: { x: 1, y: 2, z: 3}, moreNested: { a: [1,2,3], b: [4,5,6], c: [7,8,9]}, arrayFromHell: [{ innerArray: [{ innerInnerArray: [1,2,3]}]}]}
ENCODED:
{"arrayFromHell.0.innerArray.0.innerInnerArray.0":1,"arrayFromHell.0.innerArray.0.innerInnerArray.1":2,"arrayFromHell.0.innerArray.0.innerInnerArray.2":3,"nested.z":3,"nested.y":2,"nested.x":1,"integer":-1,"string":"aString","moreNested.b.0":4,"moreNested.b.1":5,"moreNested.b.2":6,"moreNested.c.0":7,"moreNested.c.1":8,"moreNested.c.2":9,"moreNested.a.0":1,"moreNested.a.1":2,"moreNested.a.2":3}
DECODED:
{"nested":{"z":3,"y":2,"x":1},"arrayFromHell":[{"innerArray":[{"innerInnerArray":[1,2,3]}]}],"integer":-1,"string":"aString","moreNested":{"b":[4,5,6],"c":[7,8,9],"a":[1,2,3]}}
As you can see, it simply looks at every leaf value (string, int) and derives the key path and stores the value there. For example,
{ x: { y: { z: 1 } } }
becomes,
{ x.y.z: 1 }
Arrays are a special case. Each value in an array becomes the path so far to the array appended with the array index. For example,
{ anArray: [1,2,3] }
becomes,
{ anArray.0: 1, anArray.1: 2, anArray.2: 3 }
package org.test.flatjson;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public class JsonFlattener {
public static String encode(JSONObject jo) throws JSONException {
String s = "{" + encode(null, jo) + "}";
return s;
}
public static String encode(String json) throws JSONException {
JSONObject jo = new JSONObject(json);
return encode(jo);
}
private static String encode(String parent, Object val)
throws JSONException {
StringBuilder sb = new StringBuilder();
if (val instanceof JSONObject) {
JSONObject jo = (JSONObject) val;
for (Iterator<String> i = jo.keys(); i.hasNext();) {
String key = i.next();
String hkey = (parent == null) ? key : parent + "." + key;
Object jval = jo.get(key);
String json = encode(hkey, jval);
sb.append(json);
if (i.hasNext()) {
sb.append(",");
}
}
} else if (val instanceof JSONArray) {
JSONArray ja = (JSONArray) val;
for (int i = 0; i < ja.length(); i++) {
String hkey = (parent == null) ? "" + i : parent + "." + i;
Object aval = ja.get(i);
String json = encode(hkey, aval);
sb.append(json);
if (i < ja.length() - 1) {
sb.append(",");
}
}
} else if (val instanceof String) {
sb.append("\"").append(parent).append("\"").append(":");
String s = (String) val;
sb.append(JSONObject.quote(s));
} else if (val instanceof Integer) {
sb.append("\"").append(parent).append("\"").append(":");
Integer integer = (Integer) val;
sb.append(integer);
}
return sb.toString();
}
public static String decode(String flatJson) throws JSONException {
JSONObject encoded = new JSONObject(flatJson);
return decodeToString(encoded);
}
public static String decodeToString(JSONObject encoded)
throws JSONException {
return decodeToObject(encoded).toString();
}
public static JSONObject decodeToObject(JSONObject encoded)
throws JSONException {
JSONObject decoded = new JSONObject();
for (Iterator<String> i = encoded.keys(); i.hasNext();) {
String hkey = i.next();
String[] keys = hkey.split("\\.");
Object json = decoded;
for (int j = 0; j < keys.length; j++) {
if (j == keys.length - 1) {
Object val = encoded.get(hkey);
if (json instanceof JSONObject) {
JSONObject jo = (JSONObject)json;
jo.put(keys[j], val);
} else if (json instanceof JSONArray) {
JSONArray ja = (JSONArray)json;
int index = Integer.parseInt(keys[j]);
ja.put(index, val);
}
} else {
// we're NOT at a leaf key
if (!isNumber(keys[j + 1])) {
// next index is an object
JSONObject joChild;
if (json instanceof JSONObject) {
// last index was an object
// we're creating an object in an object
JSONObject jo = (JSONObject)json;
if (jo.has(keys[j])) {
joChild = jo.getJSONObject(keys[j]);
} else {
joChild = new JSONObject();
jo.put(keys[j], joChild);
}
} else if (json instanceof JSONArray) {
// last index was an array
// we're creating an object in an array
JSONArray ja = (JSONArray)json;
int index = Integer.parseInt(keys[j]);
if (!ja.isNull(index)) {
joChild = ja.getJSONObject(index);
} else {
joChild = new JSONObject();
ja.put(index, joChild);
}
} else {
throw new AssertionError("unhandled object type");
}
json = joChild;
} else {
// next index is an array element
JSONArray jaChild;
if (json instanceof JSONObject) {
// last index was an object,
// we're creating an array in an object
JSONObject jo = (JSONObject)json;
if (jo.has(keys[j])) {
jaChild = jo.getJSONArray(keys[j]);
} else {
jaChild = new JSONArray();
jo.put(keys[j], jaChild);
}
} else if (json instanceof JSONArray) {
// last index was an array
// we're creating an array in an array
JSONArray ja = (JSONArray)json;
int index = Integer.parseInt(keys[j + 1]);
if (!ja.isNull(index)) {
jaChild = ja.getJSONArray(index);
} else {
jaChild = new JSONArray();
ja.put(index, jaChild);
}
} else {
throw new AssertionError("unhandled object type");
}
json = jaChild;
}
}
}
}
return decoded;
}
private static boolean isNumber(String s) {
try {
Integer.parseInt(s);
return true;
} catch (NumberFormatException e) {
return false;
}
}
}
Posted by Jeffrey Blattman 
