npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

fanci

v0.8.0

Published

Extract, rename or transform a JSON based on a template structure

Downloads

3,301

Readme

fanci

Build Status Dependency Status devDependency Status

NPM

Fanci is a lightweight node module to extract a subsets (using extract()), rename keys (using rename()) or tranform the structure (using transform()) of a JSON based on a template.

The initial goal was to consume a large JSON from an external API, and extract a smaller JSON with only the relevant fields. Unfortunately the available solutions did not really solve this problem (e.g. json-path, jsont, json2json, JSONStream, ...), at least not up to this level that we needed.

  • extract() does not change the original structure of the object, it extracts a subset of its keys
  • rename() does not change the original structure of the object, it can rename keys. All not renamed keys remain the same.
  • transform() changes the structure of the object, only the defined keys will be in the resulting object

All these methods take a source object as their first parameter and a template as their second. The template defines how the resulting JSON looks like.

Usage

Using fanci is very easy. All you need is your original JSON and a template which defines whats to do. You can find more examples in the example and test directory.

extract keys from JSON

var fanci = require('fanci');

var original = {
    "products": {
        "1234": {
            "id": 1234,
            "internal_id": "X04BEEF",
            "name": "The Beef",
            "status": {
                "available": true
            },
            "delivery": {
                "company": "My Transport",
                "rate": "business_hour",
                "time": "daily"
            }
        },
        "4567": {
            "id": 4567,
            "internal_id": "X08CAFE",
            "name": "El Coffee",
            "status": {
                "available": true
            },
            "delivery": {
                "company": "Ayayay",
                "rate": "weekend",
                "time": "weekend"
            }
        }
    }
};

var template = {
    'products': {
        '*': {
            'id': true,
            'name': true
        }
    }
}
var target = fanci.extract(original, template);

Result

target now contains the JSON with the fields from the template:

{
    "products": {
        "1234": {
            "id": 1234,
            "name": "The Beef"
        },
        "4567": {
            "id": 4567,
            "name": "El Coffee"
        }
    }
}

Template

The given JSON is compared to the template JSON. The structure can not be changed, i.e. each level in the original has its equivalent in the template. If the template does not specify deeper levels, the original JSON is transfered.

{
    'pic': {
        'id': true,
        'date': true,
        'author': { // from the 'author' object only 'name' is extracted
            'name': true
        },
        'urls': true // if 'urls' is an object, the whole object is extracted
    }
}

When dealing with arrays you can specify single array positions as object keys.

{
    'posts': { // here only the 3rd and 8th item from the posts array are extracted
        '2': true // the whole object is extracted
        '7': { // only the comments field containing the first comment is extracted
            'comments': {
                '0': true
            }
        }
    }
}

rename keys from JSON

var fanci = require('fanci');

var original = {
    "products": {
        "1234": {
            "name": "The Beef",
            "status": {
                "available": true
            },
            "delivery": {
                "company": "My Transport",
                "time": "daily"
            }
        },
        "4567": {
            "name": "El Coffee",
            "status": {
                "available": true
            },
            "delivery": {
                "company": "Ayayay",
                "time": "weekend"
            }
        }
    }
};

var template = {
    'stock': ['products', {
        '*': {
            'transport': 'delivery',
            'status': {
                'in_stock': 'available'
            }
        }
    }]
}
var target = fanci.rename(origial, template);

Result

target now contains the JSON with the renamed keys from the template:

{
    "stock": {
        "1234": {
            "name": "The Beef",
            "status": {
                "in_stock": true
            },
            "transport": {
                "company": "My Transport",
                "time": "daily"
            }
        },
        "4567": {
            "name": "El Coffee",
            "status": {
                "in_stock": true
            },
            "transport": {
                "company": "Ayayay",
                "time": "weekend"
            }
        }
    }
}

Template

In the template the new names are defined. For each new name, the old key has to be given as its value. To be able to change parent keys for objects and array, the template supports arrays to define the new names of the keys. That way arbitrary structures can processed.

{
    'books': {
        'id': 'identifier', //rename key 'identifier' to 'id'
        'writer': ['author', { //rename 'author' to 'writer' AND specify further rules for the next level
            'name': 'title' // rename the authors 'title' property to 'name'
        }]
    }
}

When dealing with arrays you can specify single array positions as object keys.

{
    'posts': { // here only the 3rd and 8th item from the posts array are renamed
        '2': {
            'name': 'fullname'
        },
        '7': {
            'name': 'firstname'
        }
    }
}

transform the structure of a JSON

var fanci = require('fanci');

var original = {
    "products": {
        "1234": {
            "id": 1234,
            "internal_id": "X04BEEF",
            "name": "The Beef",
            "status": {
                "available": true
            },
            "delivery": {
                "company": "My Transport",
                "rate": "business_hour",
                "time": "daily"
            }
        },
        "4567": {
            "id": 4567,
            "internal_id": "X08CAFE",
            "name": "El Coffee",
            "status": {
                "available": true
            },
            "delivery": {
                "company": "Ayayay",
                "rate": "weekend",
                "time": "weekend"
            }
        }
    }
};

var template = {
    'id': 'products.1234.internal_id',
    'company': 'products.4567.delivery.company',
    'name': [
        'products.6789.name',
        function(value) {
            return value.toUpperCase();
        }
    ],
    'available': 'products.*.status.available'
}
var target = fanci.transform(origial, template);

Result

target now contains the JSON with the fields from the template:

{
    "id": "X04BEEF",
    "company": "Ayayay",
    "name": "LIFE PRODUCT",
    "available": [
        true,
        true
    ]
}

Template

The template defines the new structure of the resulting object. The values are paths in the original JSON. Like that, it is possible to select nested elements and to put them in a new strucutre. By using the asteriks all elements of a level are considered. The resulting array in flattend or even removed completly if it only contains one item. It is possible to specify a format function to be applied to the object extracted from the path. This opens new possibilities to generate more complex structures. To do this, you have to specify an array instead of the path string, the first element is the path string, the second one is a function that takes the extracted object as an argument. If the second element is not a function, it is assumed that you wanted to construct an array in the resulting object.

{
    'pics': {
        'id': 'pics.id',
        'dates': [
            'pics.1.date',
            'pics.3.date',
            'pics.5.date',
        ],
        'authors': 'pics.*.author.name'
    },
    'date': [
        'Date',
        function(value) {
            return new Date(value);
        }
    ]
}

Special meaning of the * character

The asterisk (*) has a special meaning in the template:

  1. It means to use all keys from one level, this is useful when your JSON contains arbitrary keys

    {
        'products': {
            '*': { // all keys below products are taken, but only with the 'name' key
                'name': true
            }
        }
    }
  2. For arrays the asterisk represents all array elements

    {
        'docs': {
            '*': { // if docs is an array, '*' ensures that all elements are extracted
                'author': true
            }
        }
    }
  3. The same applies in the "path" that is used for transform()

    {
        'authors': 'docs.*.author'
    }
  4. In fact you can use * as a wildcard in the "path"

    {
        // returns properties like 'name', 'my_name' or 'name_of_product' of products with ids starting with 4 and ending with 7 (e.g. 4567)
        'ids': 'products.4*7.*name*' 
    }

Tests

To run the tests simply use the following command:

npm test

Release

To create a new release follow these steps:

  1. Update the version number in package.json
  2. Update the CHANGELOG.md
  3. Create a new release/tag on GitHub

The new release automatically triggers a GitHub Action to publish a new version on NPM.