How do I clone an org.json.JSONObject in Java?
Asked Answered
S

7

52

Is there a way to clone an instance of org.json.JSONObject without stringifying it and reparsing the result?

A shallow copy would be acceptable.

Sherris answered 9/10, 2012 at 23:16 Comment(1)
This question itself was the answer for me. +1Fort
P
65

Use the public JSONObject(JSONObject jo, java.lang.String[] names) constructor and the public static java.lang.String[] getNames(JSONObject jo) method.

JSONObject copy = new JSONObject(original, JSONObject.getNames(original));
Posy answered 9/10, 2012 at 23:30 Comment(2)
I'd like to warn others that this method performs a shallow copy, the asker wanted that but don't forget it.Mercurochrome
Warning: JSONObject.getNames(JSONObject instance) does not exist on Android.Expositor
B
92

Easiest (and incredibly slow and inefficient) way to do it

JSONObject clone = new JSONObject(original.toString());
Borg answered 24/8, 2013 at 11:32 Comment(6)
That would be stringifying and parsing the result, which I said I didn't want to do.Sherris
Looks like an easy way to deep clone, though.Toady
Damn why didn't I think of this :PAvaavadavat
So this seems to be the only way to perform a deep clone (using one the JSONObject's public API)?Chanteuse
The problem with this approach is that if the JSONObject has cyclic references, it will throw a StackOverflowError when toString() is invoked.Saiva
@Saiva if your JSON objects have cyclical references, you might need to refactor somethingKoser
P
65

Use the public JSONObject(JSONObject jo, java.lang.String[] names) constructor and the public static java.lang.String[] getNames(JSONObject jo) method.

JSONObject copy = new JSONObject(original, JSONObject.getNames(original));
Posy answered 9/10, 2012 at 23:30 Comment(2)
I'd like to warn others that this method performs a shallow copy, the asker wanted that but don't forget it.Mercurochrome
Warning: JSONObject.getNames(JSONObject instance) does not exist on Android.Expositor
K
5

the fastest + minimal way I found is this. it does deep copy.

JSONObject clone= new JSONObject(original.toMap());

I know the asker said

A shallow copy would be acceptable. but I think that does not rule out if the solution will do deep copy.

Update: the toMap() function is not available in Android. but the org.json library available on maven under groupId org.json has it: https://search.maven.org/artifact/org.json/json/20210307/bundle

Kinna answered 13/8, 2021 at 15:5 Comment(0)
P
3

Cause $JSONObject.getNames(original) not accessible in android, you can do it with:

public JSONObject shallowCopy(JSONObject original) {
    JSONObject copy = new JSONObject();

    for ( Iterator<String> iterator = original.keys(); iterator.hasNext(); ) {
        String      key     = iterator.next();
        JSONObject  value   = original.optJSONObject(key);

        try {
            copy.put(key, value);
        } catch ( JSONException e ) {
            //TODO process exception
        }
    }

    return copy;
}

But remember it is not deep copy.

Philippe answered 4/4, 2017 at 11:35 Comment(0)
D
3

For Android developers, the simplest solution without using .getNames is:

JSONObject copy = new JSONObject();
for (Object key : original.keySet()) {
  Object value = original.get(key);
  copy.put(key, value);
}

Note: This is a only a shallow copy

Divergence answered 13/8, 2019 at 1:18 Comment(0)
B
2

Couldn't find an existing deep clone method for com.google.gwt.json.client.JSONObject but the implementation should be few lines of code, something like:

public static JSONValue deepClone(JSONValue jsonValue){
    JSONString string = jsonValue.isString();
    if (string != null){return new JSONString(string.stringValue());}

    JSONBoolean aBoolean = jsonValue.isBoolean();
    if (aBoolean != null){return JSONBoolean.getInstance(aBoolean.booleanValue());}

    JSONNull aNull = jsonValue.isNull();
    if (aNull != null){return JSONNull.getInstance();}

    JSONNumber number = jsonValue.isNumber();
    if (number!=null){return new JSONNumber(number.doubleValue());}

    JSONObject jsonObject = jsonValue.isObject();
    if (jsonObject!=null){
        JSONObject clonedObject = new JSONObject();
        for (String key : jsonObject.keySet()){
            clonedObject.put(key, deepClone(jsonObject.get(key)));
        }
        return clonedObject;
    }

    JSONArray array = jsonValue.isArray();
    if (array != null){
        JSONArray clonedArray = new JSONArray();
        for (int i=0 ; i < array.size() ; ++i){
            clonedArray.set(i, deepClone(array.get(i)));
        }
        return clonedArray;
    }

    throw new IllegalStateException();
}

*Note:*I haven't tested it yet!

Banner answered 4/6, 2013 at 10:2 Comment(0)
D
-2

In case anyone comes here looking for a deep clone for org.google.gson, since they don't expose their deepClone() method this is what I came up with...

public static JsonElement deepClone(JsonElement el){
    if (el.isJsonPrimitive() || el.isJsonNull())
        return el;
    if (el.isJsonArray()) {
        JsonArray array = new JsonArray();
        for(JsonElement arrayEl: el.getAsJsonArray())
            array.add(deepClone(arrayEl));
        return array;
    }
    if(el.isJsonObject()) {
        JsonObject obj = new JsonObject();
        for (Map.Entry<String, JsonElement> entry : el.getAsJsonObject().entrySet()) {
            obj.add(entry.getKey(), deepClone(entry.getValue()));
        }
        return obj;
    }
    throw new IllegalArgumentException("JsonElement type " + el.getClass().getName());
}

And here are a few methods to merge two JsonObject's

public static JsonObject merge(String overrideJson, JsonObject defaultObj) {
    return mergeInto((JsonObject)new JsonParser().parse(overrideJson), defaultObj);
}
public static JsonObject merge(JsonObject overrideObj, JsonObject defaultObj) {
    return mergeOverride((JsonObject)deepClone(defaultObj), overrideObj);
}
public static JsonObject mergeOverride(JsonObject targetObj, JsonObject overrideObj) {
    for (Map.Entry<String, JsonElement> entry : overrideObj.entrySet())
            targetObj.add(entry.getKey(), deepClone(entry.getValue()));
    return targetObj;
}
public static JsonObject mergeInto(JsonObject targetObj, JsonObject defaultObj) {
    for (Map.Entry<String, JsonElement> entry : defaultObj.entrySet()) {
        if (targetObj.has(entry.getKey()) == false)
            targetObj.add(entry.getKey(), deepClone(entry.getValue()));
    }
    return targetObj;
}
Dihedron answered 5/10, 2016 at 2:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.