XML转Lua表

本文介绍了将XML数据转换为Lua表的过程,包括理解XML格式、使用正则表达式进行解析,以及如何将Lua表转为lua文件。在解析过程中,强调了与策划统一规则的重要性,并解决了匹配XML元素的问题。最后,提到了使用Python的lupa库调用lua代码的实现方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于XML转Lua表,我的想法是,首先摸清XML需要用到的数据的相关格式规律,然后用lua解析出来,保存为Lua表,再把表专为lua文件。

了解XML

首先是我理解的XML格式

<?xml version="1.0"?> <!-- 表头 -->
<Workbook <!-- 表信息 -->
<!-- 省略一些细节 -->
...
 <Worksheet ss:Name="工作表1"> <!-- 这是我们要关注的每个小表标志 如果有多个小表就有多个这样的标志,以小表为单位 -->
   <Table ss:ExpandedColumnCount="6" ss:ExpandedRowCount="20" x:FullColumns="1"
   x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="15"><!-- 不关注 -->
   <Row><!-- 每行信息列出来 -->
    <Cell ss:StyleID="s17"><Data ss:Type="String">字段英文名</Data></Cell><!-- 每列信息列出来 -->
    <Cell ss:StyleID="s17"><Data ss:Type="String">level</Data></Cell>
    <Cell ss:StyleID="s17"><Data ss:Type="String">costXP</Data></Cell>
    <Cell ss:StyleID="s17"><Data ss:Type="String">waterMAX</Data></Cell>
    <Cell ss:StyleID="s17"><Data ss:Type="String">waterrestorespeed</Data></Cell>
    <Cell ss:StyleID="s17"><Data ss:Type="String">levelname</Data></Cell>
    </Row>
</Worksheet>
</Workbook>

解析

基本上就是按上面找到规则,用正则匹配与修改。
出现过一些小问题
1. 要跟策划先统一好规则,因为我想工具化,不想每次都手动改表。我订的规则是前五行策划需要的话放说明,不需要的话也统一空出来,万一以后突然策划想补充些什么呢?增加一行default数值配置。然后还是正式数据。
2. cell的写法有两种

  <Cell ss:StyleID="s16"/>
  <Cell ss:StyleID="s17"><Data ss:Type="Number">2</Data></Cell>

所以不能简单用”< Cell.-< /Cell>”来匹配
类似的变成搜索”< Cell”来确定范围

lua表转lua文件

基本也不难,之前做过,所以直接拿来用。
遇到问题:
想统一用py来调,所以又加了一个py来统一入口,py掉lua用的是lupa,安装方法

sudo pip install lupa

最后,把完整的代码贴一下

local print = print
local table = table
local string = string
local type = type
local pairs = pairs

local printAll = function(target, params)
    local targetType = type(target)
    if targetType == "table" then
        local tip = params and params.tip or "This is a Table!^_<......................................"
        local cache = {[target] = "."}
        local isHead = false

        local function dump(t, space, level)
            local temp = {}
            if not isHead then
                temp = {tip}
                isHead = true
            end
            for k, v in pairs(t) do
                local key = tostring(k)
                if type(v) == "table" then
                    table.insert(temp, string.format("%s+[%s]\n%s", string.rep(" ", level), key, dump(v, space, level + 1)))
                else
                    table.insert(temp, string.format("%s+[%s]%s", string.rep(" ", level), key, tostring(v)))
                end
            end
            return table.concat(temp, string.format("\n%s", space))
        end
        print(dump(target, "", 0))
        print(".................................................")
    elseif targetType == "userdata" then
        return printAll(debug.getuservalue(target), {tip = "Userdata's uservalue detail:"})
    else
        print("[printAll error]: not support type")
    end
end

--Author: mixi
--Date: 2016-06-30 11:31:55
--Abstract: XmlParser xml解析工具 之前的不能用 只好重写

local XMLParsers = {}
local PREV_NAME = "t_"
local SavePath = "/Users/mixi/Desktop"

-- 一些规定
-- 第七行是字段名字,第九行是默认数值存放,第十行开始是有效数据,第一列数据是无效的
local fieldTypeLine = 7
local defaultValueLine = 9
local valueStartLine = 10
-- local invaildLineIndexTable = {min = 2, max = 5}
local valueStartRow = 2
-- 命名
local FieldInfo = {}
-- 默认数据填充
local DefaultInfo = {}
-- 主id 有的话只能有一个 且与default不能同时存在
local mainIndex = false

local function reftesh()
    FieldInfo = {}
    DefaultInfo = {}
    mainIndex = false
end

local function fieldTypeParser(cellInfo)
    FieldInfo = cellInfo
end

local function defaultValueParser(cellInfo)
    -- printAll(cellInfo)
    for rowIndex = valueStartRow, #cellInfo do
        local cellStr = cellInfo[rowIndex]
        if cellStr then
            local startPos, endPos = string.find(cellStr, "default=")
            if endPos then
                value = string.sub(cellStr, endPos + 1)
                local newValue = tonumber(value)
                DefaultInfo[rowIndex] = newValue or value
            elseif not mainIndex then
                startPos, endPos = string.find(cellStr, "main")
                if startPos then
                    mainIndex = FieldInfo[rowIndex]
                end
            end
        end
    end
