在运行时创建Composite Comparator进行排序

时间:2019-05-10 14:15:18

标签: java json spring-boot

我的样品请求

 {
  "requestModel":{
      "CUSTID": "100"
     },
  "returnParameters":[
    {
     "name":"NETWORK/NETID",
     "datatype":"String",
     "order":"asc",
     "sequence":1
    },
    {
     "name":"INFODATA/NAME",
     "datatype":"String",
     "order":"asc",
     "sequence":1
    },
    {
     "name":"SOURCE/SYSTEM",
     "datatype":"int",
     "order":"asc",
     "sequence":2
    },
   ]
 }

示例响应

下面是我动态生成的json响应的Map格式[每次根据请求参数,响应参数都会有所不同]

"responseModel":{
  "documents": [
{
 "NETWORK":[
    {"NETID":"1234"},
    {"ACT":"300"}
   ],
   "SOURCE": {
      "SYSTEM":"50"
     },
   "INFODATA":{
     "NAME":"PHIL"
     }
 },
 {
  "NETWORK":[
    {"NETID":"1234"},
    {"ACT":"300"}
   ],
   "SOURCE": {
      "SYSTEM":"100"
     },
   "INFODATA":{
     "NAME":"PHIL"
     }
  }
 ]
}

问题陈述

我需要基于请求是动态的中的“ returnParameters”进行多级排序...     “ order”表示升序(或)降序,sequence表示排序的优先级,例如(在sql查询中为group by)

代码

Map<String,Object> documentList = new HashMap<String,Object>();
JSONObject jsonObject= new JSONObject(response.getContent());

response.getContent()->没什么,但是它包含以上以Map格式的json响应。

Now I converting the map to list of json object

JSONArray jsonArray= (JSONArray)jsonObject.get("documents");
ArrayList<JSONObject> list = new ArrayList<>();
for(int i=0;i<jsonArray.length();i++){
 list.add((JSONObject) jsonArray.get(i));
 }
 Collections.sort(list, new ResponseSorter());
 public class ResponseSorter implements Comparator<JSONObject> {
  @Override
  public int compare(JSONObject o1,JSONObject o2){
  String s1= (String)((JSONObject) o1.get("NETWORK")).get("NETID");
  String s2= (String)((JSONObject) o2.get("NETWORK")).get("NETID");
  int i1=Integer.parseInt(s1);
  int i2=Integer.parseInt(s2);
  return i1-i2;
  }
 }

我被困在这里以继续进行。为Integer比较器创建了一个,是否应该为每个dataType创建一个?也   我需要通过解析“ retunrParameters”来动态构造复合比较器,下面的示例是硬编码的,如何动态创建?

(String)((JSONObject) o1.get("NETWORK")).get("NETID"); -> this should be dynamically framed , since "returnParameters" are also dynamic in nature.[NETWORK & NETID may not be come in another request],so my comparator should be capable enough to frame the keys in runtime

有人能协助我在运行时创建复合比较器进行排序吗?

注意:-由于响应是动态的,因此无法创建Java Pojo

3 个答案:

答案 0 :(得分:1)

在评论中的其他问题和描述中的其他信息之后进行了编辑


您需要执行以下几个步骤才能找到解决方案:

  1. 您希望基于请求中属性sequence的值进行动态排序。因此,您需要解析那些returnParameters的名称,并将它们排序。在下面,我将它们映射到一个列表,其中每个String []都有nameorder(asc / desc)。该列表将使用sequence的值进行排序:

    List<String[]> sortParams = params.stream() // params is a List<JSONObject>
            .filter(json -> json.containsKey("sequence")) // filter those that have "sequence" attribute
            .sorted( sequence ) // sorting using Comparator called sequence
            .map(jsonObj -> new String[]{jsonObj.get("name").toString(), jsonObj.get("order").toString()} )
            .collect(Collectors.toList());
    

在此之前,您首先将请求中的returnParameters数组中的对象映射到一个List。然后通过1.过滤JSONObjects以仅保留具有道具sequence的对象来处理流。 ,2.使用下面的比较器对JSONObject进行排序。 3.从每个JSONObject中获取“名称”和“顺序”,并将它们放入String [],4.生成包含这些数组的列表。该列表将按照属性的顺序进行排序,首先是优先级1,然后是优先级2,依此类推,因此它的排序方式与您希望JSONObjects最终排序的方式相同。

    Comparator<JSONObject> sequence = Comparator.comparingInt(
        jsonObj -> Integer.valueOf( jsonObj.get("sequence").toString() ) 
    );

