Fusion de deux Documents JSON en utilisant Jackson
6 réponses
Une façon est d'utiliser des ObjectReader
comme suit:
MyBean defaults = objectMapper.readValue(defaultJson, MyBean.class);
ObjectReader updater = objectMapper.readerForUpdating(defaults);
MyBean merged = updater.readValue(overridesJson);
qui combinera des données provenant de deux sources. Cela ne fait qu'une copie superficielle, c'est-à-dire ne fait pas de fusion récursive sur des objets contenus.
sinon vous pourriez avoir besoin de simplement lire JSON comme un arbre ( JsonNode
), boucler le contenu et fusionner manuellement. Cela a souvent du sens de toute façon puisque les règles de fusion ne sont pas anodines, et chacun a sa propre idée de la façon dont la fusion devrait fonctionner.
MODIFIER : (03-Avr-2017)
comme pour le commentaire de @Fernando Correia, il y a en fait une nouvelle fonctionnalité ajoutée dans Jackson 2.9 à venir (à paraître en avril ou mai 2017) qui permet une fusion profonde, enfin.
inspiré par la réponse de StaxMans j'ai mis en œuvre cette méthode de fusion.
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;
}
J'espère que ça aidera quelqu'un.
inspiré de la réponse D'Arn. De l'éditer pour ajouter le cas où un nœud peut avoir un tableau de nœuds.
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;
}
ci-dessous est une mise en œuvre dans Scala. Le noeud source et cible sont principalement commutatifs, sauf lorsqu'une branche existe à la fois dans source et cible.
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
}
}
si quelqu'un veut simplement ajouter deux ou plusieurs objets JsonNode dans un jsonnode, cela peut être une approche:
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);
ici, c'est la mise en œuvre complète de la fusion de deux arbres JSON en un seul. Espérons qu'il serait utile :)
/**
* 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());
}
}