Json 使用java流对复杂对象进行分组列表

Json 使用java流对复杂对象进行分组列表,json,java-8,stream,grouping,collectors,Json,Java 8,Stream,Grouping,Collectors,我有一个gprsEvents列表,其中列表中的每个元素都是一个映射,如下所示。我需要: { "events":[ { "localTimeStamp":"20170523113305", "serviceCode":"GPRS", "recEntityCode":[ "1&

我有一个gprsEvents列表,其中列表中的每个元素都是一个映射,如下所示。我需要:

{
   "events":[
      {
         "localTimeStamp":"20170523113305",
         "serviceCode":"GPRS",
         "recEntityCode":[
            "1",
            "2"
         ],
         "index":"1",
         "dataVolumeIncoming":"400000",
         "dataVolumeOutgoing":"27600",
         "callChargingId":"4100853125",
      },
      {
         "localTimeStamp":"20190523113305",
         "serviceCode":"GPRS",
         "recEntityCode":[
            "2",
            "4"
         ],
         "index":"2",
         "dataVolumeIncoming":"300000",
         "dataVolumeOutgoing":"47600",
         "callChargingId":"4100853125",
      },
      {
         "localTimeStamp":"20180523113305",
         "serviceCode":"GPRS",
         "recEntityCode":[
            "1",
            "2"
         ],
         "index":"7",
         "dataVolumeIncoming":"100000",
         "dataVolumeOutgoing":"17600",
         "callChargingId":"5100853125",
      }
      
   ]
}
  • 按字段“callChargingId”对列表中的元素进行分组
  • 分组的结果应该是一个映射,其中键是“CallChargingID”,是一个元素,其中每个字段都包含一组字段作为分组的结果。结果如下所示:
  • 其中,对于某些字段,分组结果应为列表(时间戳、recEntityCode和索引),而对于其他字段,分组结果应为summe(dataVolumeIncoming和dataVolumeOutgoing)

    我开始考虑使用Java8流(groupingBy): gprsEvents.stream().collect(Collectors.groupingBy(map->map.get(“callChargingId”).toString())


    我现在一直想得到合适的结果,特别是在一个单独的映射和上述字段的列表或总和中得到结果…

    这里的关键点是实现
    BinaryOperator mergeFunction
    对象,它将完成最复杂的部分:合并两个
    Map
    实例。我建议使用一个带有3个参数的方法:

  • keyMapper
    -
    callChargingId
    value
  • valueMapper
    -
    Map
    实例
  • mergeFunction
    -将合并类型为
    BinaryOperator
  • 简单的实现可以如下所示:

    import com.fasterxml.jackson.databind.SerializationFeature;
    import com.fasterxml.jackson.databind.json.JsonMapper;
    import com.fasterxml.jackson.databind.type.MapType;
    
    import java.io.File;
    import java.io.IOException;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    import java.util.function.BinaryOperator;
    import java.util.stream.Collectors;
    
    public class JsonTypeInfoApp {
        public static void main(String[] args) throws IOException {
            File jsonFile = new File("./resource/test.json").getAbsoluteFile();
    
            JsonMapper mapper = JsonMapper.builder()
                    .enable(SerializationFeature.INDENT_OUTPUT)
                    .build();
            MapType jsonType = mapper.getTypeFactory().constructMapType(Map.class, String.class, List.class);
            Map<String, List<Map<String, Object>>> response = mapper.readValue(jsonFile, jsonType);
    
            List<Map<String, Object>> gprsEvents = response.get("events");
            Map<Object, Map<String, Object>> result = gprsEvents.stream()
                    .collect(Collectors.toMap(
                            map -> map.get("callChargingId"),
                            map -> map,
                            new EventMerger()));
    
            mapper.writeValue(System.out, result);
        }
    }
    
    class EventMerger implements BinaryOperator<Map<String, Object>> {
    
        @Override
        public Map<String, Object> apply(Map<String, Object> map0, Map<String, Object> map1) {
            map1.forEach((secondKey, secondValue) -> {
                map0.compute(secondKey, (key, value) -> {
                    if (value == null) {
                        return secondValue;
                    } else if (value instanceof Set) {
                        Set<Object> values = (Set<Object>) value;
                        values.add(secondValue);
                        return values;
                    }
    
                    Set<Object> values = new HashSet<>();
                    values.add(value);
                    values.add(secondValue);
    
                    return values;
                });
            });
            return map0;
        }
    }
    

    没有必要在每次合并时都创建一个新映射。只要在值映射器中创建映射的副本,添加到左侧映射就足够了。另外,为什么不使用方法引用而不是新类?@fps:1。
    新映射
    -很好。我只是想显式地显示它将是一个
    合并
    点,但我在这里看到了it’没有理由创建一个新实例。也许与
    parallelStream
    一起使用会更安全些?你怎么看?2.
    为什么不使用方法引用
    -只是为了清楚地显示我在答案顶部提到的3个参数。当然,最终的解决方案取决于作者,可以作为方法引用轻松地移到内部nce。当我们创建这样的类时,提供单元测试也要容易得多。
    parallelStream
    在大多数情况下使用较慢或不正确,但与安全无关。您可以选择并行或不并行。至于使用方法引用,您完全正确,这是个人喜好的问题
    import com.fasterxml.jackson.databind.SerializationFeature;
    import com.fasterxml.jackson.databind.json.JsonMapper;
    import com.fasterxml.jackson.databind.type.MapType;
    
    import java.io.File;
    import java.io.IOException;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    import java.util.function.BinaryOperator;
    import java.util.stream.Collectors;
    
    public class JsonTypeInfoApp {
        public static void main(String[] args) throws IOException {
            File jsonFile = new File("./resource/test.json").getAbsoluteFile();
    
            JsonMapper mapper = JsonMapper.builder()
                    .enable(SerializationFeature.INDENT_OUTPUT)
                    .build();
            MapType jsonType = mapper.getTypeFactory().constructMapType(Map.class, String.class, List.class);
            Map<String, List<Map<String, Object>>> response = mapper.readValue(jsonFile, jsonType);
    
            List<Map<String, Object>> gprsEvents = response.get("events");
            Map<Object, Map<String, Object>> result = gprsEvents.stream()
                    .collect(Collectors.toMap(
                            map -> map.get("callChargingId"),
                            map -> map,
                            new EventMerger()));
    
            mapper.writeValue(System.out, result);
        }
    }
    
    class EventMerger implements BinaryOperator<Map<String, Object>> {
    
        @Override
        public Map<String, Object> apply(Map<String, Object> map0, Map<String, Object> map1) {
            map1.forEach((secondKey, secondValue) -> {
                map0.compute(secondKey, (key, value) -> {
                    if (value == null) {
                        return secondValue;
                    } else if (value instanceof Set) {
                        Set<Object> values = (Set<Object>) value;
                        values.add(secondValue);
                        return values;
                    }
    
                    Set<Object> values = new HashSet<>();
                    values.add(value);
                    values.add(secondValue);
    
                    return values;
                });
            });
            return map0;
        }
    }
    
    {
      "4100853125" : {
        "localTimeStamp" : [ "20170523113305", "20190523113305" ],
        "serviceCode" : [ "GPRS" ],
        "recEntityCode" : [ [ "1", "2" ], [ "2", "4" ] ],
        "index" : [ "1", "2" ],
        "dataVolumeOutgoing" : [ "47600", "27600" ],
        "callChargingId" : [ "4100853125" ],
        "dataVolumeIncoming" : [ "400000", "300000" ]
      },
      "5100853125" : {
        "localTimeStamp" : "20180523113305",
        "serviceCode" : "GPRS",
        "recEntityCode" : [ "1", "2" ],
        "index" : "7",
        "dataVolumeIncoming" : "100000",
        "dataVolumeOutgoing" : "17600",
        "callChargingId" : "5100853125"
      }
    }