All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.json.compare.matcher.JsonObjectMatcher Maven / Gradle / Ivy

The newest version!
package io.json.compare.matcher;

import com.fasterxml.jackson.databind.JsonNode;
import com.jayway.jsonpath.PathNotFoundException;
import io.json.compare.CompareMode;
import io.json.compare.JsonComparator;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

class JsonObjectMatcher extends AbstractJsonMatcher {

    private final Set matchedFieldNames = new HashSet<>();

    JsonObjectMatcher(JsonNode expected, JsonNode actual, JsonComparator comparator, Set compareModes) {
        super(expected, actual, comparator, compareModes);
    }

    @Override
    public List match() {
        List diffs = new ArrayList<>();

        Iterator> it = expected.fields();
        while (it.hasNext()) {
            Map.Entry entry = it.next();
            String expectedField = entry.getKey();
            JsonNode expectedValue = entry.getValue();
            UseCase fieldUseCase = getUseCase(expectedField);
            String expectedSanitizedField = sanitize(expectedField);
            Optional jsonPathExpression = extractJsonPathExp(expectedSanitizedField);
            List> candidateEntries = null;
            if (!jsonPathExpression.isPresent()) {
                candidateEntries = searchCandidatesByField(fieldUseCase, expectedSanitizedField, actual);
            }
            switch (fieldUseCase) {
                case MATCH_ANY:
                case MATCH:
                    if (!jsonPathExpression.isPresent()) {
                        if (candidateEntries.isEmpty()) {
                            diffs.add(String.format("Field '%s' was NOT FOUND", expectedField));
                        } else {
                            diffs.addAll(matchWithCandidates(expectedSanitizedField, expectedValue, candidateEntries));
                        }
                    } else {
                        try {
                            diffs.addAll(new JsonPathMatcher(jsonPathExpression.get(), expectedValue, actual, comparator, compareModes).match());
                        } catch (PathNotFoundException e) {
                            diffs.add(String.format("Json path '%s' -> %s", jsonPathExpression.get(), e.getMessage()));
                        }
                    }
                    break;
                case DO_NOT_MATCH_ANY:
                    if (expected.size() - getDoNotMatchUseCases(expected) < actual.size()) {
                        diffs.add(String.format("Expected condition '%s' was not met. Actual JSON OBJECT has extra fields", expectedField));
                    }
                    break;
                case DO_NOT_MATCH:
                    if (!jsonPathExpression.isPresent()) {
                        if (!candidateEntries.isEmpty()) {
                            diffs.add(String.format("Field '%s' was FOUND", expectedField));
                        }
                    } else {
                        try {
                            new JsonPathMatcher(jsonPathExpression.get(), expectedValue, actual, comparator, compareModes).match();
                        } catch (PathNotFoundException e) {
                            break;
                        }
                        diffs.add(String.format("Json path '%s' was FOUND", expectedField));
                    }
                    break;
            }
        }
        if (compareModes.contains(CompareMode.JSON_OBJECT_NON_EXTENSIBLE) && expected.size() - getDoNotMatchUseCases(expected) < actual.size()) {
            diffs.add("Actual JSON OBJECT has extra fields");
        }
        return diffs;
    }

    private List matchWithCandidates(String expectedField, JsonNode expectedValue, List> candidates) {
        List diffs = new ArrayList<>();

        UseCase expectedValueUseCase = getUseCase(expectedValue);

        for (Map.Entry candidateEntry : candidates) {
            String candidateField = candidateEntry.getKey();

            if (expectedValueUseCase == UseCase.MATCH_ANY) {
                matchedFieldNames.add(candidateField);
                return Collections.emptyList();
            }

            JsonNode candidateValue = candidateEntry.getValue();
            List candidateDiffs = new JsonMatcher(expectedValue, candidateValue, comparator, compareModes).match();
            if (candidateDiffs.isEmpty()) {
                matchedFieldNames.add(candidateField);
                return Collections.emptyList();
            } else {
                candidateDiffs.forEach(diff -> diffs.add(String.format("%s -> %s", expectedField, diff)));
            }
        }
        return diffs;
    }

    private List> searchCandidatesByField(UseCase fieldUseCase, String fieldName, JsonNode target) {
        List> candidates = new ArrayList<>();
        Iterator> it = target.fields();
        while (it.hasNext()) {
            Map.Entry entry = it.next();
            String key = entry.getKey();
            if (matchedFieldNames.contains(key)) {
                continue;
            }
            if (fieldUseCase.equals(UseCase.MATCH_ANY) || comparator.compareFields(fieldName, key)) {
                candidates.add(entry);
            }
        }
        return candidates;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy