2012-03-27 5 views

ответ

32

Один из способов заключается в использовании ObjectReader так:

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

, который будет объединять данные из двух источников. Это делает только мелкую копию, то есть не выполняет рекурсивное слияние с содержащимися объектами.

В противном случае вам может потребоваться просто прочитать JSON в виде дерева (JsonNode), перебрать содержимое и слить вручную. Это часто имеет смысл, так как правила слияния не являются тривиальными, и у каждого есть свои идеи о том, как слияние должно работать.

EDIT: (03-Apr-2017)

Согласно комментарий @Fernando Коррейа, есть на самом деле новый feature добавил в предстоящем Джексон 2.9 (который будет выпущен в апреле или мае 2017 года), что делает позволяют наконец, глубокое слияние.

+1

Благодарим вас за ответ. Мне нужно глубокое слияние, поэтому вы предлагаете написать слияние вручную, эхо, что у меня было, когда я разместил вопрос. Мои базовые структуры данных - это Карты, поэтому стоит написать общую процедуру слияния для java-карт. Это может оказаться пригодным для повторного использования в будущем проекте. – Danish

+0

есть глубокое слияние, доступное как функция сейчас? – yathirigan

+0

Нет, не от Jackson 2.6. – StaxMan

47

Вдохновленный ответом StaxMans, я реализовал этот метод слияния.

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; 
} 

Надеюсь, это поможет кому-то.

+0

Выглядит неплохо @Arn! Будет полезно, если мне нужно сделать это в будущем проекте. Когда я задал этот вопрос, я работал с elasticsearch, и я нашел для него плагин для частичного обновления. Это помогло мне в то время. – Danish

+3

Отлично, Арне, это именно то, что мне нужно, вы спасли мне часы расследования, спасибо. Чтобы сделать это немного больше, чем комментарий «спасибо», как и Jackson 2.4 'put()' устарел и должен быть заменен на 'replace()'.Кроме того, для пользователей JDK8 код может быть более кратким при тривиальном вызове 'forEachRemaining()' непосредственно на итераторе и передаче в несколько более коротком лямбда-выражении. – quantum

7

Вдохновленный ответом Арна. Редактируйте его, чтобы добавить случай, когда узел может иметь в себе массив узлов.

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; 
} 
+0

Кастинг 'valueToBeUpdated' в' ArrayNode' небезопасен. Вы можете получить «TextNode», например, вместо «ArrayNode» в этой переменной. – PNS

+0

Спасибо PNS. Я отредактировал свой ответ и добавил условие, чтобы проверить, является ли valueToBeUpdated arrayNode –

4

Ниже представлена ​​реализация в Скале. Исходный и целевой узлы в основном являются коммутативными, за исключением случаев, когда ветвь существует как в исходном, так и в целевом.

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 
    } 
    } 
1

Если кто-то просто хочет добавить два или более объекта 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); 
0

Здесь, является полная реализация объединения двух 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()); 
    } 
} 
Смежные вопросы