Search code examples
javaarraysjsonstringorg.json

Dot notation to JSON (including arrays)


I want to convert dot notated string to a JSONObject but include arrays too, for example: I want to set first.second[0].third[0].fourth to some string. So, JSON must be:

{
  "first": {
    "second": [
      {
        "third": [
          "fourth": "some string"
        ]
      }
    ]
  }
}

I found this method then edited and it turned out something like this:

private void expand(Object parent, String key, String value) {
    if (key == null) return;
    if (!key.contains(".") && !key.contains("[")) {
        if (parent instanceof JSONObject) {
            ((JSONObject) parent).put(key, value);
        } else {
            ((JSONArray) parent).put(value);
        }

        return;
    }

    String innerKey = key.substring(0, key.contains(".") ? key.indexOf(".") : key.length());
    String formattedInnerKey = innerKey.contains("[") ? innerKey.substring(0, innerKey.indexOf("[")) : innerKey;
    String remaining = key.contains(".") ? key.substring(key.indexOf(".") + 1) : key.contains("]") ? key.substring(key.indexOf("]") + 1) : null;

    if (parent instanceof JSONObject) {
        JSONObject jsonObject = (JSONObject) parent;
        if (jsonObject.has(formattedInnerKey)) {
            expand(jsonObject.get(formattedInnerKey), remaining, value);
            return;
        }
    } else {
        JSONArray jsonArray = (JSONArray) parent;
        Matcher matcher = Pattern.compile("(?<=\\[)([^\\]]+)(?=\\])").matcher(innerKey);
        Preconditions.checkState(matcher.find(), String.format("Matcher couldn't find a index number in \"%s\"", innerKey));
        int index = Integer.parseInt(matcher.group());
        System.out.print(index + " - ");
        if (!jsonArray.isNull(index)) {
            System.out.print(jsonArray.get(index));
            expand(jsonArray.get(index), remaining, value);
            return;
        }
    }

    Object obj = innerKey.contains("[") ? new JSONArray() : new JSONObject();
    if (parent instanceof JSONObject) {
        ((JSONObject) parent).put(formattedInnerKey, obj);
    } else {
        JSONObject base = new JSONObject();
        base.put(formattedInnerKey, obj);
        Matcher matcher = Pattern.compile("(?<=\\[)([^\\]]+)(?=\\])").matcher(innerKey);
        Preconditions.checkState(matcher.find(), String.format("Matcher couldn't find a index number in \"%s\"", innerKey));
        int index = Integer.parseInt(matcher.group());
        ((JSONArray) parent).put(index, base);
    }
    expand(obj, remaining, value);
}

This method -kinda- works but the problem is that it adds elements to the array instead of putting. I want to be able to put the object to an index in that array. How can I fix this?


Solution

  • Here's the solution:

    public void expand(Object parent, String key, Object value) {
        JSONElement element = new JSONElement(parent);
        if (!key.contains(".")) { // End
            element.put(key, value);
            return;
        }
    
        String innerKey = key.substring(0, key.indexOf("."));
        String remaining = key.substring(key.indexOf(".") + 1);
    
        if (element.has(innerKey)) {
            expand(element.get(innerKey), remaining, value);
            return;
        }
    
        Object object = element.newInstance();
        Object put = element.put(innerKey, object);
        expand(put, remaining, value);
    }
    

    JSONElement class:

    import com.google.common.base.Preconditions;
    import org.json.JSONArray;
    import org.json.JSONObject;
    
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    public class JSONElement {
    
        private static final Pattern pattern = Pattern.compile("(?<=\\[)([^]]+)(?=])");
    
        private final Object base;
    
        public JSONElement(Object base) {
            Preconditions.checkNotNull(base, "base");
            Preconditions.checkState(base instanceof JSONObject || base instanceof JSONArray, "base must be a JSONObject or JSONArray instead of " + base.getClass().getSimpleName());
            this.base = base;
        }
    
        public Object put(Object key, Object value) {
            String keyAsString = String.valueOf(key);
    
            if (base instanceof JSONObject) {
                if (keyAsString.contains("[")) {
                    String formatKey = keyAsString.contains("[") ? keyAsString.substring(0, keyAsString.indexOf("[")) : keyAsString;
                    JSONArray array = ((JSONObject) base).has(formatKey) ? ((JSONObject) base).getJSONArray(formatKey) : new JSONArray();
    
                    int index = getIndex(keyAsString);
                    array.put(index, value);
                    ((JSONObject) base).put(formatKey, array);
                    return ((JSONArray) ((JSONObject) base).get(formatKey)).get(index);
                }
    
                ((JSONObject) base).put(keyAsString, value);
                return ((JSONObject) base).get(keyAsString);
            }
    
            int index = getIndex(keyAsString);
            ((JSONArray) base).put(index, value);
            return ((JSONArray) base).get(index);
        }
    
        public boolean has(Object key) {
            String keyAsString = String.valueOf(key);
    
            if (base instanceof JSONObject) {
                JSONObject object = (JSONObject) base;
                String formatKey = formatKey(keyAsString);
                if (keyAsString.contains("["))
                    return object.has(formatKey) && !object.getJSONArray(formatKey).isNull(getIndex(keyAsString));
    
                return object.has(formatKey);
            }
    
            return !((JSONArray) base).isNull(getIndex(keyAsString));
        }
    
        public Object get(Object key) {
            String keyAsString = String.valueOf(key);
            if (base instanceof JSONObject) {
                JSONObject object = (JSONObject) base;
                String formatKey = formatKey(keyAsString);
                if (keyAsString.contains("["))
                    return object.getJSONArray(formatKey).get(getIndex(keyAsString));
    
                return object.get(formatKey);
            }
    
            return ((JSONArray) base).get(getIndex(keyAsString));
        }
    
        public Object newInstance() {
            return base instanceof JSONObject ? new JSONObject() : new JSONArray();
        }
    
        private int getIndex(String key) {
            Matcher matcher = pattern.matcher(key);
            Preconditions.checkState(matcher.find(), String.format("Matcher couldn't find an index number in \"%s\"", key));
            return Integer.parseInt(matcher.group());
        }
    
        private String formatKey(String key) {
            return key.contains("[") ? key.substring(0, key.indexOf("[")) : key;
        }
    }
    

    Usage:

    JSONObject jsonObject = new JSONObject();
    expand(jsonObject, "first.second[0].third[0].fourth", "some string");
    System.out.println(jsonObject.toString()); // Prints: {"first":{"second":[{"third":[{"fourth":"some string"}]}]}}