将Lua数据转换为JSON



这个魔兽世界插件输出一个EPGP。

我写了一个插件,将Lua数据转换为JSON对象,以便在公会网站上显示。它在旧版本的插件中工作,但现在我在试图让它正确转换文件时遇到了麻烦。下面是显示转换问题的两个代码片段——请看这个演示。

第一个用于形成嵌套数组:

["roster_info"] = {
    {
        "Agantica", -- [1]
        "ROGUE", -- [2]
        "09/03-2013", -- [3]
    }, -- [1]
    {
        "Intikamim", -- [1]
        "PALADIN", -- [2]
        "17/02-2013", -- [3]
    }, -- [2]
},

"roster_info" : [
    [
        "Agantica",
        "ROGUE",
        "09/03-2013"
    ],
    [
        "Intikamim",
        "PALADIN",
        "17/02-2013"
    ]
]

但是替换字符串将下一个代码片段视为嵌套数组,而它应该是数组内部的对象:

["bonus_loot_log"] = {
    {
        ["player"] = "Magebox",
        ["timestamp"] = "2013-03-07 13:44:00",
        ["coinsLeft"] = "-1",
        ["reward"] = "|cffa335ee|Hitem:86815:0:0:0:0:0:0:632235520:90:0:445|h[Attenuating Bracers]|h|r",
    }, -- [1]
            {
        ["player"] = "Lîutasila",
        ["coinsLeft"] = "-1",
        ["timestamp"] = "2013-03-07 13:47:00",
    }, -- [2]
},

"bonus_loot_log" : [
    [
        "player" : "Magebox",
        "timestamp" : "2013-03-07 13:44:00",
        "coinsLeft" : "-1",
        "reward" : "|cffa335ee|Hitem:86815:0:0:0:0:0:0:632235520:90:0:445|h[Attenuating Bracers]|h|r"
    ],
    [
        "player": "Lîutasila",
        "coinsLeft": "-1",
        "timestamp": "2013-03-07 13:47:00"
    ]
]

这是只在第一个代码片段上起作用的字符串转换脚本。

