javaarraysjsonpdiaccumulate

JSONArray.accumulate method in Java doesn't return arrays of one


I'm trying to accumulate values into a JSONArray. For most data, that works as I expect, for example with (a simplified version of) a function I wrote...

private void copyValidJsonArray(JSONObject objIn, String arrNameIn, JSONObject objOut, String arrNameOut) throws JSONException {
    // This function copies each item of a JSONArray named arrNameIn within objIn to another JSONArray named arrNameOut in objOut, creating arrNameOut if it doesn't exist there.
    
    if (objIn != null) {
        if (objOut == null)
            objOut = new JSONObject();
        JSONArray arrIn = objIn.optJSONArray(arrNameIn);
        if (arrIn != null && arrIn.length() > 0) {
            for (int i = 0; i < arrIn.length(); i++) {
                String valueIn = arrIn.get(i).toString();
                objOut.accumulate(arrNameOut, valueIn);
            }
        }
        if (!objOut.has(arrNameOut))
            objOut.put(arrNameOut, emptyJSONArray);
    }
}

...but when only one valueIn gets accumulated, then accumulate builds a simple string that is not in an array.

What I get:

"Colors": ["red","green","blue"]

"Shapes": ["oval","triangle"]

"Moods": "happy"

What I want:

"Colors": ["red","green","blue"]

"Shapes": ["oval","triangle"]

"Moods": ["happy"]

Researching this problem, I found out that what I get is the correct and generally desired behavior. But systems which will read my output are simplistic and expect the same structure every time.

Searching web and stack overflow, everything I found assumes I want the default behavior, but I can't be the only one who has to deal with singleminded downstream systems, right? ;)

Bottom line: How can I smoothly make sure to output a JSONArray regardless of how many values are incoming?

EDIT: I'm calling the function like this...

copyValidJsonArray(inputData, "colours", outputData, "Colors");
copyValidJsonArray(inputData, "geo", outputData, "Shapes");
copyValidJsonArray(inputData, "moods", outputData, "Moods");

...and that runs once per record, like in Pentaho (PDI).


Solution

  • I don't know if this is the best solution, but it worked for me: See the section commented with "Alter the accumulated output to use arrays of one".

    But I'd be very interested (and for posterity) to hear whether it's an efficient approach or if there's a better way to get this job done (readability or execution speed)?

    private void copyValidJsonArray(JSONObject objIn, String arrNameIn, JSONObject objOut, String arrNameOut) throws JSONException {
        // This function copies each item of a JSONArray named arrNameIn within objIn to another JSONArray named arrNameOut in objOut, creating arrNameOut if it doesn't exist there.
        
        if (objIn != null) {
            if (objOut == null)
                objOut = new JSONObject();
            JSONArray arrIn = objIn.optJSONArray(arrNameIn);
            if (arrIn != null && arrIn.length() > 0) {
                for (int i = 0; i < arrIn.length(); i++) {
                    String valueIn = arrIn.get(i).toString();
                    objOut.accumulate(arrNameOut, valueIn);
                }
                // Alter the accumulated output to use arrays of one:
                if (objOut.has(arrNameOut)) {
                    JSONArray arrOut = objOut.optJSONArray(arrNameOut);
                    // If the structure created by accumulate is a String instead of an array of one...
                    if (arrOut == null) {
                        String valueOut = objOut.optString(arrNameOut);
                        if (valueOut.length() > 0) {
                            // Remove the String and add it as an array of one:
                            objOut.remove(arrNameOut);
                            objOut.put(arrNameOut, new JSONArray().put(valueOut));
                        }
                    }
                }
            }
            if (!objOut.has(arrNameOut))
                objOut.put(arrNameOut, emptyJSONArray);
        }
    }