JS将OData $ filter字符串解析为Object Structure

时间:2014-11-11 23:23:49

标签: javascript regex recursion odata

我正在尝试将OData过滤器字符串转换为表示过滤器的对象。下面是我想要转换的复杂(非现实)OData过滤器的示例。

substringof('v', p1) or (p2 gt 5 and p3 eq 'v v' and (p4 eq false or startswith(p5.expand, 'v'))) or (p6 eq 6 and p7 eq 7)

使用下面的正则表达式和函数,我把它分成两部分:一个过滤器部分数组和一个字符串,每个过滤器部分被数组中的索引替换。

var conditionMatcher = new RegExp("(substringof\\(.+?\\)|startswith\\(.+?\\)|endswith\\(.+?\\)|[\\w\\.]+?\\s(?:eq|ne|gt|ge|lt|le)\\s(?:\\w+|\\'.+?\\'))", "g");
var filters = predicateString.match(conditionMatcher);
var i = 0;
var simpleString = predicateString.replace(conditionMatcher, function () {
    return i++;
});

// simpleString =
0 or (1 and 2 and (3 or 4)) or (5 and 6)

目标是将字符串转换为以下内容:

{
    op: "or",
    filters: [
        0,
        {
            op: "and",
            filters: [
                1,
                2,
                {
                    op: "or",
                    filters: [
                        3,
                        4
                    ]
                }
            ]
        },
        {
            op: "and",
            filters: [
                5,
                6
            ]
        }
    ]
}

我只是不确定如何到达这里。如果有人有想法,我会非常感激,同时我会继续解决这个问题。

1 个答案:

答案 0 :(得分:1)

到目前为止,我的解决方案是解析字符串,查找第一个右括号及其匹配的左括号,从组中构建过滤器,用过滤器的索引替换组并向外工作。

// This array contains the filter parts extracted from the code in my question  
var filters = [...]; 
var filterString = "0 or (1 and 2 and (3 or 4)) or (5 and 6)";

var groupString;
var groupFilter = null;
var testNextLevel = true;

while (testNextLevel) {
    var closeParenthesisIndex = filterString.indexOf(')');
    if (closeParenthesisIndex !== -1) {
        var openParenthesisIndex = filterString.lastIndexOf('(', closeParenthesisIndex);

        // Extract the string between the first deepest set of parenthesis
        groupString = filterString.substring(openParenthesisIndex + 1, closeParenthesisIndex);

        // Modify the filter string replacing the contents of the group string in addition to the parenthesis with the length of the filters array (which will be the index of the filter object that we push)
        filterString = filterString.substring(0, openParenthesisIndex) + filters.length + filterString.substring(closeParenthesisIndex + 1);
    } else {

        // There are no more parenthesis groups
        groupString = filterString;
        testNextLevel = false;
    }

    // If the group uses both 'and' and 'or' then return null as an invalid filter string.
    if (groupString.indexOf('and') >= 0 && groupString.indexOf('or') >= 0) {
        return null;
    }

    // Get the group indexes out of the group string
    var groupFilterIndexes = groupString.match(/[0-9]+/g);
    var groupFilters = [];

    // Create an array with each of the filters who's index matches the group indexes
    for (i = 0; i < groupFilterIndexes.length; i++) {
        groupFilters.push(filters[Number(groupFilterIndexes[i])]);
    }
    var op = groupString.indexOf('or') >= 0 ? 'or' : 'and';

    // Create the filter object and push it onto the filters array
    groupFilter = { op: op, filters: groupFilters };
    filters.push(groupFilter);
}

return groupFilter;

在检查每个级别时,将构建过滤器对象,并将匹配的索引推送到过滤器对象中。然后将过滤器对象推入filters数组中,filterString的当前部分将替换为过滤器对象的索引。序列如下所示:

// First Level
starting filterString: "0 or (1 and 2 and (3 or 4)) or (5 and 6)"
starting filters.length: 7
groupString: "3 or 4"
filters[7] = { op: "or", filters: [filters[3], filters[4]] }
filterString after extraction: "0 or (1 and 2 and 7) or (5 and 6)"
filters.length: 8

// Second Level
starting filterString: "0 or (1 and 2 and 7) or (5 and 6)"
starting filters.length: 8
groupString: "1 and 2 and 7"
filters[8] = { op: "and", filters: [filters[1], filters[2], filters[7]] }
filterString after extraction: "0 or 8 or (5 and 6)"
filters.length: 9

// Third Level
starting filterString: "0 or 8 or (5 and 6)"
starting filters.length: 9
groupString: "5 and 6"
filters[9] = { op: "and", filters: [filters[5], filters[6]] }
filterString after extraction: "0 or 8 or 9"
filters.length: 10

// Final Level
starting filterString: "0 or 8 or 9"
starting filters.length: 10
groupString: "0 or 8 or 9"
filters[10] = { op: "or", filters: [filters[0], filters[8], filters[9]] }
filters.length: 11

// Return filters[10]