private Stream<String> findDifferences(ObjectNode actual, ObjectNode expected, String prefix) {
        List<String> differences = new ArrayList<>();
        Set<String> actualKeys = actual.getMembers()
                .keySet()
                .stream()
                .map(StringNode::getValue)
                .collect(Collectors.toSet());
        Set<String> expectedKeys = expected.getMembers()
                .keySet()
                .stream()
                .map(StringNode::getValue)
                .collect(Collectors.toSet());

        Set<String> extraKeys = new HashSet<>(actualKeys);
        extraKeys.removeAll(expectedKeys);
        for (String extraKey : extraKeys) {
            differences.add(String.format(
                    "[%s]: Extra key `%s` encountered with content: %s",
                    prefix,
                    extraKey,
                    nodeToJson(actual.expectMember(extraKey), true)));
        }

        Set<String> missingKeys = new HashSet<>(expectedKeys);
        missingKeys.removeAll(actualKeys);
        for (String missingKey : missingKeys) {
            differences.add(String.format("[%s]: Expected key `%s` not present.", prefix, missingKey));
        }

        Set<String> sharedKeys = new HashSet<>(actualKeys);
        sharedKeys.retainAll(expectedKeys);

        return Stream.concat(differences.stream(),
                sharedKeys.stream()
                        .flatMap(key -> findDifferences(
                                actual.expectMember(key),
                                expected.expectMember(key),
                                String.format("%s/%s", prefix, key.replace("^", "^^").replace("/", "^/")))));
    }
Creates a line-by-line diff of two Node values.
smithy-model/src/main/java/software/amazon/smithy/model/node/NodeDiff.java