lua_string
    .replace(/[(.*)]s=s/g,'$1:')     // change equal to colon & remove outer brackets
    .replace(/[trn]/g,'')              // remove tabs & returns
    .replace(/},s--s[d+]}/g,']]') // replace sets ending with a comment with square brackets
    .replace(/,s--s[d+]/g,',')      // remove close subgroup and comment
    .replace(/,(}|])/g,'$1')            // remove trailing comma
    .replace(/},{/g,'],[')             // replace curly bracket set with square brackets
    .replace(/{{/g,'[[')                // change double curlies to square brackets
    .replace(/EPGP_DBs=/,'');

所以,我需要一些帮助让Lua正确转换对象数组(第二个例子)。

一般不能简单地通过字符串操作将任何Lua表转换为JSON数据。问题是,虽然Lua对数组和字典都使用表,但JSON需要两种不同的类型。还有其他语法差异。

这是最好的解决模块之间的Lua和JSON表示转换。看看Lua wiki上的JSON模块,找到一个Lua模块来将Lua转换为JSON。有多个模块,其中一些是纯Lua,是嵌入《魔兽世界》的好选择。它们可以正确地检测表是否代表数组或字典,并输出相关的JSON。

可以通过语法解析稳定转换。然而,这是一个非常繁琐的过程。

        $(function () {
            $("#run").on("click", function () {
                let src = $("#src").val();
                let [i, contents] = convert(src, 0, []);
                function isValue(element){
                    let idx = contents.indexOf(element) + 1
                    for(let i=idx; i < contents.length; i++){
                        if(["SPACE","TAB","RETURN"].indexOf(contents[i].type) > -1) continue;
                        if(contents[i].type == "SPLIT") return 0
                        if(contents[i].type == "BRKT_F") return 2
                        if(["BRKT_S","BRKT_W","BREAK","FBREAK"].indexOf(contents[i].type) > -1) return 1
                    }
                }
              
                let converted = "";
                contents.forEach((element, index) => {
                    switch(element.type){
                        case "NUMBER":{
                            converted += element.content
                            break;
                        }
                        case "UNKNOWN": {
                            if(isValue(element)==1){
                              if(element.content == "return"){
                              } else if(["true","false"].indexOf(element.content)>-1){
                                converted += element.content
                              } else {
                                converted += '"' + element.content + '"'
                              }
                            } else if(isValue(element)==2){
                                converted += element.content
                            } else {
                                converted += '"' + element.content + '"'
                            }
                            break;
                        }
                        case "STR_S":
                        case "STR_D":{
                            converted += element.content
                            break;
                        }
                        case "BRKT_S":{
                            converted += element.content
                            break;
                        }
                        case "BRKT_W":{
                            converted += element.content
                            break;
                        }
                        case "BRKT_F":{
                            converted += element.content
                            break;
                        }
                        case "SPACE":{
                            converted += element.content
                            break;
                        }
                        case "TAB":{
                            converted += element.content
                            break;
                        }
                        case "RETURN":{
                            converted += element.content
                            break;
                        }
                        case "BREAK":{
                            converted += ","
                            break;
                        }
                        case "FBREAK":{
                            converted += "."
                            break;
                        }
                        case "SPLIT":{
                            converted += ":"
                            break;
                        }
                    }
                });
                $("#result").val(converted)
            })
        })
      function getBracketSurfaceInner(contents, element){
        if(["BRKT_S", "BRKT_W", "BRKT_F"].indexOf(element.type) == -1 || "]})".indexOf(element.content) == -1) return "";
        let idx = contents.indexOf(element)
        let innerElements = [];
        let nest = 1;
        for(let i=idx-1; i>=1; i--){
          if(["BRKT_S", "BRKT_W", "BRKT_F" ].indexOf(contents[i].type)>=0){
            if("]})".indexOf(contents[i].content)>=0){ nest ++ }
            if("[{(".indexOf(contents[i].content)>=0){ nest -- }
          }
          if(nest==0 && contents[i].type == element.type){
            return innerElements;
          }
          if(nest == 1) {
            innerElements.unshift(contents[i]);
          }
        }
        return innerElements;
      }

      function removeLastCamma(contents, element){
        let idx = contents.indexOf(element)
        let last = -1;
        for(let i=idx-1; i>=1; i--){
          if(["NUMBER", "UNKNOWN", "STR_S", "STR_D"].indexOf(contents[i].type)>=0) return;
          if(contents[i].type == "BREAK"){
              last = i;
              break;
          }
        }
        contents.splice(last, 1);
      }
        function convert(text, pos, contents) {
            let MODE = undefined;
            // NUMBER
            // UNKNOWN
            // SPLIT
            // BREAK
            // FBREAK
            // STR_S
            // STR_D
            // BRKT_S
            // BRKT_W
            // BRKT_F
            // CTRL
            // RETURN
            let MODES = [MODE];
            let content = "", currentElement;
            let i, c
            function PUSH_BEFORE(replace) {
                if (content.length > 1) {
                    contents.push({
                        type: MODE,
                        content: content.slice(0, content.length - 1),
                    });
                }
                content = "" + (replace ? replace : c)
                currentElement = contents[contents.length-1];
                MODE = MODES.shift()
            }
            function PUSH_AFTER(replace) {
                if (content.length > 0) {
                    let str = (replace ? content.slice(0, content.length - 1) + replace : content.slice(0, content.length));
                    contents.push({
                        type: MODE,
                        content: str,
                    });
                }
                content = ""
                currentElement = contents[contents.length-1];
                MODE = MODES.shift()
            }

            for (i = pos; i < text.length; i++) {
                c = text.charAt(i)
                content = content + c
                if (MODE == "ESCAPE") {
                    MODE = MODES.shift()
                } else
                if (MODE == "STR_S") {
                    if (c == "'") {
                        PUSH_AFTER('"')
                    }
                } else
                if (MODE == "STR_D") {
                    if (c == '"') {
                        PUSH_AFTER()
                    }
                } else
                if (MODE == "BRKT_S") {
                    if (c == ']') {
                        PUSH_BEFORE()
                    }
                } else
                if (MODE == "BRKT_F") {
                    if (c == ')') {
                        PUSH_BEFORE()
                    }
                } else
                if (MODE == "BRKT_W") {
                    if (c == '}') {
                        PUSH_BEFORE()
                    }
                } else {
                    switch (c) {
                        case "{":{
                            PUSH_BEFORE()
                            MODE = "BRKT_W"
                            let begin_idx = contents.length; 
                            contents.push({
                                type: MODE,
                                content: c,
                            });
                            MODES.push(MODE)
                            let [f, innerContents] = convert.call(this, text, i + 1, contents)
                            removeLastCamma(contents, contents[contents.length-1]);
                            
                            let surface = getBracketSurfaceInner(innerContents, innerContents[innerContents.length-1]);
                            let d = 0;
                            for(let l=0; l<surface.length; l++){
                                if(surface[l].type == "SPLIT") { d = 1; break; }
                            }
                            i = f
                            content = ""
                            if(d==0){
                                contents[begin_idx].type = "BRKT_S";
                                contents[begin_idx].content = "[";
                                contents[contents.length-1].type = "BRKT_S";
                                contents[contents.length-1].content = "]";
                                MODE = MODES.shift() | "BRKT_S"
                            } else {
                                MODE = MODES.shift() | "BRKT_W"
                            }
                            break;
                        }
                        case "}":{
                            PUSH_BEFORE()
                            contents.push({
                                type: "BRKT_W",
                                content: c,
                            });
                            return [i, contents]
                            break;
                        }
                        case "[": {
                            PUSH_BEFORE()
                            MODE = "BRKT_S"
                            let begin_idx = contents.length;
                            contents.push({
                                type: MODE,
                                content: c,
                            });
                            MODES.push(MODE)
                            let [f, innerContents] = convert.call(this, text, i + 1, contents)
                            removeLastCamma(contents, contents[contents.length-1]);
                          
                            innerContents = getBracketSurfaceInner(contents, contents[contents.length-1]);
                            let d = 0;
                            for(let l=0; l<innerContents.length; l++){
                              if(["BREAK", "BRKT_F"].indexOf(innerContents[l].type)>-1) {d = 1; break; }
                            }
                            if(d==0){
                                contents[begin_idx].type = "NOP";
                                contents[begin_idx].content = "";
                                contents[contents.length-1].type = "NOP";
                                contents[contents.length-1].content = "";
                            }
                          
                            i = f
                            content = ""
                            MODE = MODES.shift() | "BRKT_S"
                            break;
                        }
                        case "]": {
                            PUSH_BEFORE()
                            contents.push({
                                type: "BRKT_S",
                                content: c,
                            });
                            return [i, contents]
                            break;
                        }
                        case "(": {
                            PUSH_BEFORE()
                            MODE = "BRKT_F"
                            let begin_idx = contents.length;
                            contents.push({
                                type: MODE,
                                content: c,
                            });
                            MODES.push(MODE)
                            let [f, innerContents] = convert.call(this, text, i + 1, contents)
                            removeLastCamma(contents, contents[contents.length-1]);
                          
                            innerContents = getBracketSurfaceInner(contents, contents[contents.length-1]);
                            contents[begin_idx].type = "BRKT_F";
                            contents[begin_idx].content = "(";
                            contents[contents.length-1].type = "BRKT_F";
                            contents[contents.length-1].content = ")";
                          
                            i = f
                            content = ""
                            MODE = MODES.shift() | "BRKT_F"
                            break;
                        }
                        case ")": {
                            PUSH_BEFORE()
                            contents.push({
                                type: "BRKT_F",
                                content: c,
                            });
                            return [i, contents]
                            break;
                        }
                        case "'": {
                            if(MODE=="STR_D") {
                              break;
                            }
                            PUSH_BEFORE('"')
                            MODE = "STR_S"
                            break;
                        }
                        case '"': {
                            if(MODE=="STR_S") {
                              break;
                            }
                            PUSH_BEFORE()
                            MODE = "STR_D"
                            break;
                        }
                        case "\": {
                            MODES.push(MODE)
                            MODE = "ESCAPE"
                            break;
                        }
                        case ",": {
                            PUSH_BEFORE()
                            MODE = "BREAK"
                            break;
                        }
                        case ".": {
                            PUSH_BEFORE()
                            MODE = "FBREAK"
                            break;
                        }
                        case "=": {
                            PUSH_BEFORE(":")
                            MODE = "SPLIT"
                            break;
                        }
                        case ":": {
                            PUSH_BEFORE()
                            MODE = "SPLIT"
                            break;
                        }
                        case " ": {
                            if (MODE != "SPACE") {
                                PUSH_BEFORE()
                                MODE = "SPACE"
                            }
                            break;
                        }
                        case "t": {
                            if (MODE != "TAB") {
                                PUSH_BEFORE()
                                MODE = "TAB"
                            }
                            break;
                        }
                        case "n": {
                            PUSH_BEFORE()
                            MODE = "RETURN"
                            break;
                        }
                        default: {
                            if (" SPACE TAB RETURN BREAK FBREAK SPLIT ".indexOf(" " + MODE + " ") > -1) {
                                PUSH_BEFORE()
                            }
                            if (!isNaN(content)) {
                                MODE = "NUMBER"
                            }
                            else {
                                MODE = "UNKNOWN"
                            }
                            break;
                        }
                    }
                }
            }
            return [i, contents]
        }
#src {
  width: 400px;
  height: 200px;
}
#result {
  width: 400px;
  height: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Source:<br><textarea id="src"></textarea>
<button id="run">Convert</button><br>
Result:<br><textarea id="result"></textarea>

// convert EPGP_DB from LUA to JSON
var str = document.getElementsByTagName('data')[0].innerHTML;
var diff;
do {  // replace curlies around arrays with square brackets
    diff = str.length;
    str = str.replace(/{(((nt*)t)S.*(2.*)*),s--s[d+]3}/g,'[$1$3]');
    diff = diff - str.length;
} while (diff > 0);
str = str
.replace(/EPGP_DBs=s/, '')         // remove variable definition
.replace(/s--s[d+](n)/g, '$1') // remove comment
.replace(/,(nt*})/g, '$1')       // remove trailing comma
.replace(/[(.*?)]s=s/g,'$1:')   // change equal to colon, remove brackets
.replace(/[trn]/g,'');            // remove tabs & returns
console.log(str);
json = window.JSON.parse(str);
console.log(json);
document.getElementById('result').innerText = json.global.last_version;

相关内容

  • 没有找到相关文章

最新更新