json-mask-validator 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +125 -0
- package/index.js +95 -0
- package/package.json +24 -0
package/README.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# json-mask-validator
|
|
2
|
+
|
|
3
|
+
A lightweight, high-performance Node.js utility for validating JSON payloads against a JSON Schema and masking/filtering fields using dynamic dot-notation paths. It seamlessly supports deep nesting, structural preservation, and **dynamic array mapping traversal**.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🛡️ **Schema Validation:** Built on top of `ajv` (the fastest JSON Schema validator for Node.js).
|
|
8
|
+
- 🔍 **Dynamic Filtering:** Mask objects to retain only explicitly requested field paths.
|
|
9
|
+
- 🌳 **Structure Preservation:** Keeps original parent hierarchies, nesting depths, and structures intact.
|
|
10
|
+
- 🔄 **Smart Array Traversal:** Automatically detects mid-path arrays and maps fields across every element inside the collection.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install json-mask-validator
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Make sure your project is configured for ES Modules (`"type": "module"` in your `package.json`), or use a bundler/transpiler like TypeScript/Babel.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
### 1. Standard Nested Objects
|
|
27
|
+
Filter deeply nested structures without losing their shape.
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
import { maskAndValidate } from 'json-mask-validator';
|
|
31
|
+
|
|
32
|
+
const sampleData = {
|
|
33
|
+
a: {
|
|
34
|
+
b: {
|
|
35
|
+
c: "ddc",
|
|
36
|
+
d: [1, 2, 3, 4]
|
|
37
|
+
},
|
|
38
|
+
x: "fc"
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const sampleSchema = {
|
|
43
|
+
type: "object",
|
|
44
|
+
properties: {
|
|
45
|
+
a: { type: "object" }
|
|
46
|
+
},
|
|
47
|
+
required: ["a"]
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Mask a deep sub-property
|
|
51
|
+
const res1 = maskAndValidate(sampleData, ['a.b.d'], sampleSchema);
|
|
52
|
+
console.log(res1);
|
|
53
|
+
// Output: { a: { b: { d: [1, 2, 3, 4] } } }
|
|
54
|
+
|
|
55
|
+
// Mask an intermediate parent object
|
|
56
|
+
const res2 = maskAndValidate(sampleData, ['a.x'], sampleSchema);
|
|
57
|
+
console.log(res2);
|
|
58
|
+
// Output: { a: { x: "fc" } }
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 2. Smart Array Traversal
|
|
62
|
+
If a path targets a property inside an array of objects, the package automatically iterates through the array items to extract and re-structure the target field.
|
|
63
|
+
|
|
64
|
+
```javascript
|
|
65
|
+
import { maskAndValidate } from 'json-mask-validator';
|
|
66
|
+
|
|
67
|
+
const arrayData = {
|
|
68
|
+
a: [
|
|
69
|
+
{ b: "scd", c: "df" },
|
|
70
|
+
{ b: "rt", c: "gh" }
|
|
71
|
+
]
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const schema = {
|
|
75
|
+
type: "object",
|
|
76
|
+
properties: {
|
|
77
|
+
a: {
|
|
78
|
+
type: "array",
|
|
79
|
+
items: { type: "object" }
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// Requesting 'a.b' automatically loops through the collection in 'a'
|
|
85
|
+
const result = maskAndValidate(arrayData, ['a.b'], schema);
|
|
86
|
+
console.log(JSON.stringify(result));
|
|
87
|
+
// Output: { "a": [{ "b": "scd" }, { "b": "rt" }] }
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 3. Multiple Paths Extraction
|
|
91
|
+
Combine multiple paths to pick and choose specific disjointed sections of the JSON tree.
|
|
92
|
+
|
|
93
|
+
```javascript
|
|
94
|
+
const combined = maskAndValidate(sampleData, ['a.b.c', 'a.x'], sampleSchema);
|
|
95
|
+
console.log(combined);
|
|
96
|
+
// Output: { a: { b: { c: "ddc" }, x: "fc" } }
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## API Reference
|
|
102
|
+
|
|
103
|
+
### `maskAndValidate(jsonData, paths, schema)`
|
|
104
|
+
|
|
105
|
+
Validates the input structural integrity, filters the fields, and reconstructs the mapped result object.
|
|
106
|
+
|
|
107
|
+
#### Parameters:
|
|
108
|
+
|
|
109
|
+
| Parameter | Type | Description |
|
|
110
|
+
| :--- | :--- | :--- |
|
|
111
|
+
| `jsonData` | `Object` | The input JSON object to parse and filter. |
|
|
112
|
+
| `paths` | `Array<string>` | An array of dot-notation string paths to preserve (e.g., `['a.b.c', 'user.profile.id']`). |
|
|
113
|
+
| `schema` | `Object` | A valid standard **JSON Schema** object compatible with Ajv. |
|
|
114
|
+
|
|
115
|
+
#### Returns:
|
|
116
|
+
- `Object`: A new object containing only the matched structure and values.
|
|
117
|
+
|
|
118
|
+
#### Throws:
|
|
119
|
+
- `Error`: Throws an explicit schema validation error if the incoming `jsonData` violates the rules defined in `schema`.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## License
|
|
124
|
+
|
|
125
|
+
MIT © Abhijith C U
|
package/index.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import Ajv from 'ajv';
|
|
2
|
+
|
|
3
|
+
const ajv = new Ajv();
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Recursively extracts data based on a path array, handling mid-path arrays gracefully.
|
|
7
|
+
*/
|
|
8
|
+
function getDeepValue(source, pathArray) {
|
|
9
|
+
if (source === null || source === undefined) return undefined;
|
|
10
|
+
if (pathArray.length === 0) return source;
|
|
11
|
+
|
|
12
|
+
const [currentKey, ...remainingPath] = pathArray;
|
|
13
|
+
|
|
14
|
+
// If the current source is an array, we need to extract the path from EVERY item inside it
|
|
15
|
+
if (Array.isArray(source)) {
|
|
16
|
+
return source
|
|
17
|
+
.map(item => getDeepValue(item, pathArray)) // pass the same path array down to items
|
|
18
|
+
.filter(val => val !== undefined);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// If it's an object, dig deeper
|
|
22
|
+
if (typeof source === 'object') {
|
|
23
|
+
const nextValue = source[currentKey];
|
|
24
|
+
|
|
25
|
+
// If the next value is an array, we handle it sequentially
|
|
26
|
+
if (Array.isArray(nextValue)) {
|
|
27
|
+
return nextValue.map(item => getDeepValue(item, remainingPath));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return getDeepValue(nextValue, remainingPath);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Recursively builds the target object/array structure based on the path and extracted value.
|
|
38
|
+
*/
|
|
39
|
+
function setDeepValue(target, pathArray, value) {
|
|
40
|
+
if (pathArray.length === 0 || value === undefined) return;
|
|
41
|
+
|
|
42
|
+
const [currentKey, ...remainingPath] = pathArray;
|
|
43
|
+
|
|
44
|
+
// Case 1: The value provided is an array resulting from an array-mapping traversal
|
|
45
|
+
if (Array.isArray(value) && remainingPath.length > 0) {
|
|
46
|
+
if (!target[currentKey]) {
|
|
47
|
+
target[currentKey] = [];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
value.forEach((subValue, index) => {
|
|
51
|
+
if (!target[currentKey][index]) {
|
|
52
|
+
target[currentKey][index] = {};
|
|
53
|
+
}
|
|
54
|
+
setDeepValue(target[currentKey][index], remainingPath, subValue);
|
|
55
|
+
});
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Case 2: Standard leaf-node assignment
|
|
60
|
+
if (remainingPath.length === 0) {
|
|
61
|
+
target[currentKey] = JSON.parse(JSON.stringify(value));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Case 3: Standard nested object traversal
|
|
66
|
+
if (!target[currentKey]) {
|
|
67
|
+
target[currentKey] = {};
|
|
68
|
+
}
|
|
69
|
+
setDeepValue(target[currentKey], remainingPath, value);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Main export function
|
|
74
|
+
*/
|
|
75
|
+
export function maskAndValidate(jsonData, paths, schema) {
|
|
76
|
+
const validate = ajv.compile(schema);
|
|
77
|
+
const valid = validate(jsonData);
|
|
78
|
+
|
|
79
|
+
if (!valid) {
|
|
80
|
+
throw new Error(`Schema Validation Failed: ${ajv.errorsText(validate.errors)}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const result = {};
|
|
84
|
+
|
|
85
|
+
for (const path of paths) {
|
|
86
|
+
const pathArray = path.split('.');
|
|
87
|
+
const val = getDeepValue(jsonData, pathArray);
|
|
88
|
+
|
|
89
|
+
if (val !== undefined) {
|
|
90
|
+
setDeepValue(result, pathArray, val);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return result;
|
|
95
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "json-mask-validator",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "A lightweight Node.js utility to validate JSON data against a schema and extract specific fields while preserving their nested structures and automatically traversing arrays.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"json",
|
|
9
|
+
"mask",
|
|
10
|
+
"filter",
|
|
11
|
+
"validator",
|
|
12
|
+
"ajv",
|
|
13
|
+
"dot-notation"
|
|
14
|
+
],
|
|
15
|
+
"files": [
|
|
16
|
+
"index.js",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"author": "Abhijith C U <abhijithcucu@gmail.com>",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"ajv": "8.12.0"
|
|
23
|
+
}
|
|
24
|
+
}
|