JSON-java icon indicating copy to clipboard operation
JSON-java copied to clipboard

Suggestion: JSONArray#toList(class)

Open Chew opened this issue 2 years ago • 4 comments

The current JSONArray#toList() converts the array to a list of Object. This makes things a little tough to work with.

When, for example, you have a list of typically JSON Objects or Strings, you have to do something like this:

jarr.toList().stream().map(Object::toString).toList(); // List of string
jarr.toList().stream().map(o -> (JSONObject) o).toList(); // List of JSONObject

I wrote a function that implments this, but I'm not too Java experty to know if it's ideal:

    /**
     * Converts a JSONArray to a list of {@code <T>}.
     *
     * @param array The JSONArray
     * @param cast The class to cast to
     * @param <T> The type to cast to
     * @return A list of {@code <T>}
     */
    public static <T> List<T> toList(JSONArray array, Class<T> cast) {
        List<T> results = new ArrayList<>(array.length());
        for (Object element : array) {
            if (element == null || JSONObject.NULL.equals(element)) {
                results.add(null);
            } else {
                results.add(cast.cast(element));
            }
        }
        return results;
    }

It's a little messy but it essentially casts each item in the array to the specific object. This is of course missing any exception handling since ClassCastException is likely.

Am I going about my suggestion all wrong? Is there already a way to do this? Please let me know 😄

Chew avatar Mar 21 '22 16:03 Chew

I think your function is the right idea, but not quite there. I'd implement this similar to what you have, but more like this:

 /**
     * Converts a JSONArray to a list of {@code <T>}.
     *
     * @param array The JSONArray
     * @param cast The class to cast to
     * @param ignoreInvalid when true, items that can't be cast are not added to the result array as null. If false, items that can't be cast will be inserted as null. Values that are null before casting will be kept as null in the result array.
     * @param <T> The type to cast to
     * @return A list of {@code <T>}
     */
    public static <T> List<T> toList(JSONArray array, Class<T> cast, boolean ignoreInvalid) {
        List<T> results;
        if(ignoreInvalid) results = new ArrayList<>();
        else results = new ArrayList<>(array.length());
        for (Object element : array) {
            if (JSONObject.NULL.equals(element)) {
                results.add(null);
            } else if(cast.isAssignableFrom(element.getClass()) {
                results.add(cast.cast(element));
            } else if(!ignoreInvalid) {
                results.add(null);
            }
        }
        return results;
    }

using isAssignableFrom prevents any need for exception handling, and trimming out unwanted items from the result array that don't cast can save time on processing the result in downstream processors.

johnjaylward avatar Mar 21 '22 17:03 johnjaylward

personally, I wouldn't bother with toList at all in your use-case though. as in #672, I would just use the spliterator to get a stream and continue to use the Map option like you do:

java.util.StreamSupport(jarr.spliterator(), false)
    .filter(Objects::nonNull)
    .filter(i-> JSONObject.class.isAssignableFrom(i.getClass())
    .map(i-> (JSONObject)i)
  // process
;

Using toList defeats the purpose of using a stream in the first place which is to prevent creation of extra object (in this case an entire array).

johnjaylward avatar Mar 21 '22 17:03 johnjaylward

if you want to make that as a helper method, it would look something like this:

public static <T> Stream<T> toStream(JSONArray array, Class<T> cast) {
    return java.util.StreamSupport(jarr.spliterator(), false)
        .filter(Objects::nonNull)
        .filter(i-> cast.isAssignableFrom(i.getClass())
        .map(i-> cast.cast(i));
}

johnjaylward avatar Mar 21 '22 17:03 johnjaylward

@Chew No objections if you would like to submit a pull request. Please make sure the code either handles exceptions or has a language supported workaround. Keep in mind the project's Java version requirement: What version of Java does this project require?

stleary avatar Mar 22 '22 02:03 stleary

@stleary , looks like the wiki needs a quick update to note the move to Java8 : https://github.com/stleary/JSON-java/wiki/FAQ#what-version-of-java-does-this-project-require

johnjaylward avatar Sep 05 '23 16:09 johnjaylward

@johnjaylward Should be updated now, thanks for catching this.

stleary avatar Sep 05 '23 16:09 stleary

Closing this issue. Please post here if you disagree.

stleary avatar Sep 30 '23 04:09 stleary