{"_id":"@camueller/json-converter","name":"@camueller/json-converter","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@camueller/json-converter","version":"1.0.0","description":"json-converter is a tool for translating/converting a JSON document into another JSON document with a different structure","main":"dist/json-converter.cjs.js","module":"dist/json-converter.esm.js","browser":"dist/json-converter.umd.js","scripts":{"build":"rollup -c","test":"jest","pretest":"npm run build"},"author":{"name":"Axel Müller"},"license":"ISC","repository":{"type":"git","url":"git+https://github.com/camueller/json-converter.git"},"keywords":["json","map","mapper","transform","transformation","json to json","converter"],"dependencies":{"jsonpath":"1.1.1"},"devDependencies":{"@babel/core":"^7.25.9","@babel/preset-env":"^7.25.9","@date-fns/tz":"1.2.0","@date-fns/utc":"2.1.0","babel-jest":"29.7.0","date-fns":"4.1.0","jest":"29.7.0","rollup":"^2.79.2","rollup-plugin-babel":"^4.4.0","rollup-plugin-commonjs":"^10.1.0","rollup-plugin-node-resolve":"^5.2.0","rollup-plugin-terser":"7.0.2"},"_id":"@camueller/json-converter@1.0.0","gitHead":"b39ae76e6e0746c911f43dfc96464355f2103bf8","bugs":{"url":"https://github.com/camueller/json-converter/issues"},"homepage":"https://github.com/camueller/json-converter#readme","_nodeVersion":"22.9.0","_npmVersion":"10.8.3","dist":{"integrity":"sha512-Q0gKfON4FOXcV2J6ItygQl/1bmiEIvsYG9z/0BNiV/GnRDxJs04XJ8OayevYIvzycVn2SBNqufUlZv1jNWgluA==","shasum":"4976b05f96915d243c52ef47600cb6ba08da0fae","tarball":"https://registry.npmjs.org/@camueller/json-converter/-/json-converter-1.0.0.tgz","fileCount":7,"unpackedSize":40502,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCrHtllAOP6rSME/vKpoFEFBrfFll3pvJfhOimO4CgrlgIhAKaVdnCQEkKrYon/W7v2jBBXUO2Y+VwvGysZ4qozQGF6"}]},"_npmUser":{"name":"camueller","email":"axel.mueller@avanux.de"},"directories":{},"maintainers":[{"name":"camueller","email":"axel.mueller@avanux.de"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/json-converter_1.0.0_1734511823762_0.7290602549164484"},"_hasShrinkwrap":false}},"time":{"created":"2024-12-18T08:50:23.644Z","1.0.0":"2024-12-18T08:50:23.932Z","modified":"2024-12-18T08:50:24.197Z"},"maintainers":[{"name":"camueller","email":"axel.mueller@avanux.de"}],"description":"json-converter is a tool for translating/converting a JSON document into another JSON document with a different structure","homepage":"https://github.com/camueller/json-converter#readme","keywords":["json","map","mapper","transform","transformation","json to json","converter"],"repository":{"type":"git","url":"git+https://github.com/camueller/json-converter.git"},"author":{"name":"Axel Müller"},"bugs":{"url":"https://github.com/camueller/json-converter/issues"},"license":"ISC","readme":"`JSON Converter` is a library for translating/converting a JSON document into another JSON document with a different structure. The mapping process follows a dictionary-based specification of how fields map to the new JSON format.\n\n`JSON Converter` is Javascript implementation of the [JSON Converter implemented in Python](https://github.com/ebi-ait/json-converter). It has the same features (and some more) and even this documentation is largely based on its relative. When I created the Javascript implementation I coded [unit tests for all the examples in this documentation](test/json_converter.test.js). Besides testing the implementation they also will be helpful understanding how `JSON Converter` works.\n\nThe main function is `json_converter` and takes a JSON input string, a structured specification and an optional `on` parameter:\n\n        json_converter(json_string, specification, on?)\n\n\n## Mapping Specification\n\nThe general idea is that the specification describes the resulting structure of the converted JSON document. The dictionary-based specification will closely resemble the schema of the resulting JSON.\n\n### Field Specification\n\nA field specification is defined by a list of parameters, the first of which is a name that refers to a field in the current JSON to be converted. This is the only required field.\n\n        <converted_field>: [<original_field>]\n\nFor example, given the sample JSON document,\n\n        {\n            \"person_name\": \"Juan dela Cruz\"\n            \"person_age\": 37 \n        }\n\nthe simplest mapping that can be done is to translate to a different field name. For example, to map `person_name` to `name` in the resulting JSON, the following specification is used:\n\n        {\n            'name': ['person_name']\n        }\n\n#### Field Chaining\n\nJSON mapping also supports chaining of fields on either or both side of the specification. For example, using the following specification to the JSON above,\n\n        {\n            'person.name': ['person_name'],\n            'person.age': ['person_age']\n        }\n\nwill result in the conversion:\n\n        {\n            \"person\": {\n                \"name\": \"Juan dela Cruz\",\n                \"age\": 37\n            }\n        }\n\nTo convert back to the original JSON in the example, just reverse the field specification, for example, `'person_name': ['person.name']`.\n\nDifferent from the [JSON Converter implemented in Python](https://github.com/ebi-ait/json-converter) the source field specification is interpreted as [JSON Path](https://github.com/dchester/jsonpath). This supports the specifications the Python-based `JSON Converter` uses, but provides much more flexibility beyond that. In order to test JSON Path Expressions the [JSONPath Online Evaluator](https://jsonpath.com/) is very helpful.\n\nField chaining can be done on multiple levels. However, direct field chaining for JSON array types is not supported. Processing such fields can be expressed through [anchoring](#anchoring) and [nesting](#nested-specification).\n\n#### Post-Processing using custom functions\n\n`JSON Converter` allows post-processing of field values for more complex translation rules. This is done by specifying a custom function. It is specified after the original field name in the field specification:\n\n        <converted_field>: [<original_field>, <post_processor function>]\n\n`JSON Converter` will pass the following parameters to this function (in this order):\n\n- the value of the specified field\n- the array index (if converting array elements)\n- all array elements (if converting array elements)\n\nThe second and third parameter are useful if the function has to access other array elements in order to convert the current array element.\n\nTaking the same example in the previous section, a boolean field `adult` can be added using this feature. The following spec demonstrates how this can be done:\n\n        {\n            'name': ['person_name'],\n            'age': ['person_age'],\n            'adult': ['person_age', (value) => value > 18]\n        }\n\n### Anchoring\n\nWhile `JSON Converter` has support for field chaining, for complex JSON with several levels of nesting, combined with long field names and field list, repetitively providing full field chain can be tedious. To be able to express this more concisely, anchoring can be used. Anchoring specifies the root of the JSON structure to map to a new JSON format, relative to the actual root of the original JSON document.\n\n#### The `on` Parameter\n\nThe `json_converter` function takes an optional parameter named `on` that can be used to specify the root of the JSON on which to start mapping. For example, given the following JSON,\n\n        {\n            \"user\": {\n                \"settings\": {\n                    \"basic\": {...},\n                    \"advanced\": {\n                        \"security\": {\n                            \"javascript_enabled\": true,\n                            \"allow_trackers\": false\n                        }\n                    }\n                }\n            }\n        }\n\nthe processing can be anchored on `user.settings.advanced.security` to translate the security settings. The following specification,\n\n        {\n            'javascript': ['javascript_enabled'],\n            'trackers': ['allow_trackers']\n        }\n\napplied to the JSON above using an `on` parameter value of `user.settings.advanced.security`, will result in,\n\n        {\n            \"javascript\": true,\n            \"trackers\": false\n        }\n\nWithout the anchor, it's necessary to always include `user.settings.advanced.security` in the field specification, so the `javascript` mapping would look like `'javascript': ['user.settings.advanced.security.javascript_enabled']`.\n\n#### The `$on` Specification\n\nAnother way of specifying the anchoring field is by directly adding it to the specification using the `$on` keyword. Unlike field specifications, the `$on` keyword takes a plain string and *not* an array. For example, the previous sample specification can be alternatively expressed as,\n\n        {\n            '$on': 'user.settings.advanced.security',\n            \"javascript\": ['javascript_enabled'],\n            \"trackers\": ['allow_trackers']\n        } \n\nUsing this specification, the mapping can be invoked without the `on` parameter. Keywords in specifications are case-sensitive, so `$On`, `$ON`, etc. are *not* recognised.\n\n#### Chaining `on` and `$on`\n\nThe `on` parameter and the `$on` keyword do **not** override, but instead are chained together. The existence of both during a mapping call results in the `$on` field chain being concatenated to the value provided in through the `on` parameter. For example, the following invocation is equivalent to the previous two above:\n\n        json_converter(json, {\n            '$on': 'advanced.security',\n            \"javascript\": true,\n            \"trackers\": false\n        }, 'user.settings')\n\nThe `user.settings` field supplied through `on` parameter will be treated as a prefix to the `advanced.security` field specified through the `$on` keyword.\n\n#### The `$any` Specification\n\nSometimes the field names are unknown or different for each conversion (e.g. timestamp-like field names). In this case the `$any` keyword with a conversion object should be used. The conversion object may contain the following properties:\n\n- `key` with value being a mapping function for the key to which the key value is passed as parameter \n- `value` with value being a mapping function for the value to which the value is passed as parameter\n\nAn example for a key mapping might look like this: \n\n        {\n            '$on': '$.result.watt_hours_period',\n            '$any': {key: (value) => convertDate(value)}\n        }\n\n### Nested Specification\n\nAside from field specifications, nested dictionary-like specification can be provided to any recognised fields in the root specification. Nesting is useful for expressing nesting on single objects, or for applying conversion to a list of JSON objects defined in an array.\n\n#### Single Object Nesting\n\nFor single objects, nested specs can be defined to look like the resulting JSON object. Nesting specification this way is a more expressive alternative to [field chaining that was demonstrated above](#field-chaining). For example, the following JSON, similar to the previous sections,\n\n        {\n            \"person_name\": \"Jane Eyre\",\n            \"person_age\": 30\n        }\n\ncan be mapped using nested specification defined with a nested `person` object:\n\n        {\n            'person': {\n                'name': ['person_name'],\n                'age': ['person_age']\n            }\n        }\n\n[Anchoring](#anchoring) in nested specifications is also supported. However, unlike anchors in the main specification that can be expressed through the `on` parameter, nested anchors can only be specified using the `$on` keyword. It is also important to note that nested anchors are defined relative to the parent specification. For example, the following JSON,\n\n        {\n            \"product_info\" {\n                \"manufacturing\": {\n                    \"location\": \"Cambridge, UK\", \n                    \"manufacturing_date\": \"2020-03-05\",\n                    \"best_by_date\": \"2020-09-05\"\n                }\n            }\n        }\n\ncan be mapped using the following nested specification,\n\n        {\n            '$on': 'product_info',\n            'production': {\n                '$on': 'manufacturing',\n                'date': ['manufacturing_date']\n            }\n        }\n\nThis mapping will result in the following JSON:\n\n        {\n            \"production\": {\n                \"date\": \"2020-03-05\"\n            }\n        }\n\n### Applying Specification to JSON Arrays\n\n`JSON Converter` can distinguish between JSON object nodes and JSON array, and applies specification accordingly. When it determines that a field referred to by the specification is a collection of JSON objects, it applies the rules to each one of them iteratively. Note, however, that, as earlier mentioned in this document, using field chaining to refer to nested JSON array is *not* supported. To apply specifications to JSON arrays, they need to be explicitly [anchored](#anchoring) if they are nested within the original JSON document.\n\nTo illustrate, the following JSON object,\n\n        {\n            \"books\": [\n                {\n                    \"title\": \"A Python Book\",\n                    \"price\": 23.75\n                },\n                {\n                    \"title\": \"A Novel\",\n                    \"price\": 7.99\n                },\n                {\n                    \"title\": \"Compilation of Fun Stuff\",\n                    \"price\": 10.10\n                }\n            ]\n        }\n\ncan be translated using the following specification (assume `translate` and `convert` are defined; see [post-processing](#post-processing-using-custom-functions) for more information on this),\n\n        {\n            '$on': 'books',\n            'titulo': ['title', translate, 'es'],\n            'precio': ['price', convert, 'eur']\n        }\n\nNotice that, since the specification is anchored to the `books` node, only the list of field specifications are defined. Specifications applied to multiple objects this way are expressed as if it applies to a single object. This sample translation will return an array of JSON objects and *not* a JSON object containing an array. If a nested array is desired, the specification above can be nested instead:\n\n        {\n            'libros': {\n                '$on': 'books',\n                'titulo': ['title', translate, 'es'],\n                'precio': ['price', convert, 'eur']\n            }\n        }\n\n#### Filtering\n\nWhen working on collections of data, it is sometimes required to only process some based on some criteria. This is done by specifying a custom filter function in the specification.\n\n        '$filter': [<original_field>, <function>]\n\nIt will pass the value of the `original_field` as the parameter.\n\nFor example, to process only books whose prices are above 10.00 from the sample books JSON above, the following spec can be used:\n\n        {\n            'expensive_books': {\n                '$on': 'books',\n                '$filter': ['price', (value) => value > 10],\n                'book_title': ['title'],\n                'book_price': ['price']\n            }\n        }\n\n#### Convert array elements to object properties\n\nArray elements may be converted into object properties if they have a unique property which can be used as key for the object property.\n\n        '$arrayToObject': [<key_field>, <value_field>]\n\nTo illustrate, the following JSON object\n\n        {\n            \"months\": [\n                {\n                    \"name\": \"Januar\",\n                    \"index\": 1\n                },\n                {\n                    \"name\": \"Februar\",\n                    \"index\": 2\n                },\n                {\n                    \"name\": \"March\",\n                    \"index\": 3\n                }\n            ]\n        }\n\nusing the following specification\n\n        {\n            '$on': 'months',\n            'name': ['name'],\n            'index': ['index'],\n            '$arrayToObject': ['name', 'index']\n        }\n\nwill result in\n\n        {\n            Januar: 1,\n            Februar: 2,\n            March: 3\n        }\n\n### JSON Literals\n\nThere are situations when the resulting JSON need to contain fields and values outside the scope of the source JSON document. In such cases, it's possible to define a post-processor that plugs-in a pre-defined dictionary-like or list structure. However, support for including literals in the specification is also provided.\n\n#### Using Keywords\n\nAs mentioned above, there are 2 types of node that can be used for adding predefined values into the specification, which are object, and array. To specify a JSON object literal as field value in the resulting JSON document, the `$object` keyword is used with a dictionary-like structure:\n\n        <field_name>: ['$object', <object_value>]\n\nFor example:\n\n        'metadata': ['$object', {\n            'date_created': '2020-03-13',\n            'author': 'Jane Doe'\n        }]\n\nFor collections or list of JSON objects, the `'$array'` is used instead:\n\n        <field_name>: ['$array': <list>]\n\nFor example:\n\n        'authors': ['$array', [\n            {\n                'name': 'Peter Z',\n                'institution': 'Some University'\n            },\n            {\n                'name': 'Mary Q',\n                'institution': 'Some Research Institute'\n            }\n        ]]\n","readmeFilename":"README.md"}