在Java中递归排序JSON数组和对象属性

问题描述 投票:0回答:1

目标是从 JSON 字符串开始,解析它,然后递归处理每个对象属性或数组元素。如果该值是一个数组,并且该数组的每个元素都是一个包含“Name”属性的对象,则按 object.Name 对数组进行排序。递归很重要,因为我必须将其应用到的现实 JSON 具有需要排序的三层嵌套数组。

一旦数组按照确定的顺序排序,最后,它应该按字母顺序重新序列化整个属性。

这样做的目的是为了确保 JSON 的一致性,因此我可以在预期和实际之间进行字符串比较,并且如果事物以不同的顺序序列化,或者数组元素返回,则不会丢失它以不确定的顺序(目前就是这种情况)。

在伪代码中,它看起来像这样:

process_array( arr ) {
  if ( arr.every( e => e.has("Name") ) ) {
    arr.sort( byName )
  }
  arr.forEach( process )
}
process_object( obj ) {
  for each key in obj {
    process( obj[key] )
}
process( it ) {
  if ( isArray( it ) {
    process_arr( it )
  } else {
    process_object( it )
  }
}  
standardize( json ) {
  generic_obj = parse( json )
  process( generic_obj )
  return serialize_with_sorted_keys( generic_obj )
}

为了使其具体化,请给出以下输入:

[
  {
    "Id": "1",
    "Name": "foo",
    "Children": [
        {
          "Name": "two",
          "Value": 2
        },
        {
          "Value": 1,
          "Name": "one"
        }
      ],
    "Other": [ 1, 3, 2 ]
  },
  {
    "Name": "bar",
    "Id": "2",
    "Children": [
        {
          "Name": "Banana",
          "Value": 3
        },
        {
          "Value": 4,
          "Name": "Cherry"
        },
        {
          "Apples": "are tasty",
          "Name": "Apple",
          "Value": 5
        }
      ]
   }
]

预期输出为:

[  // array elements are sorted by .Name
  {  // object properties are sorted by key
    "Children": [ // sorted by .Name
        {
          "Apples": "are tasty",
          "Name": "Apple",
          "Value": 5
        },
        {
          "Name": "Banana",
          "Value": 3
        },
        { // properties sorted by name
          "Name": "Cherry",
          "Value": 4
        }
      ],
    "Id": "2",
    "Name": "bar"
  },
  {
    "Children": [
        {
          "Name": "two",
          "Value": 2
        },
        {
          "Name": "one",
          "Value": 1
        }
      ],
    "Id": "1",
    "Name": "foo",
    "Other": [ 1, 2, 3 ]  // NOTE: order has been changed!
  }
]

如何在 Java 中以通用方式执行此操作?

java arrays json sorting jackson
1个回答
0
投票

这是我的尝试(不一定是最佳的,但可以满足我的需求)。通过

SortingNodeFactory
对对象属性进行排序来自 https://cowtowncoder.medium.com/jackson-tips-sorting-json-using-jsonnode-ce4476e37aee。该类中有一个 Jackson 依赖项,它通常是基本 Spring Boot 设置的一部分。

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.node.*;
import java.util.*;

public class JsonSorter {
    private final ObjectMapper objectMapper = new ObjectMapper();
    Comparator<JsonNode> nodeComparator = Comparator.comparing(this::toString);

    public String run(String json) throws JsonProcessingException {
        ObjectMapper sortingMapper = JsonMapper.builder()
                .nodeFactory(new SortingNodeFactory())
                .build();

        JsonNode jsonNode = sortingMapper.readTree(json);
        sortArrays(jsonNode);

        return sortingMapper.writeValueAsString(jsonNode);
    }

    // see https://cowtowncoder.medium.com/jackson-tips-sorting-json-using-jsonnode-ce4476e37aee
    static class SortingNodeFactory extends JsonNodeFactory {
        @Override
        public ObjectNode objectNode() {
            return new ObjectNode(this, new TreeMap<>());
        }
    }

    private String toString(JsonNode node) {
        try {
            return objectMapper.writeValueAsString(node);
        } catch (Exception ex) {
            return "";
        }
    }

    private void sortArrays(JsonNode node) {
        if (node.isArray()) {
            for (JsonNode child : node) {
                sortArrays(child);
            }
            // Sort elements in arrayNode
            var arrayNode = (ArrayNode) node;
            var iter = arrayNode.elements();
            var sortedElemsCopy = new ArrayList<JsonNode>(arrayNode.size());
            while(iter.hasNext()) {
                var n = iter.next();
                sortedElemsCopy.add(n);
            }
            sortedElemsCopy.sort(nodeComparator);

            arrayNode.removeAll();
            arrayNode.addAll(sortedElemsCopy);

        } else if (node.isObject()) {
            ObjectNode objectNode = (ObjectNode) node;
            Iterator<Map.Entry<String, JsonNode>> iter = objectNode.fields();
            while (iter.hasNext()) {
                Map.Entry<String, JsonNode> entry = iter.next();
                sortArrays(entry.getValue());
            }
        }
    }
}

运行代码:

String jsonContent = ...
var normalizedJsonContent = new JsonSorter().run(jsonContent);

希望有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.