JSONSelect icon indicating copy to clipboard operation
JSONSelect copied to clipboard

An expression parser

Open satyasuman opened this issue 13 years ago • 0 comments

Hi, while using JSON Select i had a requirement of being able to access a JSON object with expression like [uuid="123"].account_info.[full_name="Test"].city_state_zip which would select any node that had UUID as 123 and a object in that node with full_name as Test and then the value of city_state_zip in that object, so the sake of simplicity i took a simple example. For the above requirement i have written a parser, which will convert an expression as above to a JSON Select expression. Below is the code for the same.

var Parser = function () { var SIMPLE_EXPRESSION = 0; var COMPLEX_EXPRESSION = 1; var SELECT_ALL_EXPRESSION = 2; var ATTRIBUTE_SEPERATOR = "="; var SQUARE_OPENER = "["; var SQUARE_CLOSER = "]"; var EXPRESSION_SEPERATOR = "."; var GLOBAL_SELECTOR = "*";

    function Expression(objName, attribute, exprType) {
        this.exprType = exprType;
        this.objName = objName;
        if(exprType != SIMPLE_EXPRESSION){
            var attributeArr = attribute.split(ATTRIBUTE_SEPERATOR);
            this.attributeName = attributeArr[0];
            this.attributeVal = attributeArr[1];
        }
        this.cssExpression = this.generateCSSExpression();
    }

    Expression.prototype = {
        generateCSSExpression: function(){
            if(this.cssExpression == undefined || this.cssExpression == null){
                var cssExpression = null;
                switch(this.exprType){
                    case COMPLEX_EXPRESSION:
                        cssExpression = "."+this.objName+" :has(."+this.attributeName+":expr(x="+this.attributeVal+"))";
                        break;
                    case SELECT_ALL_EXPRESSION:
                        cssExpression = ":has(:root > ."+ this.attributeName+ ":expr(x="+this.attributeVal+"))";
                        break;
                    case SIMPLE_EXPRESSION:
                        cssExpression =  "."+this.objName;
                        break;
                }
                this.cssExpression = cssExpression;
            }
            return this.cssExpression;
        }
    }

    function Parser() {

    }

    Parser.parse = function (expr) {
        return new Parser().parse(expr);
    };

    Parser.getExpressionStack = function (expr) {
        return new Parser().getExpressionStack(expr);
    };

    Parser.prototype = {
        getExpressionStack: function(expr){
            return this.parseExpression(expr, false);
        },
        parse: function (expr) {
            return this.parseExpression(expr, true);
        },
        parseExpression: function(expr, isString){
            this.expression = expr;
            this.expressionStack = [];
            this.pos = 0;
            var objectArray = this.expression.split(EXPRESSION_SEPERATOR);
            var self = this;
            /**
             * This function is used to parse a expression and generate a CSS expression out of it
             * Example expressions are
             * name_address[type='PRI'].email_address would generate a CSS Expression .name_address .type:val('PRI') ~ .email_address
             */
            $.each(objectArray, function(index, expr){
                var expression = null;
                var expressionType = self.getExpressionType(expr);
                switch(expressionType){
                    case COMPLEX_EXPRESSION:
                    case SELECT_ALL_EXPRESSION:
                        var exprArray = expr.replace(SQUARE_OPENER,"").split(SQUARE_CLOSER);
                        if(exprArray[1] === "" && exprArray.length == 2){
                            exprArray[1] = exprArray[0].replace("*","");
                        }
                        expression = new Expression(exprArray[0], exprArray[1], expressionType);
                        break;
                    case SIMPLE_EXPRESSION:
                        expression = new Expression(expr, null, expressionType);
                        break;
                }
                if(isString){
                    self.expressionStack.push(expression.cssExpression);
                } else {
                    self.expressionStack.push(expression);
                }

            });
            if(isString){
                return self.expressionStack.join(" ");
            }
            return this.expressionStack;
        },
        getExpressionType: function(expr){
            if(expr.indexOf(GLOBAL_SELECTOR) >= 0)
                return SELECT_ALL_EXPRESSION;
            if(expr.indexOf(SQUARE_OPENER) >= 0 && expr.indexOf(SQUARE_CLOSER) >= 0)
                return COMPLEX_EXPRESSION;
            return SIMPLE_EXPRESSION;
        }
    }
    return Parser;
}();

I am a newbie to Javascript, Please let me know if the above can be improved

satyasuman avatar Sep 19 '12 14:09 satyasuman