【问题标题】:Merging Two JSON Documents Using Jackson使用 Jackson 合并两个 JSON 文档
【发布时间】:2012-03-27 18:03:12
【问题描述】:

是否可以将两个 JSON 文档与 Jackson JSON 库合并?我基本上是在使用带有简单 Java 地图的 Jackson 映射器。

我尝试在 Google 和 Jackson 的文档中进行搜索,但找不到任何内容。

【问题讨论】:

    标签: java json jackson


    【解决方案1】:

    受 StaxMans answer 的启发,我实现了这种合并方法。

    public static JsonNode merge(JsonNode mainNode, JsonNode updateNode) {
    
        Iterator<String> fieldNames = updateNode.fieldNames();
        while (fieldNames.hasNext()) {
    
            String fieldName = fieldNames.next();
            JsonNode jsonNode = mainNode.get(fieldName);
            // if field exists and is an embedded object
            if (jsonNode != null && jsonNode.isObject()) {
                merge(jsonNode, updateNode.get(fieldName));
            }
            else {
                if (mainNode instanceof ObjectNode) {
                    // Overwrite field
                    JsonNode value = updateNode.get(fieldName);
                    ((ObjectNode) mainNode).put(fieldName, value);
                }
            }
    
        }
    
        return mainNode;
    }
    

    希望这对某人有所帮助。

    【讨论】:

    • 看起来不错@Arn!如果我需要在未来的项目中这样做,这将很有帮助。当我问这个问题时,我正在使用 elasticsearch,我找到了一个部分更新插件。当时这对我有帮助。
    • 太好了,Arne,这正是我所需要的,您为我节省了数小时的调查时间,谢谢。为了使这比thank you 评论多一点,从Jackson 2.4 开始,put() 已被弃用,应替换为replace()。此外,对于 JDK8 用户,代码可以更加简洁,只需在迭代器上直接调用 forEachRemaining() 并传入更短的 lambda 表达式。
    • 非常感谢。这对嵌入式数组有用吗?在向数组中添加元素和删除元素时?还是我们必须替换整个数组?
    【解决方案2】:

    一种方法是像这样使用ObjectReader

    MyBean defaults = objectMapper.readValue(defaultJson, MyBean.class);
    ObjectReader updater = objectMapper.readerForUpdating(defaults);
    MyBean merged = updater.readValue(overridesJson);
    

    这将结合来自两个来源的数据。这只会进行浅拷贝,即不对包含的对象进行递归合并。

    否则,您可能需要将 JSON 读取为树 (JsonNode),循环内容并手动合并。无论如何,这通常是有道理的,因为合并规则并非微不足道,而且每个人对合并应该如何工作都有自己的想法。

    编辑:(2017 年 4 月 3 日)

    根据@Fernando Correia 的评论,实际上在即将发布的 Jackson 2.9(将于 2017 年 4 月或 5 月发布)中添加了一个新的 feature,最终确实允许深度合并。

    【讨论】:

    • 感谢您的回答。我确实需要深度合并,因此建议您手动编写合并,这与我发布问题时的直觉相呼应。我的底层数据结构是 Maps,因此应该为 Java Maps 编写一个通用的深度合并例程。它可能被证明可以在未来的某个项目中重复使用。
    • 现在是否可以将深度合并作为一项功能使用?
    • 不,不是 Jackson 2.6。
    • 它是为(即将推出的)2.9 版实现的:github.com/FasterXML/jackson-databind/issues/1399
    • @FernandoCorreia 很好发现,确实如此!感谢您添加更新;我将在我的答案中添加一个编辑。
    【解决方案3】:

    受 Arn 回答的启发。编辑它以添加节点中可能包含节点数组的情况。

    public static JsonNode merge(JsonNode mainNode, JsonNode updateNode) {
    
        Iterator<String> fieldNames = updateNode.fieldNames();
    
        while (fieldNames.hasNext()) {
            String updatedFieldName = fieldNames.next();
            JsonNode valueToBeUpdated = mainNode.get(updatedFieldName);
            JsonNode updatedValue = updateNode.get(updatedFieldName);
    
            // If the node is an @ArrayNode
            if (valueToBeUpdated != null && valueToBeUpdated.isArray() && 
                updatedValue.isArray()) {
                // running a loop for all elements of the updated ArrayNode
                for (int i = 0; i < updatedValue.size(); i++) {
                    JsonNode updatedChildNode = updatedValue.get(i);
                    // Create a new Node in the node that should be updated, if there was no corresponding node in it
                    // Use-case - where the updateNode will have a new element in its Array
                    if (valueToBeUpdated.size() <= i) {
                        ((ArrayNode) valueToBeUpdated).add(updatedChildNode);
                    }
                    // getting reference for the node to be updated
                    JsonNode childNodeToBeUpdated = valueToBeUpdated.get(i);
                    merge(childNodeToBeUpdated, updatedChildNode);
                }
            // if the Node is an @ObjectNode
            } else if (valueToBeUpdated != null && valueToBeUpdated.isObject()) {
                merge(valueToBeUpdated, updatedValue);
            } else {
                if (mainNode instanceof ObjectNode) {
                    ((ObjectNode) mainNode).replace(updatedFieldName, updatedValue);
                }
            }
        }
        return mainNode;
    }
    

    【讨论】:

    • valueToBeUpdated 转换为ArrayNode 是不安全的。例如,您可以获得TextNode,而不是该变量中的ArrayNode
    • 感谢 PNS。我已经编辑了我的答案并添加了条件来检查 valueToBeUpdated 是否为 arrayNode
    • 酷 - 但如果 updateNode 以 ArrayNode 开头则不起作用,因为它们没有字段,只有子级。顺便说一句 - ObjectReader 更新对我有用。
    • 谢谢你,我今天要多睡一个小时,谢谢你,我的朋友!! :)
    【解决方案4】:

    以下是 Scala 中的实现。源节点和目标节点大多是可交换的,除非在源节点和目标节点中都存在分支。

      def mergeYamlObjects(source: ObjectNode, target: ObjectNode, overwrite: Boolean = true): ObjectNode = {
        if (target == null)
          source
        else if (source == null)
          target
        else {
          val result = source.deepCopy
          val fieldlist = source.fieldNames.asScala.toList ++ target.fieldNames.asScala.toList
          for (item <- fieldlist) {
            if (!(source has item)) {
              result put(item, target get item)
            } else {
              if ((source get item).isValueNode) {
                if (target has item)
                  if (overwrite)
                    result.put(item, target get item)
              } else {
                result.put(item, mergeYamlObjects(source.get(item).asInstanceOf[ObjectNode],
                  target.get(item).asInstanceOf[ObjectNode], overwrite = overwrite))
              }
            }
          }
          result
        }
      }
    

    【讨论】:

      【解决方案5】:

      如果有人只是想将两个或多个 JsonNode 对象添加到一个 JsonNode 中,这可以是一种方法:

      ArrayNode arrayNode = objectMapper.createArrayNode();
      arrayNode.add(firstJsonNode);
      arrayNode.add(secondJsonNode);
      arrayNode.add(thirdJsonNode);
      
      JsonNode root = JsonNodeFactory.instance.objectNode();
      ((ObjectNode) root).put("", arrayNode);
      System.out.println("merged array node #: " + root);
      

      【讨论】:

        【解决方案6】:

        这里是合并两个 JSON 树为一的完整实现。希望对您有所帮助:)

        /**
         * Merge two JSON tree into one i.e mergedInTo.
         *
         * @param toBeMerged
         * @param mergedInTo
         */
        public static void merge(JsonNode toBeMerged, JsonNode mergedInTo) {
            Iterator<Map.Entry<String, JsonNode>> incomingFieldsIterator = toBeMerged.fields();
            Iterator<Map.Entry<String, JsonNode>> mergedIterator = mergedInTo.fields();
        
            while (incomingFieldsIterator.hasNext()) {
                Map.Entry<String, JsonNode> incomingEntry = incomingFieldsIterator.next();
        
                JsonNode subNode = incomingEntry.getValue();
        
                if (subNode.getNodeType().equals(JsonNodeType.OBJECT)) {
                    boolean isNewBlock = true;
                    mergedIterator = mergedInTo.fields();
                    while (mergedIterator.hasNext()) {
                        Map.Entry<String, JsonNode> entry = mergedIterator.next();
                        if (entry.getKey().equals(incomingEntry.getKey())) {
                            merge(incomingEntry.getValue(), entry.getValue());
                            isNewBlock = false;
                        }
                    }
                    if (isNewBlock) {
                        ((ObjectNode) mergedInTo).replace(incomingEntry.getKey(), incomingEntry.getValue());
                    }
                } else if (subNode.getNodeType().equals(JsonNodeType.ARRAY)) {
                    boolean newEntry = true;
                    mergedIterator = mergedInTo.fields();
                    while (mergedIterator.hasNext()) {
                        Map.Entry<String, JsonNode> entry = mergedIterator.next();
                        if (entry.getKey().equals(incomingEntry.getKey())) {
                            updateArray(incomingEntry.getValue(), entry);
                            newEntry = false;
                        }
                    }
                    if (newEntry) {
                        ((ObjectNode) mergedInTo).replace(incomingEntry.getKey(), incomingEntry.getValue());
                    }
                }
                ValueNode valueNode = null;
                JsonNode incomingValueNode = incomingEntry.getValue();
                switch (subNode.getNodeType()) {
                    case STRING:
                        valueNode = new TextNode(incomingValueNode.textValue());
                        break;
                    case NUMBER:
                        valueNode = new IntNode(incomingValueNode.intValue());
                        break;
                    case BOOLEAN:
                        valueNode = BooleanNode.valueOf(incomingValueNode.booleanValue());
                }
                if (valueNode != null) {
                    updateObject(mergedInTo, valueNode, incomingEntry);
                }
            }
        }
        
        private static void updateArray(JsonNode valueToBePlaced, Map.Entry<String, JsonNode> toBeMerged) {
            toBeMerged.setValue(valueToBePlaced);
        }
        
        private static void updateObject(JsonNode mergeInTo, ValueNode valueToBePlaced,
                                         Map.Entry<String, JsonNode> toBeMerged) {
            boolean newEntry = true;
            Iterator<Map.Entry<String, JsonNode>> mergedIterator = mergeInTo.fields();
            while (mergedIterator.hasNext()) {
                Map.Entry<String, JsonNode> entry = mergedIterator.next();
                if (entry.getKey().equals(toBeMerged.getKey())) {
                    newEntry = false;
                    entry.setValue(valueToBePlaced);
                }
            }
            if (newEntry) {
                ((ObjectNode) mergeInTo).replace(toBeMerged.getKey(), toBeMerged.getValue());
            }
        }
        

        【讨论】:

        • 嗨,也许在每个 getValue 上使用 deepCopy 会更好?
        猜你喜欢
        • 2022-09-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多