{"id":16880592,"url":"https://github.com/squaremo/rejson","last_synced_at":"2025-04-11T11:44:18.670Z","repository":{"id":1452974,"uuid":"1686361","full_name":"squaremo/rejson","owner":"squaremo","description":"JSON pattern matching","archived":false,"fork":false,"pushed_at":"2011-12-28T16:56:02.000Z","size":152,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-25T08:04:25.398Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/squaremo.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}},"created_at":"2011-04-30T23:43:08.000Z","updated_at":"2024-03-21T05:46:30.000Z","dependencies_parsed_at":"2022-08-16T13:20:43.290Z","dependency_job_id":null,"html_url":"https://github.com/squaremo/rejson","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/squaremo%2Frejson","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/squaremo%2Frejson/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/squaremo%2Frejson/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/squaremo%2Frejson/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/squaremo","download_url":"https://codeload.github.com/squaremo/rejson/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248387058,"owners_count":21095148,"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":[],"created_at":"2024-10-13T15:59:22.869Z","updated_at":"2025-04-11T11:44:18.623Z","avatar_url":"https://github.com/squaremo.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# JSON pattern matching\n\nReJSON is a pattern matching language for JSON, similar in expressive\npower to regular expressions for strings, or RelaxNG for XML.\n\n## What does it do?\n\nHere's an example in (the more complete implementation in) Erlang:\n\n    \u003e Json = json:decode(\"[1, 2, \\\"foo\\\", 3]\"),\n    \u003e Pattern = rejson:parse(\"[1, 2, number *] ^ [S = string]\"),\n    \u003e rejson:match(Pattern, Json).\n    {ok, [{\"S\", \"foo\"}]}\n\nHere's an example in JavaScript (Node.JS or CommonJS). NB this doesn't\nimplement captures yet, so you can use test to just get `true` or\n`false`.\n\n    \u003e var rejson = require('rejson');\n    \u003e var pattern = rejson.parse('{\"foo\": [number *]}');\n    \u003e pattern.test({foo: [1, 2]});\n    true\n\nYou can also lift a JSON-like value into a pattern:\n\n    \u003e var rejson = require('rejson');\n    \u003e var pattern = rejson({foo: [rejson.number, rejson.any]});\n    \u003e pattern.test({foo: [1, \"bar\"]});\n    true\n\n## Pattern syntax\n\n    \u003cpattern\u003e / \u003cvalue\u003e =\u003e \u003cresult\u003e\n\n### Values\n\nAll JSON literal values are valid patterns, and match by equivalence.\n\nFor example,\n\n    1 / 1 =\u003e match\n\nObjects are treated as unordered, so\n\n    {\"foo\": 1, \"bar\": 2} / {\"bar\": 2, \"foo\": 1} =\u003e match\n\n### Ground types\n\n`number`, `string`, and `boolean` each match any value of the\nrespective type:\n\n    number / 1.5 =\u003e match\n\nThese can be used nested in an array or object:\n\n    {\"foo\": number} / {\"foo\": 10} =\u003e match\n\n### Either\n\nA bar signifies alternatives:\n\n    number | string / \"foo\" =\u003e match\n\n### Any\n\nAny, '_', matches any value:\n\n    _ / \"foobar\" =\u003e match\n\nIt can be used in arrays and objects:\n\n    [1, 2, 3, _] / [1,2,3,\"bar\"] =\u003e match\n\n    {\"foo\": _} / {\"foo\": 23} =\u003e match\n\nUsed in the place of a whole property it can signify arbitrary\nadditional properties:\n\n    {\"foo\": 1, _} / {\"foo\": 1, \"bar\": 2} =\u003e match\n\nIt can only go in the last position for this purpose (object\nproperties are unordered anyway).\n\n### Star, Plus, Maybe\n\nThe Kleene operator '*' (zero or more) and its relative '+' (one or more) can\nmatch in arrays:\n\n    [1, number *] / [1,2,3] =\u003e match\n\nMaybe, '?', matches zero or one, and can be used in arrays:\n\n    [number, string ?] / [1] =\u003e match\n    [number, string ?] / [1, \"bar\"] =\u003e match\n\nIn objects, maybe indicates the property may be absent; if it is\npresent, the pattern must match:\n\n    {\"foo\": string ?, _} / {\"bar\": 1} =\u003e match\n    {\"foo\": string ?, _} / {\"foo\": 2} =\u003e no_match\n\n### Interleave\n\nInterleave, '^', can be used between array patterns to matches array\nvalues in which the elements matching the left-hand pattern are\narbitrarily interleaved with the elements matching the right-hand\npattern, while staying in order. For example,\n\n    [1, 2, 3] ^ [\"foo\", \"bar\"] / [1, \"foo\", 2, 3, \"bar\"] =\u003e match\n\n### Variable capture\n\nA capture can appear in almost any position, and introduces a pattern\nthat will prodice a binding if it matches.  For example,\n\n    Foo = number / 3 =\u003e match, {Foo: 3}\n\nA capture cannot appear is as a property name, or (currently) as the\noperand of an interleave, though it can appear *in* an operand.\n\n## Related work\n\nHere are something things that are like ReJSON:\n\n - [**Erlang term\n    pattern-matching**](http://www.erlang.org/doc/reference_manual/expressions.html#pattern). This\n    is pretty close, but it doesn't deal with matching objects (maps),\n    or sequences (repetition and interleave).\n\n - [**CDuce pattern matching and\n types**](http://www.cduce.org/manual_types_patterns.html). This is\n probably closest in spirit, even though it is based on XML\n schema. CDuce goes further and has a type system built on these\n patterns.\n\n - [**JSON schema**](http://json-schema.org/). This isn't\n    pattern-matching, but it is close in that it's verifying the shape\n    of a JSON term. However, JSON schema is unsurprisingly more akin\n    to XML Schema, in that it encodes a language entirely unlike JSON,\n    but describing JSON, in JSON.\n\n    I think it's better to make the pattern language parallel to the\n    value language, even if it can't reuse the parser. I'm also trying\n    to make a pattern-matcher, rather than a schema-checker. In other\n    words, the idea is to get variable bindings out the other end, not\n    only see if a term matches some shape.\n\n - *Other attempts*, notably\n    [jsonr](http://laurentszyster.be/jsonr/). This is more\n    text-oriented, with no binding capture, but is close in spirit to\n    rejson. It's limited by insisting on being encoded in JSON.\n\n## Further work\n\n### Matching with a streaming parser\n\nIt's fairly obvious that parsing the entire JSON value ahead of time\nis a waste, if the match fails early on. Using a stream parser (or\njust a lazy tokeniser, JSON is simple enough) would mean only doing\nthe parsing that's needed.\n\nBacktracks would need to save the state of the parser; but of course,\nin a functional language this isn't a big deal.\n\n### Determining which of a set of patterns matches a value\n\nOften the problem at hand is not \"which values match this pattern?\",\nbut \"which patterns does this value match?\". One way to do this is to\ncompile all patterns into a state machine where the terminal states\nyield a list of patterns matched -- something like\nhttp://en.wikipedia.org/wiki/Aho-Corasick_algorithm.\n\nI can foresee two main difficulties: variable captures and\ninterleave. Interleave because it results in state explosion. Variable\ncapture because it's just awkward.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsquaremo%2Frejson","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsquaremo%2Frejson","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsquaremo%2Frejson/lists"}