因此,在您的示例中,sortParams看起来像:List( String[]{"NETWORK/NETID", "asc"}, String[]{""INFODATA/NAME", "asc"}, String[]{"SOURCE/SYSTEM", "asc"} )

  1. 然后,您需要编写一个包含两个参数的方法:JSONObject和String(属性的路径),并返回该属性的值。最初,我建议您使用JSONAware接口,然后找出该子类,但现在就让我们忘记它。

    我不会为您编写此方法。请记住,JSON.Simple的.get(key)方法总是产生一个Object。用这个签名写一个方法:

    public String findSortValue(JSONObject doc, String path){
        // split the path
        // find the parent
        // cast it  (parent was returned as an Object of type Object)
        // find the child
        return value;
    }
    
  2. 编写一个通用的单个比较器(一次只比较一个sort属性的值),并确定它是Int,Date还是常规String。我将其编写为常规方法,以便以后将所有内容组合起来会更容易。由于您对此有很多疑问,因此我举了一个例子:

    int individualComparator(String s1, String s2){
    int compResult = 0;
    try{
        int numeric1 = Integer.parseInt(s1);
        int numeric2 = Integer.parseInt(s2);
        compResult = numeric1 - numeric2; // if this point was reached both values could be parsed
    } catch (NumberFormatException nfe){
        // if the catch block is reached they weren't numeric
        try{
            DateTime date1 = DateTime.parse(s1);
            DateTime date2 = DateTime.parse(s2);
            compResult = date1.compareTo(date2); // compareTo method of joda.time, the library I'm using
        } catch (IllegalArgumentException iae){
            //if this catch block is reached they weren't dates either
            compResult = s1.compareTo(s2);
        }
    }
    return compResult;
    

    };

    1. 编写一个包含所有内容的整体比较器

      Comparator<JSONObject> overAllComparator = (jsonObj1, jsonObj2) -> {
          List<String[]> sortValuesList = sortParams.stream()
              .map(path -> new String[]{ findValueByName(jsonObj1, path), findValueByName(jsonObj2, path) } )
              .collect(Collectors.toList());
      
      //assuming we always have 3 attributes to sort on
      int comp1 = individualComparator(sortValuesList.get(0)[0], sortValuesList.get(0)[1]);
      int comp2 = individualComparator(sortValuesList.get(1)[0], sortValuesList.get(1)[1]);
      int comp3 = individualComparator(sortValuesList.get(2)[0], sortValuesList.get(2)[1]);
      
      int result = 0;
      if (comp1 != 0){
          result = comp1;
      } else if (comp2 != 0){
          result = comp2;
      } else{
          result = comp3;
      }
      return result;
      };
      

此比较器以lambda样式编写,以获取更多信息https://www.mkyong.com/java8/java-8-lambda-comparator-example/

首先,它获取我们在步骤1中创建的sortParams的有序列表,并为每个数组返回一个数组,其中位置0的值为jsonObj1,位置1的值为jsonObj2并将其收集在sortValuesList中。然后,对于每个要排序的属性,它都会得到individualComparator方法的结果。然后,它沿线下降并作为整体比较的结果返回第一个不为0的值(当比较器的结果为0时,两个值相等)。

现在唯一缺少的是请求中的asc / desc值。您可以通过使用简单的方法将int comp1 = individualComparator(sortValuesList.get(0)[0], sortValuesList.get(0)[1]);链接到sortParams来添加它,该方法接受一个int和一个String,如果String等于“ desc”,则将int乘以-1。 (请记住,在order中,我们在数组的位置1上添加了sortParams的值。)

因为我们创建了第一个列表,所以returnParams是根据请求中指示的优先级排序的,并且我们总是按照该列表的顺序进行所有操作,因此结果是按此顺序进行的多排序。它是通用的,将由请求中Collections.sort()的内容动态确定。您可以使用> devtools::install_github("https://github.com/nwstephens/RStudioAMI.git") Downloading GitHub repo nwstephens/RStudioAMI@master ✔ checking for file ‘/tmp/RtmpEH49ON/remotes33643b988bf8/nwstephens-RStudioAMI-8507ced/DESCRIPTION’ ─ preparing ‘RStudioAMI’: ✔ checking DESCRIPTION meta-information ... OK Warning in file(con, "r") : cannot open file 'man': No such file or directory ERROR computing Rd index failed:cannot open the connection Error in (function (command = NULL, args = character(), error_on_status = TRUE, : System command error

将其应用于JSONObjects列表。

答案 1 :(得分:1)

在您的情况下,一个带有sort参数的简单比较器可能比一堆嵌套的比较器更容易理解。

基本上,您会这样做:

class ReturnParameterComparator implements Comparator<JSONObject> {
   private List<ReturnParameter> params; //set via constructor

   public int compare( JSONObject left, JSONObject right) {
     int result = 0;
     for( ReturnParameter p : params ) {
       //how exactly you get those values depends on the actual structure of your data and parameters
       String leftValueStr = left.get( p ); 
       String rightValueStr = right.get( p ); 

       switch( p.datatype ) {
         case "String": 
           result = String.compare( leftValueStr, rightValueStr );
           break;
         case "int": 
           //convert and then compare - I'll leave the rest for you 
       }

       //invert the result if the order is descending
       if( "desc".equals(p.order ) {
         result += -1;
       }

       //the values are not equal so return the order, otherwise continue with the next parameter
       if( result != 0 ) {
         return result;
       }
     }

     //at this point all values are to be considered equal, otherwise we'd have returned already (from the loop body)
     return 0;
   }
}

请注意,这只是一个入门指南。您需要添加很多内容:

  • 如何正确使用参数从json对象中提取值
  • 如何根据类型转换数据
  • 如何处理null,丢失或不兼容的数据(例如,如果值应排序为“ int”但无法解析)

添加所有这些内容对于此问题的范围而言可能太多,并且仍然取决于您的数据和要求。

答案 2 :(得分:0)

我的建议:了解以下信息:

  • Comparator.comparing,可让您通过指定密钥提取器来构建比较器
  • Comparator.thanComparing,可让您链接多个比较器。仅当前辈说对象相等时,才调用链中后面的比较器

如果需要的话,提供一个教程:https://www.baeldung.com/java-8-comparator-comparing