end

local function enumParser(rowInfo)
end

local function fullDataFromInfo(info)
    local t = {}
    for index, value in ipairs(FieldInfo) do
        if index >= valueStartRow then
            local data = info[index]
            if not data and DefaultInfo[index] then
                data = DefaultInfo[index]
            end
            t[value] = data
        end
    end
    -- printAll(t)
    return t
end

local file = false
local function doWrite(msg, file)
    msg = msg .. "\n"
    file:write(msg)
end

local function write2Table(t, indent, file_handle)
    local pre = string.rep("\t", indent)
    for k,v in pairs(t) do
        if type(v) == "table" then
            if type(k) == "number" then
                doWrite(pre .. "[" .. k .. "]" .. " = {", file_handle)
                write2Table(v, indent + 1, file_handle)
                doWrite(pre .. "},", file_handle)
            elseif type(k) == "string" then
                if tonumber(k) then
                    doWrite(pre .. "[\"" .. k .. "\"] = {", file_handle)
                elseif (tonumber(string.sub(k, 1, 1))) then
                    doWrite(pre .. "[\"" .. k .. "\"] = {", file_handle)
                else
                    doWrite(pre .. k .. " = {", file_handle)
                end
                write2Table(v, indent + 1, file_handle)
                doWrite(pre .. "},", file_handle)
            end
        elseif type(v) == "number" then
            if type(k) == "number" then
                doWrite(pre .. "[" .. k .. "]" .. " = " .. v .. ",", file_handle)
            elseif type(k) == "string" then
                if tonumber(k) then
                    doWrite(pre .. "[\"" .. k .. "\"] = " .. v .. ",", file_handle)
                elseif (tonumber(string.sub(k, 1, 1))) then
                    doWrite(pre .. "[\"" .. k .. "\"] = " .. v .. ",", file_handle)
                else
                    doWrite(pre .. k .. " = " .. v .. ",", file_handle)
                end
            end
        elseif type(v) == "string" then
            local text = string.gsub(v, "[\n]", "")
            text = string.gsub(text, "\"", "\\\"")
            if type(k) == "number" then
                doWrite(pre .. "[" .. k .. "]" .. " = \"" .. text .. "\",", file_handle)
            elseif type(k) == "string" then
                if tonumber(k) then
                    doWrite(pre .. "[\"" .. k .. "\"] = \"" .. text .. "\",", file_handle)
                elseif (tonumber(string.sub(k, 1, 1))) then
                    doWrite(pre .. "[\"" .. k .. "\"] = \"" .. text .. "\",", file_handle)
                else
                    doWrite(pre .. k .. " = \"" .. text .. "\",", file_handle)
                end
            end
        end
    end
end

local function saveFile(tableInfo, fileName, path, des)
    local output = string.format("%s/%s.lua", path, fileName)

    file = assert(io.open(output, 'w'))
    local des = des or ""
    doWrite(string.format("-- %s %s", fileName, des), file)
    doWrite("local root = {", file)
    write2Table(tableInfo, 1, file)
    doWrite("}", file)
    doWrite("return root", file)
    file:close()
end

local lineVaildIndexTable = {
    [fieldTypeLine] = fieldTypeParser,
    [defaultValueLine] = defaultValueParser,
}

local function splitStr(str, sign)
    local info = {}
    local startPos, endPos = 0, 0
    local signLength = string.len(sign)
    local function getSplitInfo()
        startPos = string.find(str, sign, endPos + 1)
        if not startPos then
            return 
        end
        endPos = string.find(str, sign, startPos + signLength)
        if endPos then 
            endPos = endPos - 1
        else
            endPos = string.len(str)
        end
        return string.sub(str, startPos, endPos)
    end
    local splitInfo = getSplitInfo()
    while splitInfo do
        table.insert(info, splitInfo)
        splitInfo = getSplitInfo()
    end
    return info
end

