golaod
7/20/2017 - 9:27 AM

Compress json #json #javascript

Compress json #json #javascript

/**
  By Steve Hanov
  Released to the public domain


  */

/* CJSON.stringify() to convert from objects to string
   CJSON.parse() to convert from string to objects.
  More documentation is pending.
*/

(function(){
    /*jslint sub: true */

    // We keep track of object types that we have seen in a tree.
    function Node( parent, key )
    {
        this.parent = parent;
        this.key = key;
        this.children = [];
        this.templateIndex = null;
        this.links = [];
    }

    Node.prototype.follow = function( key )
    {
        if ( key in this.children ) {
            return this.children[key];
        } else {
            this.children[key] = new Node( this, key );
            return this.children[key];
        }
    };

    // Given the root of the key tree, process the value possibly adding to the
    // key tree.
    function process( root, value )
    {
        var result;
        var i;
        var key;
        var node;

        if ( typeof value === "object" ) {
            // if it's an array,
            if (Object.prototype.toString.apply(value) === '[object Array]') {
                // process each item in the array.
                result = [];
                for( i = 0; i < value.length; i++ ) {
                    result.push( process( root, value[i] ) ); 
                }
            } else {
                node = root;
                result = { "":[] };
                // it's an object. For each key,
                for (key in value) {
                    if ( Object.hasOwnProperty.call( value, key ) ) {
                        // follow the node.
                        node = node.follow( key );

                        // add its value to the array.
                        result[""].push( process( root, value[key] ) );
                    }
                }

                node.links.push( result );
            }
        } else {
            result = value;
        }

        return result;
    }

    // Given the root of the key tree, return the array of template arrays.
    function createTemplates( root )
    {
        var templates = [];
        var queue = [];
        var node;
        var template;
        var cur;
        var i;
        var key;
        var numChildren;

        root.templateIndex = 0;

        for ( key in root.children ) {
            if ( Object.hasOwnProperty.call( root.children, key ) ) {
                queue.push( root.children[key] );
            }
        }

        // while queue not empty
        while( queue.length > 0 ) {
            // remove a ode from the queue,
            node = queue.shift();
            numChildren = 0;

            // add its children to the queue.
            for ( key in node.children ) {
                if ( Object.hasOwnProperty.call( node.children, key ) ) {
                    queue.push( node.children[key] );
                    numChildren += 1;
                }
            }

            // if the node had more than one child, or it has links,
            if ( numChildren > 1 || node.links.length > 0 ) {
                template = [];
                cur = node;

                // follow the path up from the node until one with a template
                // id is reached.
                while( cur.templateIndex === null ) {
                    template.unshift( cur.key );
                    cur = cur.parent;
                }

                template.unshift( cur.templateIndex );

                templates.push( template );
                node.templateIndex = templates.length;

                for( i = 0; i < node.links.length; i++ ) {
                    node.links[i][""].unshift( node.templateIndex );
                }
            }
        }

        return templates;
    }

    function Compress( value )
    {
        var root, templates, values;

        root = new Node( null, "" );
        values = process( root, value );
        templates = createTemplates( root );

        if ( templates.length > 0 ) {
            return JSON.stringify( { "f": "cjson", "t": templates, 
                "v": values }, null, null );
        } else {
            // no templates, so no compression is possible.
            return JSON.stringify( value );
        }
    }

    function getKeys( templates, index )
    {
        var keys = [];

        console.log( templates );
        while( index > 0 ) {
            keys = templates[index-1].slice( 1 ).concat( keys );
            index = templates[index-1][0];
        }

        console.log( keys );
        return keys;
    }

    function expand( templates, value )
    {
        var result, i, key, keys;

        // if it's an array, then expand each element of the array.
        if ( typeof value === 'object' ) {
            if (Object.prototype.toString.apply(value) === '[object Array]') {
                result = [];
                for ( i = 0; i < value.length; i++ ) {
                    result.push( expand( templates, value[i] ) );
                }

            } else {
                // if it's an object, then recreate the keys from the template
                // and expand.
                result = {};
                keys = getKeys( templates, value[""][0] );
                for( i = 0; i < keys.length; i++ ) {
                    result[keys[i]] = expand( templates, value[""][i+1] );
                }
            }
        } else {
            result = value;
        }

        return result;
    }

    function Expand( str )
    {
        var value = JSON.parse( str );
        if ( typeof value !== "object" ||
             !("f" in value) ||
             value["f"] !== "cjson" )
        {
            // not in cjson format. Return as is.
            return value;
        }

        return expand( value["t"], value["v"] );
    }

    window.CJSON = {};
    window.CJSON.stringify = Compress;
    window.CJSON.parse = Expand;

})();