{"id":25776893,"url":"https://github.com/lukas-krecan/JsonUnit","last_synced_at":"2026-08-13T13:30:16.263Z","repository":{"id":2334410,"uuid":"3296177","full_name":"lukas-krecan/JsonUnit","owner":"lukas-krecan","description":"Compare JSON in your Unit Tests","archived":false,"fork":false,"pushed_at":"2026-07-31T02:04:47.000Z","size":4425,"stargazers_count":1003,"open_issues_count":17,"forks_count":117,"subscribers_count":15,"default_branch":"master","last_synced_at":"2026-08-06T21:54:04.704Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lukas-krecan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"claude":null,"gemini":null,"cursor":null,"copilot":null,"dco":null,"cla":null,"disclosure":null}},"created_at":"2012-01-29T08:33:07.000Z","updated_at":"2026-08-05T10:19:00.000Z","dependencies_parsed_at":"2026-08-06T13:41:30.289Z","dependency_job_id":null,"html_url":"https://github.com/lukas-krecan/JsonUnit","commit_stats":{"total_commits":1407,"total_committers":46,"mean_commits":30.58695652173913,"dds":"0.39232409381663114","last_synced_commit":"3598f5d197e9e828e59cdf0388e9b8006bf37e64"},"previous_names":[],"tags_count":162,"template":false,"template_full_name":null,"purl":"pkg:github/lukas-krecan/JsonUnit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lukas-krecan%2FJsonUnit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lukas-krecan%2FJsonUnit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lukas-krecan%2FJsonUnit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lukas-krecan%2FJsonUnit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lukas-krecan","download_url":"https://codeload.github.com/lukas-krecan/JsonUnit/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lukas-krecan%2FJsonUnit/sbom","scorecard":{"id":456988,"data":{"date":"2025-08-11","repo":{"name":"github.com/lukas-krecan/JsonUnit","commit":"b522dc35c98c5ec1b0a2e535cf44d792e706740f"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.5,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/ci.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":0,"reason":"Found 0/9 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":10,"reason":"24 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/lukas-krecan/JsonUnit/ci.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:20: update your workflow using https://app.stepsecurity.io/secureworkflow/lukas-krecan/JsonUnit/ci.yml/master?enable=pin","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.txt:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE.txt:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":1,"reason":"branch protection is not maximal on development and all release branches","details":["Warn: 'allow deletion' enabled on branch 'master'","Info: 'force pushes' disabled on branch 'master'","Info: 'branch protection settings apply to administrators' is required to merge on branch 'master'","Warn: could not determine whether codeowners review is allowed","Warn: no status checks found to merge onto branch 'master'","Warn: PRs are not required to make changes on branch 'master'; or we don't have data to detect it.If you think it might be the latter, make sure to run Scorecard with a PAT or use Repo Rules (that are always public) instead of Branch Protection settings"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 22 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-19T09:58:28.099Z","repository_id":2334410,"created_at":"2025-08-19T09:58:28.099Z","updated_at":"2025-08-19T09:58:28.099Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":36362473,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-08-06T04:43:03.162Z","status":"online","status_checked_at":"2026-08-07T02:00:06.708Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-02-27T06:01:32.832Z","updated_at":"2026-08-13T13:30:16.250Z","avatar_url":"https://github.com/lukas-krecan.png","language":"Java","funding_links":[],"categories":["项目","Testing","测试","Projects","Five Java Libraries to amplify your automated testing"],"sub_categories":["测试"],"readme":"JsonUnit [![Apache License 2](https://img.shields.io/badge/license-ASF2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0.txt) [![Build Status](https://github.com/lukas-krecan/JsonUnit/workflows/CI/badge.svg)](https://github.com/lukas-krecan/JsonUnit/actions) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/net.javacrumbs.json-unit/json-unit/badge.svg)](https://maven-badges.herokuapp.com/maven-central/net.javacrumbs.json-unit/json-unit)\n========\n\nJsonUnit is a library that simplifies JSON comparison in tests.\n\n- [APIs](#apis)\n  * [AssertJ integration](#assertj)\n  * [Hamcrest matchers](#hamcrest)\n  * [Spring MVC assertions](#spring)\n  * [Spring WebTestClient](#spring-web-client)\n  * [Spring RestTestClient](#spring-rest-client)\n  * [Spring REST client assertions](#spring-client)\n  * [Kotest assertions](#kotest)\n- [Features](#features)\n  * [JsonPath support](#jsonpath)\n  * [Ignoring values](#ignorevalues)\n  * [Ignoring elements](#ignoreelements)\n  * [Ignoring paths](#ignorepaths)\n  * [Regular expressions](#regexp)\n  * [Type placeholders](#typeplc)\n  * [Custom matchers](#matchers)\n  * [Options](#options)\n  * [Array indexing](#arrayIndexing)\n  * [Numerical comparison](#numbers)\n  * [Escaping dots](#dots)\n  * [Lenient parsing of expected value](#lenient-parsing-of-expected-value)\n  * [DifferenceListener](#differencelistener)\n  * [Jackson Object Mapper customization](#jackson-object-mapper-customization)\n- [Release notes](#release-notes)\n\n\n# \u003ca name=\"apis\"\u003e\u003c/a\u003eAPIs\nThere are several different APIs you can use. They all have more or less the same features, just the usage is\nslightly different.\n\n## \u003ca name=\"assertj\"\u003e\u003c/a\u003eAssertJ integration\nThe recommended API is AssertJ integration which combines the power of JsonUnit and AssertJ.\n\n```java\nimport static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;\nimport static net.javacrumbs.jsonunit.assertj.JsonAssertions.json;\n\n...\n\n// compares two JSON documents (note lenient parsing of expected value)\nassertThatJson(\"{\\\"a\\\":1, \\\"b\\\":2}\").isEqualTo(\"{b:2, a:1}\");\n\n// objects are automatically serialized before comparison\nassertThatJson(jsonObject).isEqualTo(\"{\\n\\\"test\\\": 1\\n}\");\n\n// AssertJ map assertions (numbers are converted to BigDecimals)\nassertThatJson(\"{\\\"a\\\":1}\").isObject().containsEntry(\"a\", BigDecimal.valueOf(1));\n\n// Type placeholders\nassertThatJson(\"{\\\"a\\\":1, \\\"b\\\": {\\\"c\\\" :3}}\")\n    .isObject().containsValue(json(\"{\\\"c\\\" :\\\"${json-unit.any-number}\\\"}\"));\n\n// AssertJ string assertion\nassertThatJson(\"{\\\"a\\\": \\\"value\\\"\")\n    .node(\"a\").isString().isEqualTo(\"value\");\n\n// AssertJ array assertion\nassertThatJson(\"{\\\"a\\\":[{\\\"b\\\": 1}, {\\\"c\\\": 1}, {\\\"d\\\": 1}]}\")\n    .node(\"a\").isArray().contains(json(\"{\\\"c\\\": 1}\"));\n\n// Can ignore array order\nassertThatJson(\"{\\\"a\\\":[{\\\"b\\\": 1}, {\\\"c\\\": 1}, {\\\"d\\\": 1}]}\")\n    .when(Option.IGNORING_ARRAY_ORDER).node(\"a\").isArray()\n    .isEqualTo(json(\"[{\\\"c\\\": 1}, {\\\"b\\\": 1} ,{\\\"d\\\": 1}]\"));\n\n// custom matcher\nassertThatJson(\"{\\\"test\\\":-1}\")\n    .withConfiguration(c -\u003e c.withMatcher(\"positive\", greaterThan(valueOf(0))))\n    .isEqualTo(\"{\\\"test\\\": \\\"${json-unit.matches:positive}\\\"}\");\n\n// and\nassertThatJson(\"{\\\"test\\\":{\\\"a\\\":1, \\\"b\\\":2, \\\"c\\\":3}}\").and(\n    a -\u003e a.node(\"test.a\").isEqualTo(1),\n    a -\u003e a.node(\"test.b\").isEqualTo(2)\n);\n\n// JsonPath support\nassertThatJson(json)\n    .inPath(\"$.store.book\")\n    .isArray()\n    .contains(json(\n        \"            {\\n\" +\n            \"                \\\"category\\\": \\\"reference\\\",\\n\" +\n            \"                \\\"author\\\": \\\"Nigel Rees\\\",\\n\" +\n            \"                \\\"title\\\": \\\"Sayings of the Century\\\",\\n\" +\n            \"                \\\"price\\\": 8.96\\n\" +\n            \"            }\"\n    ));\n```\n\nJsonUnit tries to be clever when parsing the expected value. If the value can be parsed as valid JSON, it's\nparsed so. If it can't be parsed, it's considered to be just a string to be compared. It usually works,\nbut it can lead to unexpected situations, usually with primitive values like numbers and booleans.\n\n```java\n// This test does NOT pass. \"1\" is parsed as JSON containing number 1, the actual value is a string.\nassertThatJson(\"{\\\"id\\\":\\\"1\\\", \\\"children\\\":[{\\\"parentId\\\":\\\"1\\\"}]}\")\n    .inPath(\"children[*].parentId\")\n    .isArray()\n    .containsOnly(\"1\");\n\n// You have to wrap the expected value by `JsonAssertions.value()`\n// to prevent parsing\nassertThatJson(\"{\\\"id\\\":\\\"1\\\", \\\"children\\\":[{\\\"parentId\\\":\\\"1\\\"}]}\")\n    .inPath(\"children[*].parentId\")\n    .isArray()\n    .containsOnly(value(\"1\"));\n\n// \"true\" is valid JSON so it gets parsed to primitive `true`\n// Have to wrap it to JsonAssertions.value() in order to make sure it's not parsed\nassertThatJson(\"{\\\"root\\\":[\\\"true\\\"]}\").node(\"root\").isArray().containsExactly(value(\"true\"));\n```\n\nOn the other hand, if you want to make sure that the expected value is parsed as JSON, use `JsonAssertions.json()`.\n\n### Support for asInstanceOf\nYou can move between vanilla AssertJ and JsonUnit using `asInstanceOf` as in the following example:\n```java\nimport static net.javacrumbs.jsonunit.assertj.JsonAssertions.JSON;\nimport static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;\n\n...\n\nrecord DummyResponse(String trackingId, String json) {}\nDummyResponse resp = new DummyResponse(\"abcd-0001\", \"{ \\\"foo\\\": \\\"bar\\\" }\");\n\nassertThat(resp)\n        .hasFieldOrPropertyWithValue(\"trackingId\", \"abcd-0001\") // \u003c- Assertj API\n        .extracting(\"json\")\n        .asInstanceOf(JSON)\n        .isObject() // \u003c- JsonUnit API\n        .containsEntry(\"foo\", \"bar\");\n```\n\n\n### Kotlin support\nFollowing Kotlin API is supported (notice different import)\n\n```kotlin\n// Kotlin\nimport net.javacrumbs.jsonunit.assertj.assertThatJson\n\nassertThatJson(\"\"\"{\"root\":{\"a\":1, \"b\": 2}}\"\"\") {\n    isObject\n    node(\"root.a\").isEqualTo(1)\n    node(\"root.b\").isEqualTo(2)\n}\n```\n\n\nTo use AssertJ integration, import\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003enet.javacrumbs.json-unit\u003c/groupId\u003e\n    \u003cartifactId\u003ejson-unit-assertj\u003c/artifactId\u003e\n    \u003cversion\u003e6.1.0\u003c/version\u003e\n    \u003cscope\u003etest\u003c/scope\u003e\n\u003c/dependency\u003e\n```\nFor more examples see [the tests](https://github.com/lukas-krecan/JsonUnit/blob/master/tests/test-base/src/main/java/net/javacrumbs/jsonunit/test/base/AbstractAssertJTest.java).\n\n## \u003ca name=\"hamcrest\"\u003e\u003c/a\u003eHamcrests matchers\nYou use Hamcrest matchers in the following way\n\n```java\nimport static net.javacrumbs.jsonunit.JsonMatchers.*;\nimport static org.junit.Assert.*;\nimport static net.javacrumbs.jsonunit.core.util.ResourceUtils.resource;\n...\n\nassertThat(\"{\\\"test\\\":1}\", jsonEquals(\"{\\\"test\\\": 1}\"));\nassertThat(\"{\\\"test\\\":1}\", jsonPartEquals(\"test\", 1));\nassertThat(\"{\\\"test\\\":[1, 2, 3]}\", jsonPartEquals(\"test[0]\", 1));\n\nassertThat(\"{\\\"test\\\":{\\\"a\\\":1, \\\"b\\\":2, \\\"c\\\":3}}\",\n    jsonEquals(\"{\\\"test\\\":{\\\"b\\\":2}}\").when(IGNORING_EXTRA_FIELDS));\n\n// Can use other Hamcrest matchers too\nassertThat(\"{\\\"test\\\":1}\", jsonPartMatches(\"test\", is(valueOf(1))))\n\nassertThat(\"{\\\"test\\\":1}\", jsonEquals(resource(\"test.json\")));\n```\n\nTo use import\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003enet.javacrumbs.json-unit\u003c/groupId\u003e\n    \u003cartifactId\u003ejson-unit\u003c/artifactId\u003e\n    \u003cversion\u003e6.1.0\u003c/version\u003e\n    \u003cscope\u003etest\u003c/scope\u003e\n\u003c/dependency\u003e\n```\n\nFor more examples see [the tests](https://github.com/lukas-krecan/JsonUnit/blob/master/tests/test-base/src/main/java/net/javacrumbs/jsonunit/test/base/AbstractJsonMatchersTest.java).\n\n## \u003ca name=\"spring\"\u003e\u003c/a\u003eSpring MVC assertions\nJsonUnit supports Spring MVC test assertions. For example\n\n```java\nimport static net.javacrumbs.jsonunit.spring.JsonUnitResultMatchers.json;\n...\n\nmockMvc.perform(get(\"/sample\").andExpect(\n    json().isEqualTo(\"{\\\"result\\\":{\\\"string\\\":\\\"stringValue\\\", \\\"array\\\":[1, 2, 3],\\\"decimal\\\":1.00001}}\")\n);\nmockMvc.perform(get(\"/sample\").andExpect(\n    json().node(\"result.string2\").isAbsent()\n);\nmockMvc.perform(get(\"/sample\").andExpect(\n    json().inPath(\"$.result.array[1]\").isEqualTo(2)\n);\nmockMvc.perform(get(\"/sample\").andExpect(\n    json().node(\"result.array\").when(Option.IGNORING_ARRAY_ORDER).isEqualTo(new int[]{3, 2, 1})\n);\nmockMvc.perform(get(\"/sample\").andExpect(\n    json().node(\"result.array\").matches(everyItem(lessThanOrEqualTo(valueOf(4))))\n);\n```\n\nFollowing Kotlin DSL is supported:\n\n```kotlin\nmockMvc.get(path).andExpect {\n    jsonContent {\n        node(\"root\").isEqualTo(CORRECT_JSON)\n    }\n}\n```\n\nInside `jsonContent` you have access to all AssertJ API capabilities as described [here](#assertj).\n\nTo use import\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003enet.javacrumbs.json-unit\u003c/groupId\u003e\n    \u003cartifactId\u003ejson-unit-spring\u003c/artifactId\u003e\n    \u003cversion\u003e6.1.0\u003c/version\u003e\n    \u003cscope\u003etest\u003c/scope\u003e\n\u003c/dependency\u003e\n```\n\nFor more examples see [the tests](https://github.com/lukas-krecan/JsonUnit/blob/master/json-unit-spring/src/test/java/net/javacrumbs/jsonunit/spring/testit/MockMvcTest.java).\n\n## \u003ca name=\"spring-web-client\"\u003e\u003c/a\u003eSpring WebTestClient\nTo integrate with Spring WebTest client do\n\n```java\nimport static net.javacrumbs.jsonunit.spring.WebTestClientJsonMatcher.json;\n...\n\nclient.get().uri(path).exchange().expectBody().consumeWith(\n    json().isEqualTo(\"{\\\"result\\\":{\\\"string\\\":\\\"stringValue\\\", \\\"array\\\":[1, 2, 3],\\\"decimal\\\":1.00001}}\")\n);\nclient.get().uri(path).exchange().expectBody().consumeWith(\n    json().node(\"result.string2\").isAbsent()\n);\nclient.get().uri(path).exchange().expectBody().consumeWith(\n    json().inPath(\"$.result.array[1]\").isEqualTo(2)\n);\nclient.get().uri(path).exchange().expectBody().consumeWith(\n    json().node(\"result.array\").when(Option.IGNORING_ARRAY_ORDER).isEqualTo(new int[]{3, 2, 1})\n);\nclient.get().uri(path).exchange().expectBody().consumeWith(\n    json().node(\"result.array\").matches(everyItem(lessThanOrEqualTo(valueOf(4))))\n);\n```\n\nFor Kotlin, you can use our bespoke DSL\n\n```kotlin\nimport net.javacrumbs.jsonunit.spring.jsonContent\n...\nclient.get().uri(path).exchange().expectBody()\n    .jsonContent {\n        isEqualTo(CORRECT_JSON)\n    }\n```\n\nImport\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003enet.javacrumbs.json-unit\u003c/groupId\u003e\n    \u003cartifactId\u003ejson-unit-spring\u003c/artifactId\u003e\n    \u003cversion\u003e6.1.0\u003c/version\u003e\n    \u003cscope\u003etest\u003c/scope\u003e\n\u003c/dependency\u003e\n```\n\nFor more examples see [the tests](https://github.com/lukas-krecan/JsonUnit/blob/master/json-unit-spring/src/test/java/net/javacrumbs/jsonunit/spring/testit/WebTestClientTest.java).\n\n## \u003ca name=\"spring-rest-client\"\u003e\u003c/a\u003eSpring RestTestClient\nSpring RestTestClient is a new testing utility introduced in Spring Framework that combines features of MockMvc and WebTestClient. JsonUnit provides seamless integration with it.\n\n```java\nimport static net.javacrumbs.jsonunit.spring.RestTestClientJsonMatcher.json;\n...\n\nclient.get().uri(path).accept(MediaType.APPLICATION_JSON)\n    .exchange()\n    .expectBody()\n    .consumeWith(json().isEqualTo(CORRECT_JSON));\n\nclient.get().uri(path).exchange().expectBody()\n    .consumeWith(json().node(\"result.string\").isEqualTo(\"stringValue\"));\n\nclient.get().uri(path).exchange().expectBody()\n    .consumeWith(json().inPath(\"$.result.array[1]\").isEqualTo(2));\n\nclient.get().uri(path).exchange().expectBody()\n    .consumeWith(json().node(\"result.array\")\n        .when(Option.IGNORING_ARRAY_ORDER)\n        .isEqualTo(new int[]{3, 2, 1}));\n```\n\nFor Kotlin, you can use the DSL:\n\n```kotlin\nimport net.javacrumbs.jsonunit.spring.jsonContent\n...\n\nclient.get().uri(path).accept(APPLICATION_JSON)\n    .exchange()\n    .expectBody()\n    .jsonContent { isEqualTo(CORRECT_JSON) }\n\nclient.get().uri(path).exchange()\n    .expectBody()\n    .jsonContent {\n        node(\"result.string\").isString().isEqualTo(\"stringValue\")\n    }\n```\n\nTo use import\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003enet.javacrumbs.json-unit\u003c/groupId\u003e\n    \u003cartifactId\u003ejson-unit-spring\u003c/artifactId\u003e\n    \u003cversion\u003e6.1.0\u003c/version\u003e\n    \u003cscope\u003etest\u003c/scope\u003e\n\u003c/dependency\u003e\n```\n\nFor more examples see [the tests](https://github.com/lukas-krecan/JsonUnit/blob/master/json-unit-spring/src/test/java/net/javacrumbs/jsonunit/spring/testit/RestTestClientTest.java).\n\n## \u003ca name=\"spring-mockmvc-assertj\"\u003e\u003c/a\u003eSpring AssertJ for MockMvc\nSince version 4.0.0 JsonUnit supports Spring MockMvc AssertJ assertions.\nYou can use it like this:\n\n```java\nimport static net.javacrumbs.jsonunit.assertj.JsonAssertions.jsonUnitJson;\n\n...\n\nassertThat(mvc.get().uri(\"/sample\"))\n    .hasStatusOk()\n    .bodyJson()\n    .convertTo(jsonUnitJson()) // Switch to JsonUnit assert\n    .inPath(\"result.array\") // This is JsonUnit\n    .isArray()\n    .containsExactly(1, 2, 3);\n```\n\n## \u003ca name=\"spring-client\"\u003e\u003c/a\u003eSpring REST client assertions\n\n```java\nimport static net.javacrumbs.jsonunit.spring.JsonUnitRequestMatchers.json;\n...\nmockServer.expect(requestTo(URI))\n      .andExpect(json().isEqualTo(json))\n      .andRespond(withSuccess(jsonResponse, MediaType.APPLICATION_JSON_UTF8));\n```\n\nTo use import\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003enet.javacrumbs.json-unit\u003c/groupId\u003e\n    \u003cartifactId\u003ejson-unit-spring\u003c/artifactId\u003e\n    \u003cversion\u003e6.1.0\u003c/version\u003e\n    \u003cscope\u003etest\u003c/scope\u003e\n\u003c/dependency\u003e\n```\n\nFor more examples see [the tests](https://github.com/lukas-krecan/JsonUnit/blob/master/json-unit-spring/src/test/java/net/javacrumbs/jsonunit/spring/testit/ClientTest.java).\n\n## \u003ca name=\"kotest\"\u003e\u003c/a\u003eKotest assertions\nJsonUnit supports [Kotest assertions](https://kotest.io/docs/assertions/assertions.html).\n\nImport:\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003enet.javacrumbs.json-unit\u003c/groupId\u003e\n    \u003cartifactId\u003ejson-unit-kotest\u003c/artifactId\u003e\n    \u003cversion\u003e6.1.0\u003c/version\u003e\n    \u003cscope\u003etest\u003c/scope\u003e\n\u003c/dependency\u003e\n```\n\nAnd enjoy:\n\n```kotlin\n\"\"\"{\"test\":1}\"\"\" should equalJson(\"\"\"{\"test\": 1}\"\"\")\n\n// Provide configuration\n\"\"\"{\"test\":1.01}\"\"\" should equalJson(\"\"\"{\"test\":1}\"\"\", configuration { withTolerance(0.1) })\n\n// Use inPath\n\"\"\"{\"test\":1}\"\"\" inPath \"test\" should equalJson(\"1\")\n\n// Clues with nesting\n\"\"\"{\"test\": {\"nested\": 1}}\"\"\".inPath(\"test\").asClue {\n    it inPath \"nested\" should equalJson(\"2\")\n}\n\n\"\"\"{\"test\":1}\"\"\".inPath(\"test\").shouldBeJsonNumber()\n    // shouldBeJsonNumber returns BigDecimal, so we can use standard kotest assertions\n    // PLease note that numbers are converted to BigDecimals\n    .shouldBeEqualComparingTo(valueOf(1))\n\n// The same for arrays generated by JsonPath\n\"\"\"{\"test\": [{\"a\": \"a\"}, {\"a\": true}, {\"a\": null}, {\"a\": 4}]}\"\"\".inPath(\"$.test[*].a\")\n    .shouldBeJsonArray()\n    .shouldContainExactly(\"a\", true, null, valueOf(4))\n\n// ... and objects\n\"\"\"{\"a\":1, \"b\": true}\"\"\".shouldBeJsonObject().shouldMatchAll(\n    \"a\" to { it should beJsonNumber() },\n    \"b\" to { it should beJsonBoolean() }\n)\n```\n\nSee the [tests](https://github.com/lukas-krecan/JsonUnit/blob/master/json-unit-kotest/src/test/kotlin/net/javacrumbs/jsonunit/kotest/test/KotestTest.kt) for more examples.\n\n# Features\nMost JsonUnit features are available regardless of the API you use.\n\n## \u003ca name=\"jsonpath\"\u003e\u003c/a\u003eJsonPath support\nYou can use JsonPath navigation together with JsonUnit. It is supported by AssertJ, Spring integration, Kotest and the\ncore `JsonAssert` API. JsonPath is part of `json-unit-core`, so there is no separate `json-unit-json-path` module.\n\n```java\n// AssertJ style\nassertThatJson(json)\n    .inPath(\"$.store.book\")\n    .isArray()\n    .contains(json(\n        \"            {\\n\" +\n            \"                \\\"category\\\": \\\"reference\\\",\\n\" +\n            \"                \\\"author\\\": \\\"Nigel Rees\\\",\\n\" +\n            \"                \\\"title\\\": \\\"Sayings of the Century\\\",\\n\" +\n            \"                \\\"price\\\": 8.96\\n\" +\n            \"            }\"\n    ));\n// JsonAssert API\nassertJsonPartEquals(\"[1]\", \"{\\\"test\\\":{\\\"value\\\":1}}\", \"$..value\");\n\n// Spring\nmockMvc.perform(get(\"/sample\"))\n    .andExpect(json().inPath(\"$.result.array[1]\").isEqualTo(2));\n```\n\n```kotlin\n// Kotest\n\"\"\"{\"test\": [{\"a\": \"a\"}, {\"a\": true}, {\"a\": null}, {\"a\": 4}]}\"\"\" inPath \"$.test[*].a\" should\n    equalJson(\"\"\"[\"a\", true, null, 4]\"\"\")\n```\n\nJsonPath can also be used in path-based configuration, for example ignored paths or local options:\n\n```java\nassertThatJson(json)\n    .whenIgnoringPaths(\"$.store.book[*].price\")\n    .inPath(\"$.store.book[0]\")\n    .isEqualTo(\"\"\"\n        {\n            \"category\": \"reference\",\n            \"author\": \"Nigel Rees\",\n            \"title\": \"Sayings of the Century\",\n            \"price\": 0\n        }\"\"\");\n\nmockMvc.perform(get(\"/sample\"))\n    .andExpect(json().when(paths(\"$..array\"), then(IGNORING_ARRAY_ORDER))\n        .when(IGNORING_EXTRA_FIELDS)\n        .inPath(\"$.result\")\n        .isEqualTo(\"{array: [1, 3, 2]}\"));\n```\n\n## \u003ca name=\"ignorevalues\"\u003e\u003c/a\u003eIgnoring values\nSometimes you need to ignore certain values when comparing. It is possible to use `${json-unit.ignore}` or `#{json-unit.ignore}`\nplaceholder like this\n\n```java\n// AssertJ API\nassertThatJson(\"{\\\"a\\\":1}\")\n    .isEqualTo(json(\"{\\\"a\\\":\\\"${json-unit.ignore}\\\"}\"));\n```\nPlease note that the assertion will fail if the `test` element is missing in the actual value.\n\n## \u003ca name=\"ignoreelements\"\u003e\u003c/a\u003eIgnoring elements\nIf the element needs to be ignored completely you can use `${json-unit.ignore-element}`\nplaceholder.\n\n```java\n// AssertJ API\nassertThatJson(\"{\\\"root\\\":{\\\"test\\\":1, \\\"ignored\\\": null}}\")\n      .isEqualTo(\"{\\\"root\\\":{\\\"test\\\":1, \\\"ignored\\\": \\\"${json-unit.ignore-element}\\\"}}\");\n```\n\nThe assertion will not fail if the element is missing in the actual value.\n\n## \u003ca name=\"missingornull\"\u003e\u003c/a\u003eMissing or null\nIf the element should be missing or null, you can use `${json-unit.missing-or-null}`\nplaceholder.\n\n```java\n// AssertJ API\nassertThatJson(\"{\\\"root\\\":{\\\"test\\\":1}}\")\n      .isEqualTo(\"{\\\"root\\\":{\\\"test\\\":1, \\\"optional\\\": \\\"${json-unit.missing-or-null}\\\"}}\");\n```\n\nThe assertion will fail if the element has any non-null value. You can also use hash instead of dollar\n`#{json-unit.missing-or-null}` for example if you are using language with string interpolation like Kotlin.\n\n## \u003ca name=\"ignorepaths\"\u003e\u003c/a\u003eIgnoring paths\n\n`whenIgnoringPaths` configuration option makes JsonUnit ignore the specified paths in the actual value. If the path\nmatches, it's completely ignored. It may be missing, null or have any value. Also `when(paths(...), thenIgnore()` can be used.\n\n```java\n// AssertJ style\nassertThatJson(\"{\\\"root\\\":{\\\"test\\\":1, \\\"ignored\\\": 1}}\")\n    .whenIgnoringPaths(\"root.ignored\"))\n    .isEqualTo(\"{\\\"root\\\":{\\\"test\\\":1}}\");\n\n// Hamcrest matcher\nassertThat(\n  \"{\\\"root\\\":{\\\"test\\\":1, \\\"ignored\\\": 2}}\",\n  jsonEquals(\"{\\\"root\\\":{\\\"test\\\":1, \\\"ignored\\\": 1}}\").whenIgnoringPaths(\"root.ignored\")\n);\n```\n\nArray index placeholder\n```java\nassertThatJson(\"[{\\\"a\\\":1, \\\"b\\\":2},{\\\"a\\\":1, \\\"b\\\":3}]\")\n    .whenIgnoringPaths(\"[*].b\")\n    .isEqualTo(\"[{\\\"a\\\":1, \\\"b\\\":0},{\\\"a\\\":1, \\\"b\\\":0}]\");\n```\nPlease note that if you use JsonPath, you should start the path to be ignored by `$`. Full JsonPath syntax in\n`whenIgnoringPaths` and path-specific options is supported by AssertJ, JsonAssert, Spring and Kotest APIs. Hamcrest\nmatchers support exact paths and array index placeholder as described above.\n\nJsonPath with whenIgnoringPaths example:\n```java\n// AssertJ API\nassertThatJson(\"{\\\"fields\\\":[\" +\n        \"{\\\"key\\\":1, \\\"name\\\":\\\"AA\\\"},\" +\n        \"{\\\"key\\\":2, \\\"name\\\":\\\"AB\\\"},\" +\n        \"{\\\"key\\\":3, \\\"name\\\":\\\"AC\\\"}\" +\n    \"]}\")\n    .whenIgnoringPaths(\"$.fields[?(@.name=='AA')].key\")\n    .isEqualTo(\"{\\\"fields\\\":[\" +\n        \"{\\\"key\\\":2, \\\"name\\\":\\\"AA\\\"},\" +\n        \"{\\\"key\\\":2, \\\"name\\\":\\\"AB\\\"},\" +\n        \"{\\\"key\\\":3, \\\"name\\\":\\\"AC\\\"}\" +\n    \"]}\");\n```\n\n\n## \u003ca name=\"regexp\"\u003e\u003c/a\u003eRegular expressions\nIt is also possible to use regular expressions to compare string values\n\n```java\nassertThatJson(\"{\\\"test\\\": \\\"ABCD\\\"}\")\n    .isEqualTo(\"{\\\"test\\\": \\\"${json-unit.regex}[A-Z]+\\\"}\");\n```\n\n\nFor matching just part of the string, you can use this (we have to escape twice, once for Java, once for JSON)\n```java\nassertThatJson(\"{\\\"test\\\": \\\"This is some text followed by: ABCD, followed by this\\\"}\")\n            .isEqualTo(\"{\\\"test\\\": \\\"${json-unit.regex}^\\\\\\\\QThis is some text followed by: \\\\\\\\E[A-Z]+\\\\\\\\Q, followed by this\\\\\\\\E$\\\"}\");\n```\n\nSince this is quite hard to write, you can implement an expression builder like\n[this](https://github.com/lukas-krecan/JsonUnit/commit/75d68ef1852ade004e93ca42d676f4b996631974#diff-642a52fede8473f98c5a7b25f34c6bd68f33160cf22733127f7f7f13f3cb2fc6R713).\n\n\n## \u003ca name=\"typeplc\"\u003e\u003c/a\u003eType placeholders\nIf you want to assert just a type, but you do not care about the exact value, you can use any-* placeholder like this\n\n```java\nassertThatJson(\"{\\\"test\\\":\\\"value\\\"}\")\n    .isEqualTo(\"{test:'${json-unit.any-string}'}\");\n\nassertThatJson(\"{\\\"test\\\":true}\")\n    .isEqualTo(\"{\\\"test\\\":\\\"${json-unit.any-boolean}\\\"}\");\n\nassertThatJson(\"{\\\"test\\\":1.1}\")\n    .isEqualTo(\"{\\\"test\\\":\\\"${json-unit.any-number}\\\"}\");\n\nassertThatJson(\"{\\\"test\\\":[]]}\")\n    .isEqualTo(\"{\\\"test\\\":\\\"${json-unit.any-array}\\\"}\");\n\n```\n\nYou can also use hash instead of string `#{json-unit.any-string}` for example if you are using language with string interpolation\nlike Kotlin.\n\n## \u003ca name=\"matchers\"\u003e\u003c/a\u003eCustom matchers\nIn some special cases you might want to use your own matcher in the expected document.\n```java\n assertThatJson(\"{\\\"test\\\":-1}\")\n             .withMatcher(\"positive\", greaterThan(valueOf(0)))\n             .isEqualTo(\"{\\\"test\\\": \\\"${json-unit.matches:positive}\\\"}\");\n```\n\nIn even more special cases, you might want to parametrize your matcher.\n```java\n Matcher\u003c?\u003e divisionMatcher = new DivisionMatcher();\n assertThatJson(\"{\\\"test\\\":5}\")\n    .withMatcher(\"isDivisibleBy\", divisionMatcher)\n    .isEqualTo(\"{\\\"test\\\": \\\"${json-unit.matches:isDivisibleBy}3\\\"}\");\n\n private static class DivisionMatcher extends BaseMatcher\u003cObject\u003e implements ParametrizedMatcher {\n     private BigDecimal param;\n\n     public boolean matches(Object item) {\n         return ((BigDecimal)item).remainder(param).compareTo(ZERO) == 0;\n     }\n\n     public void describeTo(Description description) {\n         description.appendValue(param);\n     }\n\n     @Override\n     public void describeMismatch(Object item, Description description) {\n         description.appendText(\"It is not divisible by \").appendValue(param);\n     }\n\n     public void setParameter(String parameter) {\n         this.param = new BigDecimal(parameter);\n     }\n }\n```\nIf you need a matcher with more than one parameter, you can implement it\nlike [this](https://stackoverflow.com/a/66183629/277042).\n\n## \u003ca name=\"options\"\u003e\u003c/a\u003eOptions\n\nThere are multiple options how you can configure the comparison\n\n**TREATING_NULL_AS_ABSENT** - fields with null values are equivalent to absent fields. For example, this test passes\n\n```java\nassertThatJson(\"{\\\"test\\\":{\\\"a\\\":1, \\\"b\\\": null}}\")\n    .when(TREATING_NULL_AS_ABSENT)\n    .isEqualTo(\"{\\\"test\\\":{\\\"a\\\":1}}\");\n```\n\n**IGNORING_ARRAY_ORDER** - ignores order in arrays\n\n```java\nassertThatJson(\"{\\\"test\\\":[1,2,3]}\")\n    .when(IGNORING_ARRAY_ORDER)\n    .isEqualTo(\"{\\\"test\\\":[3,2,1]}\");\n```\n\n**IGNORING_EXTRA_ARRAY_ITEMS** - ignores unexpected array items\n```java\nassertThatJson(\"{\\\"test\\\":[1,2,3,4]}\")\n    .when(IGNORING_EXTRA_ARRAY_ITEMS)\n    .isEqualTo(\"{\\\"test\\\":[1,2,3]}\");\n\n\nassertThatJson(\"{\\\"test\\\":[5,5,4,4,3,3,2,2,1,1]}\")\n    .when(IGNORING_EXTRA_ARRAY_ITEMS, IGNORING_ARRAY_ORDER)\n    .isEqualTo(\"{\\\"test\\\":[1,2,3]}\");\n```\n\n**IGNORING_EXTRA_FIELDS** - ignores extra fields in the compared value\n\n```java\nassertThatJson(\"{\\\"test\\\":{\\\"a\\\":1, \\\"b\\\":2, \\\"c\\\":3}}\")\n    .when(IGNORING_EXTRA_FIELDS)\n    .isEqualTo(\"{\\\"test\\\":{\\\"b\\\":2}}\");\n```\n\n**IGNORE_VALUES** - ignores values and compares only types\n\n```java\nassertThatJson(\"{\\\"a\\\":2,\\\"b\\\":\\\"string2\\\"}\")\n    .when(paths(\"a\", \"b\"), then(IGNORING_VALUES))\n    .isEqualTo(\"{\\\"a\\\":1,\\\"b\\\":\\\"string\\\"}\");\n```\n\n**FAIL_FAST** - stops comparison at the first difference. May provide performance benefits for large JSONs and use-cases that\ndo not require to know all the differences.\n\nIt is possible to combine options.\n\n```java\nassertThatJson(\"{\\\"test\\\":[{\\\"key\\\":3},{\\\"key\\\":2, \\\"extraField\\\":2},{\\\"key\\\":1}]}\")\n    .when(IGNORING_EXTRA_FIELDS, IGNORING_ARRAY_ORDER)\n    .isEqualTo(\"{\\\"test\\\":[{\\\"key\\\":1},{\\\"key\\\":2},{\\\"key\\\":3}]}\");\n```\n\n**REPORTING_DIFFERENCE_AS_NORMALIZED_STRING** - reports errors as normalized strings. Applies some basic normalization\nto compared values and then reports full json strings, so you can see the difference in the IDE diff.\n\n\nIn Hamcrest assertion you can set the option like this\n\n```java\nassertThat(\"{\\\"test\\\":{\\\"a\\\":1, \\\"b\\\":2, \\\"c\\\":3}}\",\n    jsonEquals(\"{\\\"test\\\":{\\\"b\\\":2}}\").when(IGNORING_EXTRA_FIELDS));\n```\n\nYou can define options locally (for specific paths) by using `when(path(...), then(...))`:\n```java\n// ignore array order for [*].a\n// AssertJ\nassertThatJson(\"{\\\"test\\\":{\\\"a\\\":1,\\\"b\\\":2,\\\"c\\\":3}}\").when(paths(\"test.c\"), then(IGNORING_VALUES))\n    .isEqualTo(\"{\\\"test\\\":{\\\"a\\\":1,\\\"b\\\":2,\\\"c\\\":4}}\");\n// ignore array order everywhere but [*].b\nassertThatJson(\"[{\\\"b\\\":[4,5,6]},{\\\"b\\\":[1,2,3]}]\")\n    .when(IGNORING_ARRAY_ORDER)\n    .when(path(\"[*].b\"), thenNot(IGNORING_ARRAY_ORDER))\n    .isEqualTo(\"[{\\\"b\\\":[1,2,3]},{\\\"b\\\":[4,5,6]}]\");\n// ignore extra fields in the object \"a\"\nassertThatJson(\"{\\\"a\\\":{\\\"a1\\\":1,\\\"a2\\\":2},\\\"b\\\":{\\\"b1\\\":1,\\\"b2\\\":2}}\")\n    .when(path(\"a\"), then(IGNORING_EXTRA_FIELDS))\n    .isEqualTo(\"{\\\"a\\\":{\\\"a1\\\":1},\\\"b\\\":{\\\"b1\\\":1}}\"))\n// ignore extra array items in the array\nassertThatJson(\"{\\\"a\\\":[1,2,3]}\")\n    .when(path(\"a\"), then(IGNORING_EXTRA_ARRAY_ITEMS))\n    .isEqualTo(\"{\\\"a\\\":[1,2]}\");\n// Hamcrest\nassertThat(\"{\\\"test\\\":{\\\"a\\\":1,\\\"b\\\":2,\\\"c\\\":3}}\",\n    jsonEquals(\"{\\\"test\\\":{\\\"a\\\":1,\\\"b\\\":2,\\\"c\\\":4}}\").when(path(\"test.c\"), then(IGNORING_VALUES)));\n\n```\nNote that **TREATING_NULL_AS_ABSENT** and **IGNORING_VALUES** require exact paths to ignored fields:\n```java\n// ignoring number and str\nassertThatJson(\"{\\\"a\\\":2,\\\"b\\\":\\\"string2\\\"}\")\n    .when(paths(\"a\", \"b\"), then(IGNORING_VALUES))\n    .isEqualTo(\"{\\\"a\\\":1,\\\"b\\\":\\\"string\\\"}\");\n// treat null B as absent B\nassertThatJson(\"{\\\"A\\\":1,\\\"B\\\":null}\")\n    .when(path(\"B\"), then(TREATING_NULL_AS_ABSENT))\n    .isEqualTo(\"{\\\"A\\\":1}\");\n```\nAll other options require paths to objects or arrays where values or order should be ignored.\n\n## \u003ca name=\"arrayIndexing\"\u003e\u003c/a\u003eArray indexing\nYou can use negative numbers to index arrays form the end\n```java\nassertThatJson(\"{\\\"root\\\":{\\\"test\\\":[1,2,3]}}\")\n    .node(\"root.test[-1]\").isEqualTo(3);\n```\n\n## \u003ca name=\"numbers\"\u003e\u003c/a\u003eNumerical comparison\nNumbers are by default compared in the following way:\n\n* If the type differs, the number is different. So 1 and 1.0 are different (int vs. float). This does not apply when Moshi is used since it [parses all numbers as Doubles](https://github.com/square/moshi/issues/192).\n* Floating number comparison is exact, down to the scale - 1.0 and 1.00 are considered to be different.\n\nYou can change this behavior by setting tolerance. If you set tolerance to `0` two numbers are considered equal if they are\nequal mathematically even though they have different type or precision (`a.compareTo(b) == 0`).\n\n```java\nassertThatJson(\"{\\\"test\\\":1.00}\").node(\"test\").withTolerance(0).isEqualTo(1);\n```\n\nIf you set tolerance to non-zero value, the values are considered equal if `abs(a-b) \u003c= tolerance`.\n```java\nassertThatJson(\"{\\\"test\\\":1.00001}\").node(\"test\").withTolerance(0.001).isEqualTo(1);\n```\n\nOr you can use Hamcrest matcher\n\n```java\nimport static java.math.BigDecimal.valueOf;\n...\nassertThatJson(\"{\\\"test\\\":1.10001}\").node(\"test\")\n    .matches(closeTo(valueOf(1.1), valueOf(0.001)));\n```\n\nIf you are interested why 1 and 1.0 are treated as different numbers please read this [comment](https://github.com/lukas-krecan/JsonUnit/issues/229#issuecomment-623882801).\n\nIf you want to have special handling of numerical values, you can inject your own number comparator.\n\n```java\nassertThatJson(\"{\\\"a\\\":1.0}\")\n    .withConfiguration(c -\u003e c.withNumberComparator(numberComparator))\n    .isEqualTo(\"{\\\"a\\\":1.00}\");\n```\n\n## \u003ca name=\"dots\"\u003e\u003c/a\u003eEscaping dots\nSometimes you have dots in JSON element names, and you need to address those elements. It is possible to escape dots like this\n\n```java\nassertThatJson(\"{\\\"name.with.dot\\\": \\\"value\\\"}\").node(\"name\\\\.with\\\\.dot\").isStringEqualTo(\"value\");\n```\n\n## \u003ca name=\"lenient\"\u003e\u003c/a\u003eLenient parsing of expected value\nWriting JSON string in Java is huge pain. JsonUnit parses expected values leniently, so you do not have to quote keys,\nand you can use single quotes instead of double quotes. Please note that the actual value being compared is parsed in strict mode.\n\n```java\nassertThatJson(\"{\\\"a\\\":\\\"1\\\", \\\"b\\\":2}\").isEqualTo(\"{b:2, a:'1'}\");\n```\n\n## \u003ca name=\"object-mapper-cust\"\u003e\u003c/a\u003eJackson Object Mapper customization\nIf you need to customize Jackson 2 Object Mapper, you can do using [SPI](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html).\nImplement `net.javacrumbs.jsonunit.providers.Jackson2ObjectMapperProvider`.\n\n```java\npublic class Java8ObjectMapperProvider implements Jackson2ObjectMapperProvider {\n    private final ObjectMapper mapper;\n\n    private final ObjectMapper lenientMapper;\n\n\n    public Java8ObjectMapperProvider() {\n        mapper = new ObjectMapper().registerModule(new JavaTimeModule());\n        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);\n\n        lenientMapper = new ObjectMapper().registerModule(new JavaTimeModule());\n        lenientMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);\n        lenientMapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);\n        lenientMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);\n    }\n\n    @Override\n    public ObjectMapper getObjectMapper(boolean lenient) {\n        return lenient ? lenientMapper : mapper;\n    }\n}\n```\n\nand register it in `META-INF/services/net.javacrumbs.jsonunit.providers.Jackson2ObjectMapperProvider`.\nSee [this example](https://github.com/lukas-krecan/JsonUnit/commit/8dc7c884448c7373886dcf3b0eabfecf47c0710b).\n\n## Jackson 3 Object Mapper customization\nIf you need to customize Jackson 3 Object Mapper, you can do using [SPI](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html).\nImplement `net.javacrumbs.jsonunit.providers.Jackson3ObjectMapperProvider`.\n\n```java\npublic class Jackson3CustomProvider implements Jackson3ObjectMapperProvider {\n    private final ObjectMapper mapper;\n    private final ObjectMapper lenientMapper;\n\n    public Jackson3CustomProvider() {\n        mapper = new ObjectMapper().registerModule(new JavaTimeModule());\n        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);\n\n        lenientMapper = new ObjectMapper().registerModule(new JavaTimeModule());\n        lenientMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);\n        lenientMapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);\n        lenientMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);\n    }\n\n    @Override\n    public ObjectMapper getObjectMapper(boolean lenient) {\n        return lenient ? lenientMapper : mapper;\n    }\n}\n```\n\nand register it in `META-INF/services/net.javacrumbs.jsonunit.providers.Jackson3ObjectMapperProvider`.\n\nNote: Jackson 3 uses the `tools.jackson` package namespace instead of `com.fasterxml.jackson`.\n\n## Logging\n\nAlthough the differences are printed out by the assert statement, sometimes you use JsonUnit with other libraries like\n[Jadler](https://github.com/jadler-mocking/jadler) that do not print the differences between documents. In such case, you can switch on the\nlogging. JsonUnit uses [SLF4J](http://www.slf4j.org/). The only thing you need to do is to configure your logging\nframework to log `net.javacrumbs.jsonunit.difference` on DEBUG level.\n\n## DifferenceListener\n\nIf you need better difference reporting, you can implement [`DifferenceListener`](https://github.com/lukas-krecan/JsonUnit/blob/master/json-unit-core/src/main/java/net/javacrumbs/jsonunit/core/listener/DifferenceListener.java) and use it this way\n\n```java\nassertThatJson(\"{\\\"test\\\":-1}\")\n    .withDifferenceListener(listener)\n    .isEqualTo(json);\n```\n\n## Selecting underlying library\n\nJsonUnit is trying to cleverly match which JSON library to use. In case you need to change the default behavior, you can\nuse `json-unit.libraries` system property. For example `-Djson-unit.libraries=jackson2,jackson3,gson`\nor `System.setProperty(\"json-unit.libraries\", \"jackson2\");`. Supported values are gson, json.org, moshi, jackson2, jackson3\n\nLicence\n-------\nJsonUnit is licensed under [Apache 2.0 licence](https://www.apache.org/licenses/LICENSE-2.0).\n\nRelease notes\n=============\nReleased notes moved to a [separate file](RELEASES.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flukas-krecan%2FJsonUnit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flukas-krecan%2FJsonUnit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flukas-krecan%2FJsonUnit/lists"}