2012-03-27 22 views

Odpowiedz

32

Jednym ze sposobów jest użycie ObjectReader tak:

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

które łączą dane z dwóch źródeł. Powoduje to tylko płytką kopię, tzn. Nie rekurencyjne scalanie zawartych obiektów.

W przeciwnym razie może zajść potrzeba przeczytania JSON jako drzewa (JsonNode), zapętlić zawartość i scalić ręcznie. Często ma to sens, ponieważ zasady łączenia nie są trywialne i każdy ma własne pomysły na to, jak powinno działać scalanie.

EDIT (03-kwi-2017)

Zgodnie komentarzu @Fernando Correia, jest faktycznie nowy feature dodane w nadchodzącym Jackson 2,9 (ma zostać wydany w kwietniu lub maju 2017 roku), które zezwala głębokie scalanie.

+1

Dziękuję za odpowiedź. Potrzebuję głębokiego scalenia, dlatego sugerujesz, aby napisać scalenie ręcznie, echo, jakie czułem, gdy napisałem pytanie. Moją podstawową strukturą danych są Mapy, stąd warto napisać ogólną procedurę rutowania dla map Java. Może okazać się przydatny do ponownego wykorzystania w niektórych przyszłych projektach. – Danish

+0

to głębokie scalanie dostępne teraz jako funkcja? – yathirigan

+0

Nie, nie jak z Jackson 2.6. – StaxMan

47

Zainspirowany odpowiedzią StaxMans Zaimplementowałem tę metodę łączenia.

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

Mam nadzieję, że to pomoże komuś.

+0

Wygląda całkiem nieźle @Arn! Będzie to pomocne, jeśli będę musiał to zrobić w przyszłym projekcie. Kiedy zadałem to pytanie, pracowałem z elasticsearch i znalazłem dla niego wtyczkę częściowej aktualizacji. To mi pomogło w tym czasie. – Danish

+3

Doskonale, Arne, dokładnie to, czego potrzebowałem, oszczędziłeś mi godzin śledztwa, dziękuję. Aby uczynić to czymś więcej niż komentarzem "dziękuję", tak jak w Jackson 2.4 'put()' jest przestarzałe i powinno zostać zastąpione 'replace()'.Ponadto, dla użytkowników JDK8, kod mógłby być bardziej zwięzły z trywialnym wywołaniem 'forEachRemaining()' bezpośrednio w iteratorze i przekazywaniem w nieco krótszym wyrażeniu lambda. – quantum

7

Zainspirowany odpowiedzią Arn'a. Edytowanie w celu dodania przypadku, w którym węzeł może mieć tablicę węzłów.

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

Odlewanie 'valueToBeUpdated' do' ArrayNode' nie jest bezpieczne. Zamiast tego możesz uzyskać 'TextNode' zamiast' ArrayNode' w tej zmiennej. – PNS

+0

Dzięki PNS. Zmieniono moją odpowiedź i dodano warunek, aby sprawdzić, czy valueToBeUpdated jest parametrem arrayNode –

4

Poniżej znajduje się wdrożenie w Scali. Węzły źródłowy i docelowy są w większości przemienne, z wyjątkiem sytuacji, gdy oddział istnieje zarówno w źródle, jak i docelowym.

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

Jeśli ktoś po prostu chce dodać dwa lub więcej obiektów JsonNode w jednym JsonNode, może to być jedno podejście:

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

Tutaj jest pełna realizacja połączenia dwóch JSON drzewo w jednym. Mam nadzieję, że to będzie pomocne :)

/** 
* 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()); 
    } 
} 
Powiązane problemy