【问题标题】:Guava Hashmap comparisonGuava Hashmap 比较
【发布时间】:2021-08-14 20:37:13
【问题描述】:

我在使用 Guava 的 Maps.difference 时遇到了一些问题

现在,使用此代码比较来自两个不同 json 的两个 HashMap:

//Create maps from the given jsons
Gson gson = new Gson();
Type type = new TypeToken<Map<String, Object>>(){}.getType();

Map<String, Object> map1 = gson.fromJson(jsonObject1, type);
Map<String, Object> map2 = gson.fromJson(jsonObject2, type);

//Flatten the maps
Map<String, Object> leftFlatMap = FlatMap.flatten(map1);
Map<String, Object> rightFlatMap = FlatMap.flatten(map2);
        
//Check differences between both maps
MapDifference<String, Object> difference = Maps.difference(leftFlatMap, rightFlatMap);

一切正常,并且(几乎)正确比较了所有元素。 问题是当 HashMap 中的一个元素是一个映射数组并且元素相同但顺序不同时。像这样:

第一个 JSON:

{    "body":[
      {
         "primitive":"VALUE",
         "jsonArray":[
            {
               "element":83284180
            },
            {
               "anotherElement":20832841804
            }
         ]
      }    
    ] 
 }

第二个 JSON:

{
   "body":[
      {
         "primitive":"VALUE",
         "jsonArray":[
            {  
               "anotherElement":20832841804
            },
            {
               "element":83284180
            }  
         ]
      }
   ]
}

如您所见,element 和 anotherElement 的值相同,但由于它们在数组中出现的顺序不同,因此差异显示错误。

之前有没有可能对数组进行排序?或任何其他解决方案?

提前致谢!!

【问题讨论】:

  • FlatMap.flatten() 来自哪里?
  • 如果您可以将每个列表映射到一个集合,您就可以克服这个问题。
  • 我使用 FlatMap 只是为了使结果更具可读性,如果我不使用它,结果将类似于:body: ([{primitive=VALUE, jsonArray=[{element=8.328418E7}, {anotherElement=2.0832841804E10}]}], [{primitive=VALUE, jsonArray=[{anotherElement=2.0832841804E10}, {element=8.328418E7}]}]) 而使用 Flatmap:/body/0/jsonArray/0/element: 8.328418E7 /body/0/jsonArray/1/anotherElement: 2.0832841804E10

标签: java json hashmap gson guava


【解决方案1】:

一个可能的解决方案可能是对内部子数组进行排序,以便它会影响反序列化的地图(但是,我认为在这种情况下用 JSON 对象制作地图可能不是一个好主意由于反序列化成本和策略不一定代表原始 JSON 对象)。

假设jsonObject1jsonObject2JsonElement 实现,您可以对其后代进行排序。

@UtilityClass
public final class JsonElements {

    public static List<JsonElement> asListView(final JsonArray jsonArray) {
        return new JsonArrayListView(jsonArray);
    }

    public static void sort(final JsonArray jsonArray, final Comparator<? super JsonElement> comparator) {
        Collections.sort(asListView(jsonArray), comparator);
    }

    @AllArgsConstructor(access = AccessLevel.PRIVATE)
    private static final class JsonArrayListView
            extends AbstractList<JsonElement> {

        private final JsonArray jsonArray;

        @Override
        public JsonElement get(final int index) {
            return jsonArray.get(index);
        }

        @Override
        public int size() {
            return jsonArray.size();
        }

        @Override
        @SuppressWarnings("MethodDoesntCallSuperMethod")
        public JsonElement set(final int index, final JsonElement element) {
            return jsonArray.set(index, element);
        }

    }

}
public final class JsonElementsTest {

    private static final Gson gson = new GsonBuilder()
            .disableHtmlEscaping()
            .disableInnerClassSerialization()
            .create();

    private static final Type stringToObjectMapType = new TypeToken<Map<String, Object>>() {}.getType();

    @Test
    public void testSort()
            throws IOException {
        final JsonElement jsonElement1 = ... read the 1st JSON document ...;
        final JsonElement jsonElement2 = ... read the 2nd JSON document ...;
        final JsonArray jsonSubArray1 = getSubArray(jsonElement1);
        final JsonArray jsonSubArray2 = getSubArray(jsonElement2);
        Assertions.assertNotEquals(jsonSubArray1, jsonSubArray2);
        JsonElements.sort(jsonSubArray1, JsonElementsTest::compare);
        JsonElements.sort(jsonSubArray2, JsonElementsTest::compare);
        final Map<String, Object> map1 = gson.fromJson(jsonElement1, stringToObjectMapType);
        final Map<String, Object> map2 = gson.fromJson(jsonElement2, stringToObjectMapType);
        Assertions.assertEquals(map1, map2);
    }

    private static JsonArray getSubArray(final JsonElement jsonElement) {
        return jsonElement.getAsJsonObject()
                .get("body")
                .getAsJsonArray()
                .get(0)
                .getAsJsonObject()
                .get("jsonArray")
                .getAsJsonArray();
    }

    private static int compare(final JsonElement jsonElement1, final JsonElement jsonElement2)
            throws IllegalArgumentException {
        final JsonObject jsonObject1 = jsonElement1.getAsJsonObject();
        final int size1 = jsonObject1.size();
        if ( size1 != 1 ) {
            throw new IllegalArgumentException("Size-1 must equal 1, but was " + size1);
        }
        final JsonObject jsonObject2 = jsonElement2.getAsJsonObject();
        final int size2 = jsonObject2.size();
        if ( size2 != 1 ) {
            throw new IllegalArgumentException("Size-2 must equal 2, but was " + size2);
        }
        // TODO optimize somehow
        final String key1 = jsonObject1.keySet().iterator().next();
        final String key2 = jsonObject2.keySet().iterator().next();
        return key1.compareTo(key2);
    }

}

如有必要,还可以考虑对后代进行递归排序。

请注意,您还可能拥有给定 JSON 文档的映射,但我不认为这是您的情况,但如果是,那么您可能需要申请 @JsonAdapter 来申请一个特殊的排序反序列化器(但我仍然认为这不是一个好主意)。否则,也可以为给定的JsonObjects 创建一个地图视图,以便它可以生成递归重新排序视图。

【讨论】:

  • 谢谢!!我能够使用集合并对其进行排序来完成我正在寻找的事情。您的回复很有帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-23
  • 2014-07-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多