{"id":22792298,"url":"https://github.com/jaskaranbir/json_template-compare","last_synced_at":"2025-03-30T17:16:25.881Z","repository":{"id":97983815,"uuid":"121925444","full_name":"Jaskaranbir/JSON_Template-Compare","owner":"Jaskaranbir","description":"A simple, nice, fancy, and sweet way to track/validate JSON values.","archived":false,"fork":false,"pushed_at":"2018-02-19T06:05:23.000Z","size":14,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-05T19:06:53.264Z","etag":null,"topics":["ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Jaskaranbir.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2018-02-18T06:28:33.000Z","updated_at":"2018-02-19T06:47:58.000Z","dependencies_parsed_at":"2023-06-01T10:01:14.879Z","dependency_job_id":null,"html_url":"https://github.com/Jaskaranbir/JSON_Template-Compare","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jaskaranbir%2FJSON_Template-Compare","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jaskaranbir%2FJSON_Template-Compare/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jaskaranbir%2FJSON_Template-Compare/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jaskaranbir%2FJSON_Template-Compare/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Jaskaranbir","download_url":"https://codeload.github.com/Jaskaranbir/JSON_Template-Compare/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246351016,"owners_count":20763232,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["ruby"],"created_at":"2024-12-12T03:10:26.669Z","updated_at":"2025-03-30T17:16:25.873Z","avatar_url":"https://github.com/Jaskaranbir.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# JSON Template-Based Comparison\n\nThis \"module\" (call it gem/module/whatever suits your programmer-soul). The base idea is to compare two JSON objects without conditional statements.\n\nOne additional advantage is that you no longer need to check for *existence* of parent key for checking/verifying the value of child key.\n\nOriginally, this was a part of **[ElasticSearch_Monitor_K8s_Cron][1]** repo but decided that this deserved its own repo. As such, the previously mentioned repo is actually a great example of how this can be used!\n\n---\n\n**Index:**\n\n- [How does it work][2]\n- [Why][3]\n- [Current Limitations / TODOs][4]\n- **[How to use][5]**\n  - [The error object][6]\n  - [Change how the error output is dealt with][7]\n  - [How is the final error-output object formed][8]\n  - [Basic Example][9]\n  - [Providing multiple possible values][10]\n  - [Wildcards][11]\n    - [Array Wildcards][12]\n      - [`___AND`][12]\n      - [`___OR`][13]\n      - [`___ARRAY_INDEX`][14]\n      - [Math-Operations Wildcards][15]\n        - [`___BT`][16]\n        - [`___EQ`][17]\n        - [`___GT`][18]\n        - [`___LT`][19]\n    - [Hash Wildcards][20]\n        - [`___*`][21]\n        - [`___OPTIONAL`][22]\n- [How to Contribute][23]\n- [License][24]\n\n---\n\n## How does it work\n\nYou provide the ***`rules`*** and the ***`scan-data`***. The scan-data is validated based on provided rules. Any keys/values found not in accordance with the rules are reported.\n\nBy default, it just prints the errored keys/values while specifying what sort of error occurred (check [alert_helper][25] for error types and how error-logs are `transported`).\n\nNothing will be printed if there were no errors found and all rules succeeded.\n\n---\n\n## Why\n\n* Couldn't find something similar \"on the internet\" (at time of this write-up).\n* Because I wanted to.\n\n* **Why Ruby?**\n\nThis project is, to some extent, a way of getting familiarized with Ruby basics. Besides, its fairly easy to port into any other language given the simplicity of structures used.\n\n* **Why use this?**\n\nHere's a sample JSON data:\n\n    {\n      \"a\": 4,\n      \"b\": {\n        \"c\": {\n          \"d\": [\n            1,\n            2,\n            3\n          ]\n        }\n      },\n      \"e\": 8,\n      \"f\": 'green'\n    }\n\nNow, imagine doing this:\n\n    if (data[a] \u0026\u0026 data[a] \u003e 4)\n      # --\n    end\n\n    if (data[b] \u0026\u0026 data[b][c] \u0026\u0026 data[b][c][d] \u0026\u0026 data[b][c][d].include?(2))\n      # --\n    end\n\n    # Or doing this:\n\n    if (data[b] \u0026\u0026 data[b][c] \u0026\u0026 data[b][c][d])\n      data[b][c][d].each do | e |\n        if (e \u003e 4)\n          # --\n        end\n      end\n    end\n\n    if (data[f] \u0026\u0026 data[f] == 'green' || data[f] == 'yellow')\n      # --\n    end\n\n...versus doing this:\n\n    {\n      a: [:'___GT', 4],\n      b: {\n        c: {\n          d: [\n            {\n              '___AND': 2\n            },\n            [\n              '___GT': 4\n            ]\n          ]\n        }\n      },\n\n      f: ['green', 'yellow']\n    }\n\nWhich one's easier to read, understand, and deal with? I leave the answer to you...\n\n***But, what about performance,*** you ask?\n\nPerformance depends on rules you provide. In other words, it won't be drastically different compared to if you would manually use `if-else` statements. The code works by simply scanning (or more specifically, recursively iterating) the rules. Yes, this will have slightly higher performance impact. But is that really a concern, when the computers these days are so fast, and easy to scale? Is the loss of developer productivity worth it? Again, I leave the answer to you...\n\n---\n\n## Current Limitations / TODOs\n\n*(Discovered so far).*\n\n* Can't create a rule such as `Any value of datatype x`.\n* Can't specify the number of elements in array, or number of keys in Hash\n\n---\n\n## How to use\n\n* Call the [`monitor`][26] method of [`MonitorEngine`][27] class. This method takes 2 parameters.\n\n  * `scan_data`: The data which has to be scanned for errors (values that don't match provided rules).\n\n  * `rules`: The rules using which data has to be compared. Any data not matching these rules will count as errors.\n\nAs explained above, the usage consists of `rules` (what values should JSON data contain) and `scan-data` (JSON data to scan and match with `rules`).\n\n* **Check below for [Usage Examples][9]**\n\n**Rules are to be written as Ruby Symbolic-Hash (Hash with symbols as keys, instead of strings).**\n\n### The error object\n\nIf some rule doesn't match for some reason, an error object is printed to stdout. This error object/*Hash* hash two fields:\n\n* **error_obj**: The actual error-object containing errors occured and actual values. This object contains JSON structure that corresponds to the original `scan-data`'s structure.\n\n* **error_paths**: Since `error_obj` is a JSON structure, getting all errorss would require a recursive iteration of error-object. Hence this object exists to save recursion (means you *don't* have to use recursion to navigate error object). This object is simply an array that contains strings corresponding to JSON path where the error occurred. These string(s) can be parsed to get exact error path in Hash, hence eliminating the need to use recursion.\n\n* **Every error-indentifier-property starts with 6 underscores (`______`)**.\n\n* **If error occurs at ROOT level of JSON, the error-path is represented by `___root`.**\n\n*(Check below for examples).*\n\n#### Handling error messages\n\nThe *transport* method for error messages is defined in [alert.rb][28]. Currently, it just prints to stdout.\n\n---\n\n### Change how the error output is dealt with\n\n...or where is error output displayed?\n\nSimply edit the [`transport`][28] method of [alert.rb][29]. This method provides two arguments:\n\n* **error_obj**: The object containing all errors. The structure of this object matches the JSON data being scanned with rules.\n\n* **error_paths**: Array containing key-locations in `error_obj` where the errors occured. (Also see **[above][6]**).\n\nJust add your logic to however you would like the data to be handled here.\n\n---\n\n### How is the final error-output object formed\n\nThe final error object has JSON structure similar to `scan-data` provided. The **[alert_helper.rb][25]** is responsible for forming this object.\n\nThe object is formed using the idea of ***states***. A *state* can be any particular level (in terms of nesting) in JSON object. An error information is also a state, as whenever an error-data is added to result-object, a new state is added.\n\nThe states are added and `reverted` mainly using keys (keys, as in Hash). Reverting a state refers to going back to previous state.\n\nStates are basically different levels in nesting of same object (as mentioned above). The final ouput is simply using the very first state, or the top-level (or root-level) nesting state of error-object to disaply all data.\n\n---\n\n#### Note: All outputs in examples are formatted for readability purposes. The actual output is *NOT* formatted.\n\n* ### Basic Example\n\n#### Example 1\n\n-\u003e Sample scan-data:\n\n    {\n      \"name\": \"minion\",\n      \"age\": 5\n    }\n\n-\u003e Sample rule (remember, `rule` should be a symbol-based Ruby Hash):\n\n    {\n      name: 'minion',\n      age: 10\n    }\n\nWill result in output:\n\n    {\n      \"error_obj\": {\n        \"age\": {\n          \"______expected\":10,\n          \"______got\":5\n        }\n      },\n      \"error_path\": [\"age\"]\n    }\n\n#### Example 2\n\n-\u003e Sample scan-data:\n\n    {\n      \"category\": \"dogs\",\n      \"count\": {\n        \"water\": 15,\n        \"fire\": 6\n      }\n    }\n\n-\u003e Sample rule:\n\n    {\n      category: 'pokeman',\n      count: {\n        electric: 20,\n        fire: 10\n      }\n    }\n\nWill result in output:\n\n    {\"error_obj\":{\"category\":{\"______expected\":\"pokeman\",\"______got\":\"dogs\"},\"count\":{\"electric\":{\"______key_not_found\":\"electric\",\"______at_location\":\"___root.count\"},\"fire\":{\"______expected\":10,\"______got\":6}}},\"error_paths\":[\"category\",\"count.electric\",\"count.fire\"]}\n\nHere's formatted version of output (its **not** formatted automatically by the module).\n\n    {\n      \"error_obj\": {\n        \"category\": {\n          \"______expected\": \"pokeman\",\n          \"______got\": \"dogs\"\n        },\n        \"count\": {\n          \"electric\": {\n            \"______key_not_found\": \"electric\",\n            \"______at_location\": \"___root.count\"\n          },\n          \"fire\": {\n            \"______expected\": 10,\n            \"______got\": 6\n          }\n        }\n      }\n\n      \"error_paths\": [\n        \"category\",\n        \"count.electric\",\n        \"count.fire\"\n      ]\n    }\n\n* ### Providing multiple possible values\n\n**-\u003e Provide multiple possible values. If one of the values match, the rule is counted as success.**\n\nJust specify the values in an array. One of the values must match. Examples Provided for simple data-types, but complex and nested objects work just as fine.\n\n-\u003e Sample scan-data:\n\n    {\n      \"a\": 4,\n      \"b\": \"SomeText\"\n    }\n\n-\u003e Sample rules:\n\n    {\n      a: [1, 3, 4, 2, 5],\n      b: ['val1', 'val2', 'val3']\n    }\n\n-\u003e Output:\n\n    {\n      \"error_obj\": {\n        \"b\": {\n          \"______no_match_found_from_values\": [\"val1\", \"val2\", \"val3\"],\n          \"______expected_value\": \"SomeText\"\n        }\n      },\n      \"error_path\": [\"b\"]\n    }\n\n* ### Wildcards\n\nTo make the tool more convenient and efficient to use, there are also some wildcards implemented. **Every wildcard starts with 3 underscores (`___`)**.\n\n### Array Wildcards\n\n* ### `___AND`\n\n**-\u003e All specified values *must* be present in an array**\n\nThis checks for presence of values in array. It means that all the specified values should be contained in an array or it counts towards failed rule.\n\n-\u003e Sample scan-data:\n\n    {\n      \"category\": \"dogs\",\n      \"count\": {\n        \"water\": 15,\n        \"fire\": 6\n      },\n      \"names\": [\n        \"Charlie\",\n        \"Buddy\",\n        \"Max\",\n        \"Archie\"\n      ]\n    }\n\n-\u003e Sample rules:\n\n    {\n      category: 'dogs',\n      count: {\n        water: 15,\n        fire: 6\n      },\n      names: [\n        {\n          '___AND': [\n            'Tom',\n            'Charlie',\n            'Berry',\n            'Shadow',\n            'Max'\n          ]\n        }\n      }\n    }\n\n-\u003e Output:\n\n    {\n      \"error_obj\": {\n        \"names\": {\n          \"______array_elements_not_found\": [\"Tom\", \"Berry\", \"shadow\"],\n          \"______got_array\": [\"Charlie\", \"Buddy\", \"Max\", \"Archie\"]\n        }\n      },\n      \"error_paths\":[\"names\"]\n    }\n\n* ### `___OR`\n\n**-\u003e At least one of the specified values *must* be present in an array**\n\nThis checks for presence of values in array. It means that one of the specified values should be contained in an array or it counts towards failed rule.\n\n-\u003e Sample scan-data:\n\n    {\n      \"category\": \"dogs\",\n      \"count\": {\n        \"water\": 15,\n        \"fire\": 6\n      },\n      \"names\": [\n        \"Charlie\",\n        \"Buddy\",\n        \"Max\",\n        \"Archie\"\n      ]\n    }\n\n-\u003e Sample rules:\n\n    {\n      category: 'dogs',\n      count: {\n        water: 15,\n        fire: 6\n      },\n      names: [\n        {\n         '___OR': [\n            'Tom',\n            'Shadow'\n          ]\n        }\n      ]\n    }\n\n-\u003e Output:\n\n    {\n      \"error_obj\": {\n        \"names\": {\n          \"______expected_atleast_one_value_from\": [\"Tom\", \"shadow\"],\n          \"______got_array\": [\"Charlie\", \"Buddy\", \"Max\", \"Archie\"]\n        }\n      },\n      \"error_paths\":[\"names\"]\n    }\n\n*  **You can also use both `___AND` and `___OR` at the same time.**\n\nFor example, consider these rules:\n\n    {\n      category: 'dogs',\n      count: {\n        water: 15,\n        fire: 6\n      },\n      names: [\n        {\n          '___OR': [\n            'Tom',\n            'Shadow'\n          ].\n          '___AND': [\n            'Jerry',\n            'Max',\n            'Buddy'\n          ]\n        }\n      ]\n    }\n\nThese rules imply that **any value out of *Tom, Shadow*, but all values from *Jerry, Max, Buddy*** should be present for rules to count as success.\n\n* If same rule is specified under both `___OR` and `___AND`, precedence goes to `___AND`.\n\n### -\u003e The default operation is `AND`. Example:\n\n-\u003e Sample scan-data:\n\n    [\n      1,\n      2,\n      3,\n      4,\n      5\n    ]\n\n-\u003e Sample rule:\n\n    [\n      1,\n      2,\n      10\n    ]\n\nThe above implies that all elements from **1, 2, 10** must be present in array.\n\n-\u003e Output:\n\n    {\n      \"error_obj\": {\n        \"______array_elements_not_found\": [10],\n        \"______got_array\": [1,2,3,4,5]\n      },\n      \"error_paths\": [\"___root\"]\n    }\n\n* ### `___ARRAY_INDEX`\n\n**-\u003e A given value must be present at the specified index in Array**\n\n**-\u003e Indices are 0 based, both in scan-data and rules.**\n\n* **The rule must be specified inside an array, otherwise data mismatch error will be thrown.**\n\nFor example, this rule is valid:\n\n    {\n      x: [\n        {\n          '___ARRAY_INDEX': 1,\n          '___VALUE': 3\n        }\n      ]\n    }\n\nBut this rule isn't:\n\n    {\n      x: {\n        '___ARRAY_INDEX': 1,\n        '___VALUE': 3\n      }\n    }\n\n* #### Example 1\n\n-\u003e Sample scan-data:\n\n    [\n      1,\n      2,\n      3,\n      4,\n      5\n    ]\n\n-\u003e Sample rules:\n\n    [\n      {\n        '___ARRAY_INDEX': 1,\n        '___VALUE': 3\n      },\n      {\n        '___ARRAY_INDEX': 2,\n        '___VALUE': 3\n      },\n      {\n        '___ARRAY_INDEX': 4,\n        '___VALUE': 10\n      }\n    ]\n\n-\u003e Output:\n\n    {\n      \"error_obj\": [\n        {\n          \"______expected\": 3,\n          \"______got\": 2,\n          \"______at_index\": 1\n        },\n        {\n          \"______expected\": 10,\n          \"______got\": 5,\n          \"______at_index\": 4\n        }\n      ],\n      \"error_paths\": [\n        \"___root\"\n      ]\n    }\n\n* #### Example 2\n\n-\u003e Sample scan-data:\n\n    {\n      \"test\": {\n        \"t\": {\n          \"y\": {\n            \"testValues\": [1, 2, 3, 4, 5, 6]\n          }\n        },\n        \"x\": [1, 4, 6, 7]\n      }\n    }'\n\n-\u003e Sample rules:\n\n    {\n      test: {\n        t: {\n          y: {\n            testValues: [\n              {\n                '___ARRAY_INDEX': 1,\n                '___VALUE': 2\n              },\n              {\n                '___ARRAY_INDEX': 3,\n                '___VALUE': 10\n              },\n              {\n                '___ARRAY_INDEX': 2,\n                '___VALUE': 5\n              },\n              {\n                '___ARRAY_INDEX': 1,\n                '___VALUE': 15\n              }\n            ]\n          }\n        },\n        x: [\n          {\n          '___ARRAY_INDEX': 1,\n          '___VALUE': 15\n          }\n        ]\n      }\n    }\n\n-\u003e Output:\n\n    {\n      \"error_obj\": {\n        \"test\": {\n          \"t\": {\n            \"y\": {\n              \"testValues\": [\n                {\n                  \"______expected\": 10,\n                  \"______got\": 4,\n                  \"______at_index\": 3\n                },\n                {\n                  \"______expected\": 5,\n                  \"______got\": 3,\n                  \"______at_index\": 2\n                },\n                {\n                  \"______expected\": 15,\n                  \"______got\": 2,\n                  \"______at_index\": 1\n                }\n              ]\n            }\n          },\n          \"x\": [\n            {\n              \"______expected\": 15,\n              \"______got\": 4,\n              \"______at_index\": 1\n            }\n          ]\n        }\n      },\n      \"error_paths\": [\n        \"test.t.y.testValues\",\n        \"test.x\"\n      ]\n    }\n\n* ### Math-Operations Wildcards\n\n* These operations allow you to specify rules such as *if the value should be greater than, less than, equals, or between two numbers*.\n\n* You specify an array, containing first element as operation to be performed, and consecutive elements as the numbers on which operation is to be performed.\n\n* **The default operation is *Equals* (`___EQ`).** For example, below rule means that the value of `x` must be *equal* to 5:\n\nSample rule:\n\n    {\n      x: 5\n    }\n\n* ### `___BT`\n\n**-\u003e The value should be between two numbers specified (*exclusively*).**\n\n**-\u003e Syntax: [Array]**\n\n* First Element: *Operation Type* (`___BT`)\n* Second Element: *Lower Number* (exclusive)\n* Third Element: *Upper Number* (exclusive)\n\n**Usage**:\n\n#### Example 1:\n\n-\u003e Sample scan-data:\n\n    [\n      1,\n      2,\n      3,\n      4,\n      5\n    ]\n\n-\u003e Sample rules:\n\n    [\n      {\n        '___ARRAY_INDEX': 1,\n        '___VALUE': [:'___GT', 30]\n      },\n      {\n        '___ARRAY_INDEX': 2,\n        '___VALUE': 3\n      },\n      {\n        '___ARRAY_INDEX': 4,\n        '___VALUE': 10\n      }\n    ]\n\n-\u003e Output:\n\n    {\n      \"error_obj\": [\n        {\n          \"______math_operation_failure\": [\"___GT\", 30],\n          \"______got_value\": 2,\n          \"______at_index\": 1\n        },\n        {\n          \"______expected\": 10,\n          \"______got\": 5,\n          \"______at_index\": 4\n        }\n      ],\n      \"error_paths\": [\"___root\"]\n    }\n\n#### Example 2:\n\n-\u003e Sample scan-data:\n\n    {\n      \"test\": {\n        \"t\": {\n          \"y\": 23\n        },\n        \"x\": 10,\n        \"z\": 80,\n        \"a\": {\n          \"b\": 5\n        }\n      }\n    }\n\n-\u003e Sample rules:\n\n    {\n      test: {\n        t: {\n          y: [:'___BT', 20, 25]\n        },\n        x: [:'___BT', 9, 11],\n        z: [:'___BT', 29, 31],\n        a: {\n          b: [:'___BT', 2, 4]\n        }\n      }\n    }\n\n-\u003e Output:\n\n    {\n      \"error_obj\": {\n        \"z\": {\n          \"______math_operation_failure\": [\"___BT\", 29, 31],\n          \"______got_value\":80\n        },\n        \"a\": {\n          \"b\": {\n            \"______math_operation_failure\":[\"___BT\", 2, 4],\n            \"______got_value\":5\n          }\n        }\n      },\n      \"error_paths\": [\"z\", \"a.b\"]\n    }\n\n* ### `___EQ`\n\n**-\u003e The value should be equal to the number specified.**\n\n**-\u003e Syntax: [Array]**\n\n* First Element: *Operation Type* (`___EQ`)\n* Second Element: *Value*\n\n#### Example:\n\n-\u003e Sample scan-data:\n\n    {\n      \"x\": 5\n    }\n\n-\u003e Sample rule:\n\n    {\n      x: [:'___EQ', 4]\n    }\n\n-\u003e Output:\n\n    {\n      \"error_obj\": {\n        \"x\": {\n          \"______math_operation_failure\": [\"___EQ\", 4],\n          \"______got_value\":5\n        }\n      },\n      \"error_paths\":[\"x\"]\n    }\n\n* ### `___GT`\n\n**-\u003e The value should be greater than the number specified.**\n\n**-\u003e Syntax: [Array]**\n\n* First Element: *Operation Type* (`___GT`)\n* Second Element: *Value*\n\n#### Example:\n\n-\u003e Sample scan-data:\n\n    {\n      \"x\": 5\n    }\n\n-\u003e Sample rule:\n\n    {\n      x: [:'___GT', 6]\n    }\n\n-\u003e Output:\n\n    {\n      \"error_obj\": {\n        \"x\": {\n          \"______math_operation_failure\": [\"___GT\", 6],\n          \"______got_value\":5\n        }\n      },\n      \"error_paths\":[\"x\"]\n    }\n\n* ### `___LT`\n\n**-\u003e The value should be less than the number specified.**\n\n**-\u003e Syntax: [Array]**\n\n* First Element: *Operation Type* (`___LT`)\n* Second Element: *Value*\n\n#### Example:\n\n-\u003e Sample scan-data:\n\n    {\n      \"x\": 5\n    }\n\n-\u003e Sample rule:\n\n    {\n      x: [:'___LT', 5]\n    }\n\n-\u003e Output:\n\n    {\n      \"error_obj\": {\n        \"x\": {\n          \"______math_operation_failure\": [\"___LT\", 5],\n          \"______got_value\":5\n        }\n      },\n      \"error_paths\":[\"x\"]\n    }\n\n### Hash Wildcards\n\n* ### `___*`\n\n**-\u003e This implies that the given rule should be applied to every key *within the same level* in JSON object.**\n\n#### Example:\n\n-\u003e Sample scan-data:\n\n    {\n      \"a\": {\n        \"x1\": 1,\n        \"x2\": 3,\n        \"x3\": \"exampleText\"\n      },\n      \"b\": {\n        \"y\": \"q\",\n        \"z\": 2\n      }\n    }\n\n-\u003e Sample rules:\n\n    {\n      a: {\n        '___*': 1\n      },\n      b: {\n        '___*': 'q'\n      }\n    }\n\n-\u003e Output:\n\n    {\n      \"error_obj\": {\n        \"a\": {\n          \"x2\": {\n            \"______expected\": 1,\n            \"______got\": 3\n          },\n          \"x3\": {\n            \"______expected\": 1,\n            \"______got\": \"exampleText\"\n          }\n        },\n        \"b\": {\n          \"z\": {\n            \"______expected\": \"q\",\n            \"______got\": 2\n          }\n        }\n      },\n      \"error_paths\": [\"a.x2\", \"a.x3\", \"b.z\"]\n    }\n\n* ### `___OPTIONAL`\n\n**-\u003e This implies that *if* the provided key is present, then it should have the provided value.** In simpler words, if some key is present, it should have the specified value. Otherwise it won't log errors regarding missing key.\n\n#### Example:\n\n-\u003e Sample scan-data:\n\n    {\n      \"a\": 4,\n      \"c\": 3\n    }\n\n-\u003e Sample rules:\n\n    {\n      '___OPTIONAL': {\n        a: 5,\n        b: 4,\n        c: 3\n      }\n    }\n\n-\u003e Output:\n\n    {\n      \"error_obj\": {\n        \"a\": {\n          \"______expected\": 5,\n          \"______got\": 4\n        }\n      },\n      \"error_paths\": [\"a\"]\n    }\n\n---\n\n## How to Contribute\n\n* Reporting bugs (provide *scan-data*, *rules* associated when you report).\n* See the [**Limitations/TODO**][4] list above.\n* Suggestions.\n\n---\n\n## License\n\nNone, really. Just use it however you like, but remember to give credits where required.\n\n\n  [1]: https://github.com/Jaskaranbir/ElasticSearch_Monitor_K8s_Cron\n  [2]: https://github.com/Jaskaranbir/JSON_Template-Compare#how-does-it-work\n  [3]: https://github.com/Jaskaranbir/JSON_Template-Compare#why\n  [4]: https://github.com/Jaskaranbir/JSON_Template-Compare#current-limitations--todos\n  [5]: https://github.com/Jaskaranbir/JSON_Template-Compare#how-to-use\n  [6]: https://github.com/Jaskaranbir/JSON_Template-Compare#the-error-object\n  [7]: https://github.com/Jaskaranbir/JSON_Template-Compare#change-how-the-error-output-is-dealt-with\n  [8]: https://github.com/Jaskaranbir/JSON_Template-Compare#how-is-the-final-error-output-object-formed\n  [9]: https://github.com/Jaskaranbir/JSON_Template-Compare#basic-example\n  [10]: https://github.com/Jaskaranbir/JSON_Template-Compare#providing-multiple-possible-values\n  [11]: https://github.com/Jaskaranbir/JSON_Template-Compare#wildcards\n  [12]: https://github.com/Jaskaranbir/JSON_Template-Compare#array-wildcards\n  [13]: https://github.com/Jaskaranbir/JSON_Template-Compare#___or\n  [14]: https://github.com/Jaskaranbir/JSON_Template-Compare#___array_index\n  [15]: https://github.com/Jaskaranbir/JSON_Template-Compare#math-operations-wildcards\n  [16]: https://github.com/Jaskaranbir/JSON_Template-Compare#___bt\n  [17]: https://github.com/Jaskaranbir/JSON_Template-Compare#___eq\n  [18]: https://github.com/Jaskaranbir/JSON_Template-Compare#___gt\n  [19]: https://github.com/Jaskaranbir/JSON_Template-Compare#___lt\n  [20]: https://github.com/Jaskaranbir/JSON_Template-Compare#hash-wildcards\n  [21]: https://github.com/Jaskaranbir/JSON_Template-Compare#___\n  [22]: https://github.com/Jaskaranbir/JSON_Template-Compare#___optional\n  [23]: https://github.com/Jaskaranbir/JSON_Template-Compare#how-to-contribute\n  [24]: https://github.com/Jaskaranbir/JSON_Template-Compare#license\n  [25]: https://github.com/Jaskaranbir/JSON_Template-Compare/blob/master/alert_helper.rb\n  [26]: https://github.com/Jaskaranbir/JSON_Template-Compare/blob/master/monitor_engine.rb#L18\n  [27]: https://github.com/Jaskaranbir/JSON_Template-Compare/blob/master/monitor_engine.rb\n  [28]: https://github.com/Jaskaranbir/JSON_Template-Compare/blob/master/alert.rb#L7\n  [29]: https://github.com/Jaskaranbir/JSON_Template-Compare/blob/master/alert.rb","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjaskaranbir%2Fjson_template-compare","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjaskaranbir%2Fjson_template-compare","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjaskaranbir%2Fjson_template-compare/lists"}