关于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>
解析
基本上就是按上面找到规则,用正则匹配与修改。 @H_301_231@ 出现过一些小问题 @H_301_231@ 1. 要跟策划先统一好规则,因为我想工具化,不想每次都手动改表。我订的规则是前五行策划需要的话放说明,不需要的话也统一空出来,万一以后突然策划想补充些什么呢?增加一行default数值配置。然后还是正式数据。 @H_301_231@ 2. cell的写法有两种
<Cell ss:StyleID="s16"/>
<Cell ss:StyleID="s17"><Data ss:Type="Number">2</Data></Cell>
所以不能简单用”< Cell.-< /Cell>”来匹配 @H_301_231@ 类似的变成搜索”< Cell”来确定范围
lua表转lua文件
基本也不难,之前做过,所以直接拿来用。 @H_301_231@ 遇到问题: @H_301_231@ 想统一用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,level + 1)))
else
table.insert(temp,string.format("%s+[%s]%s",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,"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,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 .. ",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 .. "\",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",fileName)
file = assert(io.open(output,'w'))
local des = des or ""
doWrite(string.format("-- %s %s",des),file)
doWrite("local root = {",file)
write2Table(tableInfo,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,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,bracketPos - 1)
string.gsub(nameStr,"%(.-%)",function(str) name = string.sub(str,-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)