-4

我正在创建一个基于 JSON 和 JavaScript 的 DSL,并且我需要让键值指定为“原始”并且不包含在字符串分隔符中。一个简单的例子,希望能解释这一点:

{myKey:custom_function('arg1'), myKey2:custom_function("another arg1")}

应该成为

{myKey:"custom_function('arg1')", myKey2:"custom_function(\"another arg1\")"}

这是因为在解析 JSON 对象时,custom_function 将不存在。我需要能够在不评估任何值的情况下解析 JSON,然后在迭代键时只一个一个地扩展值。

我可以使用什么正则表达式或其他方法将第一个片段转换为第二个片段?

我假设一个更简单的解决方案将涵盖 90% 的情况,但编写一个防弹实现将需要付出很多努力。根据我对 JavaScript 的正则表达式支持(显然没有后视能力)所做的研究,我假设它需要的不仅仅是 1 或 2 行正则表达式模式。

此外,这是针对节点应用程序的,因此它为此提供的任何技巧也会有所帮助。

编辑:

这个问题似乎得到了一些反对意见,但为了未来的谷歌员工/我自己的参考,我还是把它留了下来。关于哪种方法/技术最适合此类问题,这是一个完全有效的问题,并且很容易有其他 node/js 新手面临类似问题。

4

1 回答 1

0

最终答案:正则表达式不适合像这样复杂的任务。我在网上找到的任何类似复杂的解决方案(例如删除代码注释)都采用了主要是自定义的迭代方法,仅少量使用正则表达式,因此在这种情况下,类似的方法最终不会太轻松。

所以最后我能找到的“最佳”方法并没有涉及太多的正则表达式或节点或其他适合该问题的任何专门库。

最后,为了将来可能遇到类似问题的谷歌用户的利益,我在https://gist.github.com/2590689上发布了我的解决方案并复制如下:

//clothe a hub file that has 'naked' expressions
//e.g. turn {key:$('body p')} into {key:"$('body p')"}
function clothe(contents){

closers = /[\}\]\)\/"']/
openers = /[\{\[\(\/"']/
closing = {
    "{": "}",
    "[": "]",
    "(": ")",
    "/": "/",
    '"': '"',
    "'": "'"
}

contents = contents.split("");

var beforeKey = true;
var inKey = false;
var beforeValue = false;
var inValue = false;
var inArray = false;
var delimiterStack = [];

function inDelimited(){
    return delimiterStack.length > 0;
}

function toggleDelimiter(d){
    if(openers.exec(d) && !closers.exec(d)){
        pushDelimiter(d);
    }else if(openers.exec(d) && closers.exec(d)){
        if(topDelimiter()){
            if(topDelimiter()==d){
                popDelimiterIfValid(d);
            }else{
                pushDelimiter(d);
            }
        }else{
            pushDelimiter(d);
        }
    }else if(closers.exec(d)){
        popDelimiterIfValid(d);
    }
}

function topDelimiter(){
    if(delimiterStack.length>=0){
        return delimiterStack[delimiterStack.length-1];
    }else{
        return undefined;
    }
}

function pushDelimiter(d){
    delimiterStack.push(d);
}

function popDelimiterIfValid(d){
    if(delimiterStack.length>0)
        if(closing[delimiterStack[delimiterStack.length-1]]==d)
            delimiterStack.pop(d);
}

function rTrimmedRightBound(rightBound){
    while(rightBound>0){
        if(!/\s/g.exec(contents[--rightBound])){
            return rightBound+1;
        }
    }
}

for(var i=0; i<contents.length; i++){

    function delimiterCheck(c){
        if(c=='"'){
            toggleDelimiter('"');
            contents.splice(i, 0, '\\');
            i++;
        }else if(openers.exec(c) || closers.exec(c)){
            toggleDelimiter(c)
        }
    }

    if(beforeKey){
        if(/[a-zA-Z0-9$_!]/.exec(contents[i])){
            beforeKey = false;
            inKey = true;
        }
    }else if(inKey){
        if(contents[i]==":"){
            inKey = false;
            beforeValue = true;
        }
    }else if(beforeValue){
        if(/[a-zA-Z0-9$_!'"\(\/]/.exec(contents[i])){
            contents.splice(i, 0, '"');
            i++;
            beforeValue = false;
            inValue = true;
            delimiterCheck(contents[i]);
        }else if(/\{/.exec(contents[i])){
            beforeKey = true;
            beforeValue = false;
        }else if(/\[/.exec(contents[i])){
            beforeValue = false;
            inArray = true;
        }
    }else if(inArray && !inValue){
        if(/[a-zA-Z0-9$_!'"\(\/]/.exec(contents[i])){
            contents.splice(i, 0, '"');
            i++;
            beforeValue = false;
            inValue = true;
            delimiterCheck(contents[i]);
        }
    }else if(inValue){
        if(!inDelimited() && /[\},\]]/.exec(contents[i])){
            contents.splice(rTrimmedRightBound(i), 0, '"');
            i++;
            inValue = false;
            if(/\]/.exec(contents[i])){
                inArray = false;
            }
            beforeKey = !inArray;
        }else{
            delimiterCheck(contents[i]);
        }
    }
}

return contents.join("");
}
于 2012-05-04T00:33:14.257 回答