关于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)