0

I am using a PrimeFaces JSONArray object containing JSON arrays, e.g. (simplified)

[['james', 12, 2019], ['sarah', 29, 2015], ['robert', 15, 2011]]

as input to a static build method which runs over the fields of the nested arrays to construct a POJO representing their data. I implement this using static build methods within both the entity and container-of-entity classes (the latter of which simply parses the JSON and calls the former build method to construct each entity). This works fine with a crude for loop:

public static MyContainer buildContainer(JSONArray json) {
    MyContainer list = new MyContainer ();
    for (int i = 0; i < json.length(); i++) {
        MyEntity ent = MyEntity.buildEntity(json, i);
        list.add(ent);
    }
    return list;
}

but I would like a more elegant solution, in particular one using a functional approach (to improve my knowledge of functional Java, if for no better reason). I use Jackson to parse the JSONArray representing my complete dataset to an array of JSONArrays (i.e. a JSONArray[]). This array can then be converted to a list, which allows easier functional manipulation. Here is my initial attempt at writing down what I think that would look like, which probably shows some fundamental misunderstandings of Jackson and functional Java:

public static MyContainer buildContainer(JSONArray json) {
    ObjectMapper mapper = new ObjectMapper();
    try {
        JSONArray[] jaArr = mapper.readValue(json.toString(), JSONArray[].class);
        List<JSONArray> jaList = Arrays.asList(jaArr);
        MyContainer list = (MyContainer) (List<MyEntity>) Arrays.asList(json)
                .stream()
                .map(MyEntity::buildEntity)
                .collect(Collectors.toList());
        return list;
    } catch (JsonProcessingException ex) {
        return null;
    }
}

Netbeans does not suggest anything is wrong with this, and there are no compile-time issues. At runtime, however, I get the exception

Severe: com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of org.primefaces.json.JSONArray out of START_ARRAY token.

So, my questions are why this exception arises, when the input is of the format I have shown above, and whether there is a more elegant solution than serialising the JSONArray to a string then reading it back as a JSONArray[] – i.e. could I convert my initial JSONArray more directly to a type that allows .stream() or .forEach()?

Gavin Kirby
  • 43
  • 10
  • Is `MyEntity` is a `POJO` with 3 properties for each element in array: `['james', 12, 2019]`? It looks like `MyContainer` implements `List` interface. Why do you need your own implementation? – Michał Ziober Sep 09 '19 at 12:32
  • That is a simplified example; there are many more fields in the real case, with various data types. My container class does implement List; it is a custom List with additional fields, because it also needs to contain statistical information about the objects within it. This is used elsewhere in the application, but isn't relevant to the question here. – Gavin Kirby Sep 09 '19 at 12:45
  • From which package `JSONArray` class comes from? – Michał Ziober Sep 09 '19 at 12:47
  • It's an instance of org.primefaces.json.JSONArray, as you see in the exception I posted – Gavin Kirby Sep 09 '19 at 12:51

1 Answers1

0

JSONArray in version 7.0 implements Iterable so you can do something like this:

StreamSupport
  .stream(iterable.spliterator(), false)
  .map(MyEntity::buildEntity)
  .collect(Collectors.toList());

See also:

Michał Ziober
  • 37,175
  • 18
  • 99
  • 146
  • 1
    Thank you, that was exactly what I needed. The only addition I had to make was an element-by-element type conversion before calling the build method ```{java} public static MyContainer build(JSONArray json) { return fromList( StreamSupport .stream(json.spliterator(), false) .map(array -> (JSONArray) array) .map(MyEntity::buildEntity) .collect(Collectors.toList()) ); } ``` – Gavin Kirby Sep 09 '19 at 14:38