/json-traverse

Traverse and manipulate JSON objects.

Primary LanguageJavaScriptMIT LicenseMIT

License: MIT npm (scoped) node-current (scoped) Build Status Coverage Status

Traverse and manipulate JSON objects.

Usage

Basic usage

const jt = require('@tsmx/json-traverse');

const callbacks = {
    processValue: (key, value, level, path, isObjectRoot, isArrayElement, cbSetValue) => { 
            /* your logic here */ 
        },
    enterLevel: (level, path) => { 
            /* your logic here */ 
        },
    exitLevel: (level, path) => { 
            /* your logic here */ 
        }
};

var obj = { /* your JSON object */ };

jt.traverse(obj, callbacks);

Example 1: print out a simple object

var simpleObj = {
    MyValue: 'test',
    OtherValue: 'zzz',
    NumberValue: 311,
    MyArray: [1, 2, 3, 50, 60, 70]
};

const callbacks = {
    processValue: (key, value, level, path, isObjectRoot, isArrayElement, cbSetValue) => { 
        console.log(level + ' ' + (path.length > 0 ? (path.join('.') + '.') : '') + key + ' = ' + value); 
    }
};

const jt = require('@tsmx/json-traverse');

jt.traverse(simpleObj, callbacks);
// 0 MyValue = test
// 0 OtherValue = xxx
// 0 NumberValue = 311
// 0 MyArray._0 = 1
// 0 MyArray._1 = 2
// 0 MyArray._2 = 3
// 0 MyArray._3 = 50
// 0 MyArray._4 = 60
// 0 MyArray._5 = 70

// flat array-mode: flattenArray = true (arrays are treated as one value)
jt.traverse(simpleObj, callbacks, true);
// 0 MyValue = test
// 0 OtherValue = xxx
// 0 NumberValue = 311
// 0 MyArray = 1,2,3,50,60,70

Example 2: change values of an object

var simpleObj = {
    MyValue: 'test',
    OtherValue: 'zzz',
    NumberValue: 311,
    MyArray: [1, 2, 3, 50, 60, 70]
};

const callbacks = {
    processValue: (key, value, level, path, isObjectRoot, isArrayElement, cbSetValue) => { 
        // change values of properties starting with 'My' and
        // multiply all numeric array values greater then 50 by 100
        if (key.startsWith('My')) {
            cbSetValue('MyNew-' + value);
        }
        if (isArrayElement && parseInt(value) > 50) {
            cbSetValue(100 * parseInt(value));
        }
    }
};

const jt = require('@tsmx/json-traverse');

jt.traverse(simpleObj, callbacks);

// {
//   MyValue : "MyNew-test",
//   OtherValue: "xxx",
//   NumberValue: 311,
//   MyArray: [ 1, 2, 3, 50, 6000, 7000 ]
// }

Example 3: convert a more complex object to a collapsible HTML list

var htmlObj = {
    MyArray: [0, 0],
    ArrayInArray: [0, 1, ['two', 'three', [4, 5, 6]]],
    MyNumber: 123,
    MyString: 'test',
    Child: {
        ChildVal: 1,
        SubChild: {
            SubChildVal: 777
        },
        ChildArray: [1, 2, 66, 9, 900]
    },
    TrailingValue: 'testtesttest'
}

const callbacksHtmlList = {
    processValue: (key, value, level, path, isObjectRoot, isArrayElement, cbSetValue) => {
        if (isObjectRoot) {
            console.log(('  ').repeat(level) + ' <li class=\"caret\">Key: ' + key + '</li>')
        }
        else {
            console.log(('  ').repeat(level) + ' <li>Key: ' + key + ', Value: ' + value + '</li>')
        };
    },
    enterLevel: (level, path) => {
        if (level == 0) {
            console.log('<ul>');
        }
        else {
            console.log(('  ').repeat(level) + '<ul class=\"nested\">');
        };
    },
    exitLevel: (level, path) => { console.log(('  ').repeat(level) + '</ul>'); }
};

const jt = require('@tsmx/json-traverse');

jt.traverse(htmlObj, callbacksHtmlList, true);