local function filterRowStr(rowStr)
    local info = {}
    local cellIndex = 0
    -- 由于不是一定以</Cell>结尾 还有以/>结尾
    local cellSign = "<Cell"
    local cellStrInfo = splitStr(rowStr, cellSign)
    -- print("cellStrInfo")
    -- printAll(cellStrInfo)
    for i, cellStr in ipairs(cellStrInfo) do
        -- 找到row信息
        string.gsub(
            cellStr, 
            "<Cell.->", 
            function(str)
                local _, pos = string.find(str, "ss:Index")
                if pos then 
                    local startPos, endPos = string.find(str, "\".-\"", pos + 1)
                    cellIndex = string.sub(str, startPos + 1, endPos - 1)
                    cellIndex = tonumber(cellIndex)
                else
                    cellIndex = cellIndex + 1
                end
            end
        )

        local offset = cellIndex - (#info + 1)
        if offset > 0 then
            for i = 1, offset do
                table.insert(info, false)
            end
        end

        if cellIndex < valueStartRow then
            table.insert(info, false)
        else
            local value = false
            -- 解析data
            local prevPos, prevEndPos = string.find(cellStr, "<Data.->")
            local suffixPos, suffixEndPos = string.find(cellStr, "</Data>")
            if prevEndPos and suffixPos then
                local data = string.sub(cellStr, prevEndPos + 1, suffixPos - 1)
                if data then
                    local dataType
                    local str = string.sub(cellStr, prevPos, prevEndPos)
                    string.gsub(str, "\".-\"", function(s) dataType = string.sub(s, 2, -2) end)
                    if dataType and dataType == "Number" then
                        data = tonumber(data)
                    end
                    -- print("dataTypedataType", dataType, data)
                    value = data
                end
            end
            table.insert(info, value)
        end
    end
    return info
end

local function parseXmlText(xmlText)
    local xmlInfo = {}
    local dataInfo = {}

    string.gsub(
        xmlText, 
        "<Worksheet .-</Worksheet>", 
        function(ss)
            -- 找表名
            local name, des = false
            string.gsub(
                ss, 
                "<Worksheet.->", 
                function(nameStr)
                    local bracketPos = string.find(nameStr, "%(")
                    if bracketPos then
                        local startPos, endPos = string.find(nameStr, "\".-\"")
                        des = string.sub(nameStr, startPos + 1, bracketPos - 1)
                        string.gsub(nameStr, "%(.-%)", function(str) name = string.sub(str, 2, -2) end)
                    end
                end
            )
            print("文件名", name)
            -- 有名字的才有效
            if name then
                local dataTable = {}
                reftesh()
                local rowSign = "<Row"
                local rowStrInfo = splitStr(ss, rowSign)

                -- 枚举另外解析
                if name == "enum" then
                    local enumName, enumInfo
                    local enumNameIndex, enumDesIndex, enumValueIndex = 2, 3, 4
                    local rowMaxIndex = #rowStrInfo
                    for lineIndex, rowStr in ipairs(rowStrInfo) do
                        local info = filterRowStr(rowStr)
                        -- printAll(info)
                        if info[enumNameIndex] then
                            if not enumName then
                                enumName = info[enumNameIndex]
                                enumInfo = {}
                            else
                                enumInfo[info[enumNameIndex]] = tonumber(info[enumValueIndex])
                            end
                        elseif enumName and enumInfo then
                            dataTable[enumName] = enumInfo
                            enumName = false
                            enumInfo = {}
                        end
                        -- 最后一项的处理
                        if lineIndex == rowMaxIndex and not dataTable[enumName] then
                            dataTable[enumName] = enumInfo
                        end
                    end
                else
                    -- 表名
                    name = string.format("%s%s", PREV_NAME, name)
                    -- printAll(lineVaildIndexTable)
                    for lineIndex, rowStr in ipairs(rowStrInfo) do
                        if lineVaildIndexTable[lineIndex] then
                            local info = filterRowStr(rowStr)
                            lineVaildIndexTable[lineIndex](info)
                        elseif lineIndex >= valueStartLine then
                            local info = filterRowStr(rowStr)
                            local newData = fullDataFromInfo(info)
                            table.insert(dataTable, newData)
                        end
                    end
                    -- 更换index 不能有重复, 重复的就先覆盖吧
                    if mainIndex then
                        local newDataTable = {}
                        for i, v in ipairs(dataTable) do
                            if v[mainIndex] and newDataTable[v[mainIndex]] then
                                print("警告,有个数据重复了", mainIndex, v[mainIndex], i)
                            end
                            newDataTable[v[mainIndex]] = v
                        end
                        dataTable = newDataTable
                    end
                end
                saveFile(dataTable, name, SavePath, des)                        
            end
        end
    )
end

function XMLParsers.loadFile(xmlFileFullPath, saveFilePath)
    local hFile, err = io.open(xmlFileFullPath, "r");
    if saveFilePath then
        SavePath = saveFilePath
    end
    if hFile and not err then
        local xmlText = hFile:read("*a"); -- read file content
        io.close(hFile);
        return parseXmlText(xmlText), nil;
    else
        print(err)
        return nil
    end
end

return XMLParsers

创建py

#!/usr/bin/env python
# coding=utf-8

import os
import sys
import lupa

# 获取基本路径前缀
dir = os.path.abspath(os.path.dirname(__file__))

# xmlParams
LUA_REQUIRE_DIR = os.path.join(dir, "../xml2Table")
lua = lupa.LuaRuntime()
lua.execute("package.path = package.path .. \";\" ..\"%s/?.lua\"" % LUA_REQUIRE_DIR)
XMLParsers = lua.require('XMLParsers')

# 配置表xml-->lua,生成在临时目录
def convertConfig(configSrc, configTempOutPut):
    for f in os.listdir(configSrc):
        fs = f.split(".")
        if len(fs) > 1 and fs[1] == "xml":
            # print f
            XMLParsers.loadFile(
                os.path.join(configSrc, f), 
                configTempOutPut
            )

configSrc = os.path.join(dir, "../../public/配置表")
configTempOutPut = os.path.join(dir, "../../client/src/game/Resource/config/config")
convertConfig(configSrc, configTempOutPut)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值