private void mutateNodes(final JsonNode parent,
                             final List<String> names,
                             final Function<String, String> valueMutator,
                             final String path) {

        if (parent instanceof ArrayNode) {
            for (int i = 0; i < parent.size(); i++) {
                mutateNodes(parent.get(i), names, valueMutator, path + "/" + i);
            }
        } else if (parent instanceof ObjectNode) {
            parent.fields().forEachRemaining(entry -> {
                final String valueNodePath = path + "/" + entry.getKey();
                if (names.contains(entry.getKey())) {
                    // found our node so mutate it
                    final String value = entry.getValue().textValue();
                    final String newValue = valueMutator.apply(value);
                    log("Replacing value for \"{}\": [{}] => [{}]",
                            valueNodePath, value, newValue);
                    ((ObjectNode) parent).put(entry.getKey(), newValue);
                } else {
                    // not our node so recurse into it
                    mutateNodes(entry.getValue(), names, valueMutator, path + "/" + entry.getKey());
                }
            });
        }
    }
Merge our compile time defaults with the de-serialised config so we have a full tree
stroom-proxy/stroom-proxy-app/src/main/java/stroom/proxy/app/ProxyConfigurationSourceProvider.java