Unione di due documenti JSON utilizzando Jackson

È ansible unire due documenti JSON con la libreria Jackson JSON? Fondamentalmente sto usando il mappatore Jackson con semplici mappe Java.

Ho provato a cercare nella documentazione di Google e Jackson ma non ho trovato nulla.

Un modo è utilizzare ObjectReader modo:

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

che combinerà i dati da due fonti. Questo fa solo una copia superficiale, cioè non fa un’unione ricorsiva su oggetti contenuti.

Altrimenti potrebbe essere necessario leggere semplicemente JSON come albero ( JsonNode ), JsonNode loop sui contenuti e unirli manualmente. Questo ha spesso senso poiché le regole di fusione non sono banali e ognuno ha le proprie idee su come la fusione dovrebbe funzionare.

EDIT : (03-Apr-2017)

Come per il commento di @Fernando Correia, c’è in realtà una nuova funzionalità aggiunta in imminente Jackson 2.9 (che sarà rilasciata in aprile o maggio 2017) che consente una fusione profonda, finalmente.

Ispirato dalla risposta StaxMans ho implementato questo metodo di fusione.

 public static JsonNode merge(JsonNode mainNode, JsonNode updateNode) { Iterator 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; } 

Spero che questo aiuti qualcuno.

Ispirato alla risposta di Arn. Modificandolo per aggiungere il caso in cui un nodo può avere una matrice di nodes in esso.

 public static JsonNode merge(JsonNode mainNode, JsonNode updateNode) { Iterator 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; } 

Di seguito è una implementazione in Scala. I nodes di origine e di destinazione sono per lo più commutativi tranne quando esiste un ramo sia nell’origine che nella destinazione.

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

Se qualcuno vuole semplicemente aggiungere due o più oggetti JsonNode in un JsonNode, questo può essere un approccio:

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

Qui, è l’implementazione completa di unire due alberi JSON in uno. Spero che sarebbe utile 🙂

 /** * Merge two JSON tree into one ie mergedInTo. * * @param toBeMerged * @param mergedInTo */ public static void merge(JsonNode toBeMerged, JsonNode mergedInTo) { Iterator> incomingFieldsIterator = toBeMerged.fields(); Iterator> mergedIterator = mergedInTo.fields(); while (incomingFieldsIterator.hasNext()) { Map.Entry incomingEntry = incomingFieldsIterator.next(); JsonNode subNode = incomingEntry.getValue(); if (subNode.getNodeType().equals(JsonNodeType.OBJECT)) { boolean isNewBlock = true; mergedIterator = mergedInTo.fields(); while (mergedIterator.hasNext()) { Map.Entry 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 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 toBeMerged) { toBeMerged.setValue(valueToBePlaced); } private static void updateObject(JsonNode mergeInTo, ValueNode valueToBePlaced, Map.Entry toBeMerged) { boolean newEntry = true; Iterator> mergedIterator = mergeInTo.fields(); while (mergedIterator.hasNext()) { Map.Entry entry = mergedIterator.next(); if (entry.getKey().equals(toBeMerged.getKey())) { newEntry = false; entry.setValue(valueToBePlaced); } } if (newEntry) { ((ObjectNode) mergeInTo).replace(toBeMerged.getKey(), toBeMerged.getValue()); } }