Search code examples
javajsonfasterxml

How to sort json array elements in java


I know that there is a method to sort maps in json

with

    mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
    mapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);

by fasterxml

But when i got such an array:

{
  "testArray": [
    {
      "firstName": {
        "value": "Lui"
      },
      "lastName": {
        "value": "Armstrong"
      },
      "photo": "234"
    }, {
      "firstName": {
        "value": "Charley"
      },
      "lastName": {
        "value": "Mor"
      },
      "photo": "123"
    }
  ]
}

I guess to sort it by first equals field value, if field keys are equals. So i need the first element in example was after the last, cause word Charley must be before Lui, and they both got the same path - firstName.value. The order of keys is the same, cause of ORDER_MAP_ENTRIES_BY_KEYS. So if path is the same, i expect the order of elements must base on the values, but it does not happens.

I try to use

ObjectMapper mapper = new ObjectMapper();
mapper.setNodeFactory(new JsonNodeFactory() {
    @Override
    public ObjectNode objectNode() {
        return new ObjectNode(this, new TreeMap<String, JsonNode>());
    }

    @Override
    public ArrayNode arrayNode() {
        return new ArrayNode(this, new SomeSortedArrayListThatBaseOnComparatorInComstructor<JsonNode>(new Comparator<JsonNode>() {
            @Override
            public int compare(final JsonNode o1, final JsonNode o2) {
                return //logic to compare
            }
        }) { });

But when i call

mapper.writeValueAsString(someObject)

the code dont use my JsonNodeFactory. In debug there is not thread appear in methods objectNode() or arrayNode().

I cant understand why.

Or maybe there is another method to sort such a jsons?

Add some search results for better google searching: How to sort array of json objects in java, How can I sort a JSONArray of objects in JAVA, How to sort json objects using the order of another json sort json array elements java


Solution

  • Here is a solution

    i need to call readTree, that use JsonNodeFactory

    and then convert to string by writeValueAsString

    Here a code

    public static String writeAsSortedJson(Object object) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setNodeFactory(getArrayNodeKeyValueSortingNodeFactory());
    
        mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
        mapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);
    
        String preparedJson = mapper.writeValueAsString(object);
        JsonNode jsonNode = mapper.readTree(preparedJson);
    
        return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonNode);
    }
    
    @SuppressWarnings("JavaNCSS")
    public static JsonNodeFactory getArrayNodeKeyValueSortingNodeFactory() {
        return new JsonNodeFactory() {
            @Override
            public ObjectNode objectNode() {
                return new ObjectNode(this, new TreeMap<String, JsonNode>());
            }
    
            @SuppressWarnings("JavaNCSS")
            @Override
            public ArrayNode arrayNode() {
                return new ArrayNode(this, new SortedArrayList<JsonNode>(new Comparator<JsonNode>() {
                    @Override
                    public int compare(final JsonNode o1, final JsonNode o2) {
                        return compareRecursively(o1, o2);
                    }
    
                    private int compareRecursively(final JsonNode o1, final JsonNode o2) {
                        if (o1.isObject() && o2.isObject()) {
                            return compareObjectNodes(o1, o2);
                        } else if (o1.isObject()) {
                            return 1;
                        } else if (o2.isObject()) {
                            return -1;
                        } else if (o1.isArray() && o2.isArray()) {
                            return compareArrayNodes(o1, o2);
                        } else if (o1.isArray()) {
                            return 1;
                        } else if (o2.isArray()) {
                            return -1;
                        }
                        return o1.asText().compareTo(o2.asText());
                    }
    
                    private int compareArrayNodes(final JsonNode o1, final JsonNode o2) {
                        List<JsonNode> elements1 = Lists.newArrayList(o1.elements());
                        List<JsonNode> elements2 = Lists.newArrayList(o2.elements());
    
                        for (int i = 0; i < elements1.size() || i < elements2.size(); i++) {
                            if (i >= elements1.size()) {
                                return -1;
                            } else if (i >= elements2.size()) {
                                return 1;
                            }
    
                            JsonNode jsonNode1 = elements1.get(i);
                            JsonNode jsonNode2 = elements2.get(i);
                            int compareRecursively = compareRecursively(jsonNode1, jsonNode2);
                            if (compareRecursively != 0) {
                                return compareRecursively;
                            }
                        }
    
                        return 0;
                    }
    
                    @SuppressWarnings("ReturnCountExtended")
                    private int compareObjectNodes(JsonNode o1, JsonNode o2) {
                        List<Map.Entry<String, JsonNode>> elements1 = Lists.newArrayList(o1.fields());
                        List<Map.Entry<String, JsonNode>> elements2 = Lists.newArrayList(o2.fields());
                        if (elements1.isEmpty() && elements2.isEmpty()) {
                            return 0;
                        } else if (elements1.isEmpty()) {
                            return -1;
                        } else if (elements2.isEmpty()) {
                            return 1;
                        }
    
                        for (int i = 0; i < elements1.size() || i < elements2.size(); i++) {
                            if (i >= elements1.size()) {
                                return -1;
                            } else if (i >= elements2.size()) {
                                return 1;
                            }
    
                            Map.Entry<String, JsonNode> entry1 = elements1.get(i);
                            Map.Entry<String, JsonNode> entry2 = elements2.get(i);
                            int compare = entry1.getKey().compareTo(entry2.getKey());
                            if (compare == 0) {
                                int compareRecursively = compareRecursively(entry1.getValue(), entry2.getValue());
                                if (compareRecursively != 0) {
                                    return compareRecursively;
                                }
                            } else {
                                return compare;
                            }
                        }
    
                        return 0;
                    }
                }) { });
            }
        };
    }
    
    public static class SortedArrayList<T> implements List<T> {
        private final List<T> delegate = new ArrayList<>();
        private final Comparator<T> comparator;
    
        public SortedArrayList(Comparator<T> comparator) {
            this.comparator = comparator;
        }
    
        @Override
        public int size() {
            return delegate.size();
        }
    
        @Override
        public boolean isEmpty() {
            return delegate.isEmpty();
        }
    
        @Override
        public boolean contains(final Object o) {
            return delegate.contains(o);
        }
    
        @NotNull
        @Override
        public Iterator<T> iterator() {
            return delegate.iterator();
        }
    
        @NotNull
        @Override
        public Object[] toArray() {
            return delegate.toArray();
        }
    
        @NotNull
        @Override
        public <T1> T1[] toArray(@NotNull final T1[] a) {
            return delegate.toArray(a);
        }
    
        @Override
        public boolean add(final T t) {
            boolean add = delegate.add(t);
            sort();
            return add;
        }
    
        @Override
        public void add(final int index, final T element) {
            delegate.add(index, element);
            sort();
        }
    
        @Override
        public boolean remove(final Object o) {
            boolean remove = delegate.remove(o);
            sort();
            return remove;
        }
    
        @Override
        public T remove(final int index) {
            T remove = delegate.remove(index);
            sort();
            return remove;
        }
    
        @Override
        public boolean containsAll(@NotNull final Collection<?> c) {
            return delegate.containsAll(c);
        }
    
        @Override
        public boolean addAll(@NotNull final Collection<? extends T> c) {
            boolean addAll = delegate.addAll(c);
            sort();
            return addAll;
        }
    
        @Override
        public boolean addAll(int index, @NotNull Collection<? extends T> c) {
            boolean addAll = delegate.addAll(index, c);
            sort();
            return addAll;
        }
    
        @Override
        public boolean removeAll(@NotNull final Collection<?> c) {
            boolean removeAll = delegate.removeAll(c);
            sort();
            return removeAll;
        }
    
        @Override
        public boolean retainAll(@NotNull final Collection<?> c) {
            boolean retainAll = delegate.retainAll(c);
            sort();
            return retainAll;
        }
    
        @Override
        public void clear() {
            delegate.clear();
        }
    
        @Override
        public boolean equals(final Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            SortedArrayList<?> that = (SortedArrayList<?>) o;
            return Objects.equals(delegate, that.delegate);
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(delegate);
        }
    
        @Override
        public T get(final int index) {
            return delegate.get(index);
        }
    
        @Override
        public T set(final int index, final T element) {
            delegate.set(index, element);
            sort();
            return element;
        }
    
        @Override
        public int indexOf(final Object o) {
            return delegate.indexOf(o);
        }
    
        @Override
        public int lastIndexOf(final Object o) {
            return delegate.lastIndexOf(o);
        }
    
        @NotNull
        @Override
        public ListIterator<T> listIterator() {
            return delegate.listIterator();
        }
    
        @NotNull
        @Override
        public ListIterator<T> listIterator(final int index) {
            return delegate.listIterator(index);
        }
    
        @NotNull
        @Override
        public List<T> subList(final int fromIndex, final int toIndex) {
            return delegate.subList(fromIndex, toIndex);
        }
    
        private void sort() {
            delegate.sort(comparator);
        }
    }