【问题标题】:Merge multiple JSONObjects (possibly containing arrays) in Java [duplicate]在Java中合并多个JSONObjects(可能包含数组)[重复]
【发布时间】:2021-09-29 01:13:33
【问题描述】:

鉴于此 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",
    "telephone/us"
  ],
  "financial_data": [
    "credit_card_numbers",
    "bank_account_number/ca",
    "bank_account_number/uk",
    "bank_account_number/all"
  ],
  "government_id": [
    "driving_license/americas"
  ],
  "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",
    "bank_account_number/uk",
    "bank_account_number/ca",
    "credit_card_numbers"
  ]
...

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

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

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

【问题讨论】:

    标签: java json merge gson org.json


    【解决方案1】:

    实际上,它不会起作用。问题出在这里:

    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 中相同键的值。也就是说,它不会工作。

    您可以做的是,您可以检查键是否已经在地图中具有值。如果此键没有现有的 JSON 对象,只需将其放入 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));
    }
    

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

    【讨论】:

    • 谢谢,不过我看不到这个方法:org.json.JSONArray.toJSONString?
    • org.json.simple包下
    • 我正在使用 org.json.JSONObject - 你的例子是使用 org.json.simple.JSON 吗?例如我正在调用 JSONObject.getNames(firstJsonObject) 之类的方法 - 我是否需要重写这些方法才能使用 org.json.simple.JSON?
    • 不,org.json.simple.JSONArray.toJSONString() 返回字符串,org.json.JSONObject 可以接受。所以这里没有其他变化。
    • 看来你在这里使用org.json.simple 等等:JSONObject jsonObject = merged.get(nextJsonObjectFieldName);
    【解决方案2】:

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

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

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

    【讨论】:

      猜你喜欢
      • 2011-01-25
      • 1970-01-01
      • 2014-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多