在 Java 中合并多个 JSONObjects可能包含数组

问题描述

鉴于此 JSON:

{
  "contact_data": [
    "address/all","telephone/us"
  ],"financial_data": [
    "bank_account_number/all","bank_account_number/uk","credit_card_numbers"
  ]
}

和这个 JSON:

{
  "financial_data": [
    "credit_card_numbers","bank_account_number/ca","bank_account_number/all"
  ],"government_id": [
    "driving_license/americas"
  ],"sensitive_data": [
    "racial_ethnic_origin"
  ]
}

我想合并这些看起来像这样:

{
  "contact_data": [
    "address/all","financial_data": [
    "credit_card_numbers","sensitive_data": [
    "racial_ethnic_origin"
  ]
}

我有以下几乎有效:

import org.json.JSONObject;
...
final List<String> jsonStrings = ...; // A list of the above sample JSONs
final List<JSONObject> jsonObjects = jsonStrings
      .stream()
      .map(JSONObject::new)
      // JSONObject.getNames() (called later on) will return null if JSONObject is empty,so filter out empty objects.
      .filter(jsonObject -> !jsonObject.isEmpty()) 
      .collect(Collectors.toList());
if (jsonObjects..size() > 1) {
    // Merge multiple JSONObjects: https://stackoverflow.com/a/2403453/12177456
    final JSONObject firstJsonObject = jsonObjects.get(0);
    final JSONObject merged = new JSONObject(firstJsonObject,JSONObject.getNames(firstJsonObject));
    final List<JSONObject> remainingJsonObjects = jsonObjects.subList(1,jsonObjects.size());
    for (final JSONObject nextJsonObject : remainingJsonObjects) {
        for (final String nextJsonObjectFieldName : JSONObject.getNames(nextJsonObject)) {
            merged.put(nextJsonObjectFieldName,nextJsonObject.get(nextJsonObjectFieldName));
        }
    }
    return merged;
} 

但是,我希望在 financial_data 中看到 4 个条目:

...
"financial_data": [
    "bank_account_number/all","credit_card_numbers"
  ]
...

相反,我只看到 3 个,bank_account_number/uk 不在合并结果中:

...
"financial_data": [
    "bank_account_number/all","credit_card_numbers"
  ]
...

我并没有坚持使用 org.json,如果使用 gson、jackson、普通 Java 地图更简单,我可以接受。

解决方法

实际上,这是行不通的。问题出在这里:

merged.put(nextJsonObjectFieldName,nextJsonObject.get(nextJsonObjectFieldName))

这会将带有键 nextJsonObjectFieldName 的条目替换为后面的条目。这就是您从第二个对象进入 financial_data 的原因。

{
    "financial_data": [
        "credit_card_numbers","bank_account_number/ca","bank_account_number/all"
    ]
}

您看到其他键没问题,因为其他键在两个 JSON 中具有完全相同的值。如果您在第二个 json 中也更改了其他键的值,您将看到合并的 JSON 将具有与第二个 json 相同的键的值。也就是说,它行不通。

您可以做的是,您可以检查该键是否已经在地图中具有值。如果此键没有现有的 JSONobject,只需将它放入 merged 中即可。否则,我们还有更多工作要做:

JSONObject jsonObject = merged.get(nextJsonObjectFieldName);
if (jsonObject != null) {
    final JSONObject finalObj = mergeJsonObjectCheckingFieldValues(jsonObject,nextJsonObject.get(nextJsonObjectFieldName));
    merged.put(nextJsonObjectFieldName,finalObj);
} else {
    merged.put(nextJsonObjectFieldName,nextJsonObject.get(nextJsonObjectFieldName));
}

mergeJsonObjectCheckingFieldValues 方法检查给定的两个 JSONObject 之间的每个元素,并比较它们是否相同。根据您的示例以及简单地回答这个问题,我假设每个 JSONObject 只不过是一个字符串列表。为此,我们将需要 objectMapper。因此,请确保您的项目中有 com.fasterxml.jackson.databind.ObjectMapper。因此,检查将是:

ObjectMapper mapper = new ObjectMapper();

public JSONObject mergeJsonObjectCheckingFieldValues(JSONObject jsonObject,JSONObject nextJsonObject)) {
    List<String> existingList = Arrays.asList(mapper
                    .readValue(jsonObject.toString(),String[].class));

    List<String> newList = Arrays.asList(mapper
                    .readValue(nextJsonObject.toString(),String[].class));
    
    List<String> toBeAdded = newList
                                .stream()
                                .filter(x -> !existingList.contains(x))
                                .collect(Collectors.toList());

    if(toBeAdded.size() > 0) {
        existingList.addAll(toBeAdded);
    }
    return new JSONObject(JSONArray.toJSONString(existingList));
}

这是您问题的可能解决方案。我还没有测试过,但代码应该差不多。

,

我在这里使用 gson 接受了已接受的答案:

https://stackoverflow.com/a/34092374/12177456

  • 处理递归合并 JsonObjects
  • 当密钥存在于两个映射中时处理冲突
  • 对非原始值(如数组)有特殊处理