// <ul>
//  <li>Key: MyArray, Value: 0,0</li>
//  <li>Key: ArrayInArray, Value: 0,1,two,three,4,5,6</li>
//  <li>Key: MyNumber, Value: 123</li>
//  <li>Key: MyString, Value: test</li>
//  <li class="caret">Key: Child</li>
//   <ul class="nested">
//    <li>Key: ChildVal, Value: 1</li>
//    <li class="caret">Key: SubChild</li>
//     <ul class="nested">
//      <li>Key: SubChildVal, Value: 777</li>
//     </ul>
//    <li>Key: ChildArray, Value: 1,2,66,9,900</li>
//   </ul>
//  <li>Key: TrailingValue, Value: testtesttest</li>
// </ul>

Key-features

  • Define your callbacks for the following events:
    • processValue: processing a traversed value
    • enterLevel: entering a new nesting level
    • exitLevel: leaving nesting level
  • For every inspected value you will get rich meta-data
    • key name
    • level of nesting
    • isObjectRoot flag to indicate if it's an object root (root of a nested object)
    • isArrayElement flag to indicate if it's an array item
    • full path to the key as an array of path elements
  • Provides cbSetValue function to change any value in-place (directly in the traversed object)
  • Supports deep inspection of
    • Subobjects
    • Arrays
    • Arrays-in-Arrays
    • Subobjects-in-Arrays
  • Optional array flattening (treat arrays as flat values)

API

traverse(obj, callbacks = null, flattenArray = false)

Traverse the obj and apply the defined callbacks while traversing.

obj

Type: Object

The object to be traversed.

callbacks

Type: Object Default: null

An Object containing the callback functions that should be applied while traversing obj. Every callback is optional. The expected form is:

callbacks = {
    processValue: (key, value, level, path, isObjectRoot, isArrayElement, cbSetValue) => { 
            /* your logic here */ 
        },
    enterLevel: (level, path) => { 
            /* your logic here */ 
        },
    exitLevel: (level, path) => { 
            /* your logic here */ 
        }
};
processValue(key, value, level, path, isObjectRoot, isArrayElement, cbSetValue)

Defined callback function that is executed on each value when traversing the object. Receives the following input parameters:

key

Type: String

The key of the current value that is processed. If an array is deep-inspected the key for each processed item is _ + Index (_0, _1, _2,...).

value

Type: String

The actual value for key.

level

Type: Number

The nesting level. 0 indicates the first level.

path

Type: Array

An array containing all keys that where passed to reach the current key/value pair. Example:

{
    child: {
        subchild: {
            myvalue: 123;
        }
    }
}

When processing the value 123 with key myvalue, path would be ['child', 'subchild' ].

For deep-inspected arrays the path would contain the name of the array itself whereas the key would be the index of the processed value. Example:

{
    child: {
        subchild: {
            myvalues: [1, 2, 3]
        }
    }
}

When processing the array the keys would be _0, _1 and _2 and the path would always be ['child', 'subchild', 'myvalues'].

isObjectRoot

Type: Boolean

true if the currently processed key is the root of another sub-object. In our example:

{
    child: {
        subchild: {
            myvalue: 123;
        }
    }
}

isObjectRoot would be true for the keys child and subchild.

isArrayElement

Type: Boolean

true if the currently processed key is an item of an array.

cbSetValue(newValue)

Type: Function

Callback function receiving the newValue that should replace the currently traversed value.

Note: Setting a new value directly changes the traversed object! So if you need the original later on be sure to create a copy of the object first.

enterLevel(level, path)

Defined callback function that is executed on entering a new nesting level when traversing the object. Receives the following input parameters:

level

Type: Number

0-based index of the nesting level that is entered.

path

Type: Array

An array containing all keys that where passed to reach the current level that is entered.

exitLevel(level, path)

Defined callback function that is executed on leaving a nesting level when traversing the object. Receives the following input parameters:

level

Type: Number

0-based index of the nesting level that is exited.

path

Type: Array

An array containing all keys that where passed to reach the current level that is exited.

flattenArray

Type: Boolean Default: false

If set to true arrays will not be iterated but treated as one single value. The default is false, where arrays are iterated and each entry is processed separately including deep-inspection, e.g. if the entry is an object or another array.