├── example ├── output │ ├── tindex_test │ ├── item_test │ ├── page_test │ └── tree_test ├── files │ ├── 空白.xls │ ├── 范例.xls │ ├── 关卡 │ │ ├── 副本.xls │ │ └── g关卡.xls │ ├── 多页测试.xls │ ├── 测试树形.xls │ ├── 物品测试.xls │ ├── 物品测试2.xls │ └── struct.yaml ├── run.bat ├── config.lua ├── scripts │ └── example.lua └── alias │ └── example.yaml ├── .gitignore ├── LICENSE ├── converter ├── typedef.py ├── lseri.py ├── alias.py ├── dumper.lua ├── xlsparse.py ├── main.lua └── json.lua └── README.md /example/output/tindex_test: -------------------------------------------------------------------------------- 1 | return { { id=1 }, { id=2 } } -------------------------------------------------------------------------------- /example/files/空白.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bttscut/xls-converter/HEAD/example/files/空白.xls -------------------------------------------------------------------------------- /example/files/范例.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bttscut/xls-converter/HEAD/example/files/范例.xls -------------------------------------------------------------------------------- /example/files/关卡/副本.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bttscut/xls-converter/HEAD/example/files/关卡/副本.xls -------------------------------------------------------------------------------- /example/files/多页测试.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bttscut/xls-converter/HEAD/example/files/多页测试.xls -------------------------------------------------------------------------------- /example/files/测试树形.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bttscut/xls-converter/HEAD/example/files/测试树形.xls -------------------------------------------------------------------------------- /example/files/物品测试.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bttscut/xls-converter/HEAD/example/files/物品测试.xls -------------------------------------------------------------------------------- /example/files/物品测试2.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bttscut/xls-converter/HEAD/example/files/物品测试2.xls -------------------------------------------------------------------------------- /example/files/关卡/g关卡.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bttscut/xls-converter/HEAD/example/files/关卡/g关卡.xls -------------------------------------------------------------------------------- /example/output/item_test: -------------------------------------------------------------------------------- 1 | return { 2 | { id=1, ["描述"]="asdf", ["物品别名"]="斧子" }, 3 | { id=2, ["描述"]="asdf", ["物品别名"]="大刀" }, 4 | [10]={ id=10, ["描述"]="asdf", ["物品别名"]="剪刀" } 5 | } -------------------------------------------------------------------------------- /example/run.bat: -------------------------------------------------------------------------------- 1 | echo start 2 | @echo off 3 | set path=.\bin\lua;.\bin\py;%path% 4 | 5 | set BIN_ROOT=..\converter 6 | set LUA_PATH=%BIN_ROOT%/?.lua 7 | lua %BIN_ROOT%\main.lua config.lua files alias scripts output 8 | 9 | pause -------------------------------------------------------------------------------- /example/output/page_test: -------------------------------------------------------------------------------- 1 | return { 2 | page_test1={ { i_set={ id=10, num=2 }, id=1 }, [10]={ i_set={ id=1, num=3 }, id=10 } }, 3 | page_test2={ 4 | [100]={ ["数目"]=1, ["测试索引"]=100, ["物品集合"]={ id=2, num=2 } }, 5 | [200]={ ["数目"]=200, ["测试索引"]=200, ["物品集合"]={ id=10, num=3 } } 6 | } 7 | } -------------------------------------------------------------------------------- /example/files/struct.yaml: -------------------------------------------------------------------------------- 1 | Example: 2 | - id: int 3 | - num: int 4 | - rate: float 5 | 6 | ItemTest: 7 | - id: string 8 | - num: int 9 | 10 | Vector3: 11 | - x: float 12 | - y: float 13 | - z: float 14 | 15 | Item: 16 | - id: int 17 | - num: int 18 | 19 | Cost: 20 | - item_id: int 21 | - num: int 22 | -------------------------------------------------------------------------------- /example/output/tree_test: -------------------------------------------------------------------------------- 1 | return { 2 | { ["别名"]="大树", ["条目1"]=1, ["条目2"]={ 11, 11 }, ["条目3"]="111" }, 3 | { ["条目3"]="" }, 4 | { ["条目2"]={ 12, 12 }, ["条目3"]="121.0" }, 5 | { ["条目3"]="122.0" }, 6 | { 7 | ["别名"]="小树", 8 | ["条目1"]=2, 9 | ["条目2"]={ 11, 11 }, 10 | ["条目3"]="111.0" 11 | }, 12 | { ["条目3"]="112.0" } 13 | } -------------------------------------------------------------------------------- /example/config.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 格式: 3 | {表,页,导表程序,输出文件名,附加信息} 4 | 页可以是s1, [s1,s2], "*", 分别代表一页, 部分页,全部页 5 | --]] 6 | export = { 7 | {"物品测试.xls", "*", nil, "item_test", {}}, 8 | {"多页测试.xls", "Sheet1", nil, "page_test1", {merge="page_test"}}, 9 | {"多页测试.xls", "Sheet2", nil, "page_test2", {merge="page_test"}}, 10 | {"测试树形.xls", "Sheet1", nil, "tree_test", {}}, 11 | {"测试树形.xls", "Sheet2", nil, "tindex_test", {}}, 12 | --{"范例.xls", "页面1", "example.lua", "example", {}}, 13 | } 14 | 15 | -- save_suffix = ".lua" 16 | --to_json_list = { "weapon_types", "role", "hit_effects", "barrier","profession","monster","fly_items","pet","common_battle","hit_effects"} 17 | -------------------------------------------------------------------------------- /example/scripts/example.lua: -------------------------------------------------------------------------------- 1 | 2 | -- 范例 3 | -- convert按照config文件指定的顺序调用 4 | -- sheet是原始表数据 5 | -- global中是一些全局数据,很少用到 6 | -- ext是一些扩展信息,例如表名 7 | -- 多张表公用一个导表程序时,会自动合并导出的结果 8 | function convert(sheet, global, ext) 9 | print("---------------------") 10 | print(ext) 11 | for k, v in pairs(global.raw) do 12 | print(k, v) 13 | end 14 | for k, v in pairs(global.save) do 15 | print(k, v) 16 | end 17 | return sheet 18 | end 19 | 20 | -- 所有的convert都结束了后,才执行post_convert,post的执行次序也按照config文件指定 21 | -- save是之前convert后的存盘数据,这里可以进一步处理 22 | -- global同上 23 | function post_convert(save, global) 24 | print(...) 25 | -- body 26 | end 27 | -------------------------------------------------------------------------------- /example/alias/example.yaml: -------------------------------------------------------------------------------- 1 | 物品: 2 | .sheets: 3 | 范例.xls: all 4 | id: id 5 | 名字: name 6 | 数值: num 7 | 牛逼: niu 8 | 吃货: eat 9 | 10 | page_test: 11 | .sheets: 12 | 多页测试.xls: Sheet2 13 | .deps: 14 | item: item_test 15 | item_list: item_test 16 | item_dict: 17 | key: item_test 18 | value: item_test 19 | 物品集合: itemset 20 | 测试索引: id 21 | 物品: item 22 | 物品列表: item_list 23 | 物品字典: item_dict 24 | 数目: num 25 | 26 | page_test: 27 | .sheets: 28 | 多页测试.xls: Sheet1 29 | id: id 30 | 物品集合: i_set 31 | 32 | item_test: 33 | .sheet: 34 | 物品测试.xls: all 35 | id: id 36 | 物品别名: item_name 37 | 描述: num 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | 55 | # Sphinx documentation 56 | docs/_build/ 57 | 58 | # PyBuilder 59 | target/ 60 | 61 | #Ipython Notebook 62 | .ipynb_checkpoints 63 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 bttscut@gmail.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /converter/typedef.py: -------------------------------------------------------------------------------- 1 | #coding=utf8 2 | import yaml 3 | 4 | def _log(msg, struct, idx=None): 5 | return "struct:%s def error, idx:<%s>, %s"%(struct, idx, msg) 6 | 7 | def parse(fn): 8 | deps = {} 9 | try: 10 | fp = open(fn, "r") 11 | except IOError: 12 | return {} 13 | struct_d = yaml.load(fp) 14 | fp.close() 15 | for name, cfg in struct_d.iteritems(): 16 | assert isinstance(cfg, list), _log("elem def isn't list", name) 17 | assert name[:1].isupper(), _log("First must upper!", name) 18 | for idx, entry in enumerate(cfg): 19 | assert len(entry)==1, _log("field def error", name, idx) 20 | type_s = entry.values()[0] 21 | assert type_s in ["int", "float", "string"], _log("field type error:<%s>"%type_s, name, idx) 22 | entry[entry.keys()[0]] = type_s 23 | return struct_d 24 | 25 | def _deps_log(msg, struct, field=None): 26 | return "struct:%s deps def error, field:<%s>, %s"%(struct, field, msg) 27 | 28 | def parse_deps(fn, struct_d): 29 | try: 30 | fp = open(fn, "r") 31 | except IOError: 32 | return {} 33 | deps_d = yaml.load(fp) 34 | fp.close() 35 | if not deps_d: 36 | return {} 37 | for name, cfg in deps_d.iteritems(): 38 | assert name in struct_d, _deps_log("struct没有定义", name) 39 | fields = [i.keys()[0] for i in struct_d[name]] 40 | for field, dep in cfg.iteritems(): 41 | assert field in fields, _deps_log("字段没有定义", name, field) 42 | return deps_d 43 | 44 | -------------------------------------------------------------------------------- /converter/lseri.py: -------------------------------------------------------------------------------- 1 | #coding=utf8 2 | from __future__ import unicode_literals 3 | import os, sys 4 | 5 | def _format_list(l): 6 | s = ",".join([tolua(i) for i in l]) 7 | return "{%s}"%s 8 | 9 | def _format_dict(d): 10 | l = [] 11 | for k, v in d.iteritems(): 12 | l.append("[%s]=%s"%(tolua(k), tolua(v))) 13 | s = ",".join(l) 14 | return "{%s}"%s 15 | 16 | def _format_bool(b): 17 | return "true" if b else "false" 18 | 19 | def _format_unicode(obj): 20 | temp = obj.encode("utf8") 21 | return _format_basic(temp) 22 | 23 | def _format_basic(obj): 24 | if isinstance(obj, str): 25 | sep = "'" if obj.find('"') != -1 else '"' 26 | repl_sep = "'" if sep == '"' else '"' 27 | s = "%s%s%s"%(sep, obj.replace(sep, repl_sep).replace("\\", "\\\\").replace("\n", "\\n"), sep) 28 | return s 29 | if obj == None: 30 | return "nil" 31 | return str(obj) 32 | 33 | def _tolua(obj): 34 | return obj 35 | 36 | def tolua(obj): 37 | 38 | if isinstance(obj, list): 39 | return _format_list(obj) 40 | elif isinstance(obj, dict): 41 | return _format_dict(obj) 42 | elif isinstance(obj, bool): 43 | return _format_bool(obj) 44 | elif isinstance(obj, unicode): 45 | return _format_unicode(obj) 46 | else: 47 | return _format_basic(obj) 48 | 49 | 50 | if __name__ == "__main__": 51 | reload(sys) 52 | sys.setdefaultencoding("utf8") 53 | l = ["haha", "很", 1, 1.0] 54 | d = { 55 | "中国":l, 56 | "aaaa":"asdf'fdd'", 57 | "b":[1,2,3] 58 | } 59 | print d 60 | print tolua(d) 61 | -------------------------------------------------------------------------------- /converter/alias.py: -------------------------------------------------------------------------------- 1 | #coding=utf8 2 | from __future__ import unicode_literals 3 | import os, sys 4 | import yaml 5 | 6 | def make_key(bname, sname): 7 | return "%s.%s"%(bname, sname) 8 | 9 | def _parse_sheets(conf): 10 | ret = [] 11 | for k, v in conf.iteritems(): 12 | if isinstance(v, str): 13 | assert v == "all", v 14 | ret.append(k) 15 | else: 16 | assert isinstance(v, list), v 17 | for i in v: 18 | ret.append(make_key(k, i)) 19 | return ret 20 | 21 | def _log(msg, fn, cfgname, xls=None, sheet=None): 22 | return "alias error, %s, file:<%s>, cfg:<%s>, xls:<%s>, sheet:<%s>"%(msg, fn, cfgname, xls, sheet) 23 | 24 | def _parse_deps(depsd): 25 | return depsd 26 | 27 | def parse(xls_dir, path): 28 | raw = {} 29 | field_alias = {} 30 | deps = {} 31 | cfg_defs = set() 32 | all_defs = set() 33 | sheet_defs = set() 34 | 35 | for fn in [os.path.join(path, i) for i in os.listdir(path)]: 36 | if not fn.endswith(".yaml"): 37 | continue 38 | # 专门指定自定义结构依赖的yaml 39 | if fn.endswith("struct_deps.yaml"): 40 | continue 41 | fp = open(fn, "r") 42 | try: 43 | d = yaml.load(fp) 44 | except Exception, e: 45 | raise Exception("alias file:<%s> parse error, maybe tab, space..., msg:%s"%(fn, e)) 46 | fp.close() 47 | for cfgname, item in d.iteritems(): 48 | assert cfgname not in cfg_defs, _log("cfg dup", fn, cfgname) 49 | cfg_defs.add(cfgname) 50 | if ".sheets" not in item: 51 | continue 52 | sheets = item[".sheets"] 53 | del item[".sheets"] 54 | assert isinstance(sheets, dict), _log("sheets is not dict", fn, cfgname) 55 | cfg = {} 56 | if ".deps" in item: 57 | deps[cfgname] = item[".deps"] 58 | cfg["deps"] = item[".deps"] 59 | del item[".deps"] 60 | if ".export" in item: 61 | cfg["export"] = item[".export"] 62 | del item[".export"] 63 | raw[cfgname] = item 64 | cfg["alias"] = item 65 | def _sheet_log(msg): 66 | return _log(msg, fn, cfgname, xls, cont) 67 | def _process_all(cont): 68 | assert cont == "all", _sheet_log("sheet def error") 69 | assert xls not in all_defs, _sheet_log("all def dup") 70 | all_defs.add(xls) 71 | assert xls not in sheet_defs, _sheet_log("all def after sheet") 72 | field_alias[xls] = cfg 73 | def _process_list(cont): 74 | assert isinstance(cont, list), _sheet_log("sheet def type error") 75 | for sh in cont: 76 | assert xls not in all_defs, _sheet_log("sheet def after all") 77 | key = make_key(xls, sh) 78 | assert key not in field_alias, _sheet_log("sheet def after sheet") 79 | sheet_defs.add(xls) 80 | field_alias[key] = cfg 81 | for xls, cont in sheets.iteritems(): 82 | xls_path = os.path.join(xls_dir, xls) 83 | assert os.path.exists(xls_path), "NOT EXIST XLS:%s YMAL:%s !!!" % (xls, fn) 84 | if cont == "all": 85 | _process_all(cont) 86 | else: 87 | if isinstance(cont, str) or isinstance(cont, unicode): 88 | cont = [cont,] 89 | _process_list(cont) 90 | return raw, field_alias, deps 91 | 92 | if __name__ == "__main__": 93 | parse("./alias") 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xls-converter 2 | convert xls or csv to lua/json file with check and custom rule 3 | # 填写格式 4 | **以下所有的格式在example目录下的xls文件中都可以找到例子** 5 | 6 | excel第一行是类型行,填写规则是类型#标签1#标签2#标签n... 7 |
第二行是列名, 填写人方便识别的列名 8 |
第三行开始是数据行 9 |
除第一二行外,任意行的首个单元格中的内容如果以“//”开头,则该行为注释行,不参与导表。 10 |
所有的表名或者页名,只要名字以"_"开头,会被忽略掉,不参与导表。 11 | 12 | # 类型 13 | ## 基本类型 14 | - int 整数 15 | - float 浮点数 16 | - string 字符串 17 | ## 容器类型 18 | - list 数组类型 19 |
各元素用空格隔开,例如list 填写: 1 2 3 20 | - dict 字典类型 21 |
key与value用":"隔开,各项用空格隔开,例如:key:value key2:value2 22 | 23 |
*T是基本类型或者复合类型,但是dict的key不能为复合类型* 24 | 25 | ## 复合类型 26 | 比如坐标,包含x,y,z,可以定义一个单独的类型Point,在**struct.yaml**(导表excel目录下)里面,定义为 27 | 28 | Point: 29 | x: float 30 | y: float 31 | z: float 32 | 33 | 定义完之后,可以在需要填写类型的地方直接填写Point,也可以用作list或者dict的值,比如list,dict 34 |
*注意dict的key不能是复合类型,**dict<复合类型, 其他>是不支持的* 35 | 36 | 复合类型填表时候用竖线隔开每个值,例如 1.1|2.2|3.3,在list和dict中填写遵从list和dict的规范。 37 | 38 | # 标签 39 | 40 | - key:只能用在第一列,表示该列所填内容作为本条目的索引,不得重复,只支持int和string做key。例:int#key 41 | - key_alias: 给key起的别名,只能在第二列,不得重复,只支持string类型,该列填写的别名,可以用作其他表的索引 42 | - default:可以不填,有默认值,并且可以指定。例:int#default 或 int#default(3) 43 | - ignore: 本列会被忽略,一般用于描述说明字段。 44 | - index: 表示本列内容是索引了其他表的key或者key_alias,定义后程序员一定要做依赖检查,否则无法程序中无法使用 45 | - raw: 只能用在第一列,表示整个表原样导出,导出后的的列按照别名定义输出,同时填写的单元按照表头定义类型输出,没填写的一律不输出,在lua脚本中获取为nil。这个标签的目的是为了应对一些复杂的表(例如树形结构的填写方式),将工作完全交给导表脚本,简化设计。 46 | 47 | # 导表程序 48 | 导表的主程序是main.lua,使用py进行表的预处理,在lua中检查依赖,处理,输出最后的文件。
49 | 启动main.lua的时候,向它传入必要的各种路径信息,参考example。
50 | 主配置文件格式如下: 51 | 52 | export = { 53 | {"物品测试.xls", "all", nil, "item_test", {}}, 54 | {"多页测试.xls", "Sheet1", nil, "page_test1", {merge="page_test"}}, 55 | {"多页测试.xls", "Sheet2", nil, "page_test2", {merge="page_test"}}, 56 | {"测试树形.xls", "Sheet1", "example.lua", "tree_test", {}}, 57 | {"测试树形.xls", "Sheet2", nil, "tindex_test", {}}, 58 | } 59 | 60 | 每行的定义:{表,页,导表程序,输出文件名,附加信息}
61 | 页可以是s1, [s1,s2], "all", 分别代表一页, 部分页,全部页
62 | 导表程序可以是nil,则按照默认规则输出。
63 | 附加信息项定义如下: 64 | 65 | - merge=name, 最终输出时将多个名字放入到name的table下,统一输出名字为name的文件。比如上面的例子中,最终会输出: 66 | 67 | 68 | page_test = { 69 | page_test1 = {...}, 70 | page_test2 = {...}, 71 | } 72 | 73 | # 导表脚本 74 | 导表脚本中可以定义两个global函数:
75 | 76 | function convert(sheet, global, ext) 77 | function post_convert(save, global) 78 | 79 | convert负责第一遍顺序处理,它必须return一个table,作为save的数据,参数如下: 80 | 81 | 82 | - sheet是本次调用处理的表内容 83 | - global中传递了一些全局信息 84 | - save,已经添加到save中的数据。 85 | - alias2key, excel中定义的key_alias和key的索引表,方便查询。 86 | - raw,所有的原始表信息。 87 | - ext本次调用的一些附加信息,比如sheet所在的原始文件名,页名。 88 | 89 | return的数据,会以配置表中的存盘名字保存在global.save中,如果不return,则为空。
90 | 注意,convert的处理对象是文件中的页,多个页配置了同个导表程序会依次产生调用,并且最终的结果会自动合并(根据文件是table还是array)。 91 | 92 | post_convert负责第二次处理,此时,所有的表按照config的配置已经处理过了,此时的save参数代表了合并后的所有存盘名字下的数据集合,global含义如上。返回的table作为最终存盘数据。
93 | 此时可以对最终的存盘结果做调整,甚至可以向global.save中删除添加表项。 94 | 95 | # 别名和依赖定义 96 | 格式: 97 | 98 | cfg_name: 99 | .sheets: 100 | file_name: sheet_name 101 | .deps: 102 | alias_name: deps_cfg_name 103 | 编号: id 104 | 原名: alias_name 105 | 106 | 范例: 107 | 108 | page_test: 109 | .sheets: 110 | 多页测试.xls: Sheet2 111 | .deps: 112 | item: item_test 113 | item_list: item_test 114 | item_dict: 115 | key: item_test 116 | value: item_test 117 | 物品集合: itemset 118 | 测试索引: id 119 | 物品: item 120 | 物品列表: item_list 121 | 物品字典: item_dict 122 | 数目: num 123 | 124 | 125 | 采用yaml格式,并且要明确以yaml作为文件后缀名。整个alias目录下的所有yaml都会被读取。
126 | 每个配置有个名字cfg\_name, 这个名字最好要跟主配置文件里的存盘名字一致,这样才能用来做依赖检查。
127 | 每个配置中要有个.sheets的key,存放该配置应用于哪些sheet。
128 | 别名的定义就是key:value的键值对。
129 | .sheets的value是一个列表,每个元素是一个字典,例如,文件1:[ 表1,表2 ], 当文件中所有的表都应用时,可以这样指定,文件:all。
130 | .deps定义了表中字段的依赖关系,alias\_name: deps\_cfg\_name, 其中deps\_cfg\_name就是对应依赖表的config.lua中的导出存盘名字。
131 | 检查依赖的流程如下: 132 | 133 | 1. xls文件的每个页经过py读取,进行别名转换,输出给main.lua脚本 134 | 1. lua中进行预处理,把所有savename(存盘名字)相同的表的key和key_alias的字段进行合并,并保存在savename对应的table中,即key_check。 135 | 1. 根据config配置的savename查询别名定义里是否同名的配置项,如果有则看别名定义里是否有改别名的deps配置,如果有,则根据配置依赖的deps\_cfg\_name在key_check的table中查询该字段的value是否存在。 136 | 1. 里面有个隐藏的规则是,如果字段为字符串,则首先将该字段转换成对应的key的值。 137 | 2. 注意!!!别名的配置项名字只有在本别名定义中有deps字段时才需要与config中的导出名字一致,这样框架才能根据名字找到需要检查的字段。而依赖项(deps\_cfg\_name)的字段,就是config里的导出名字。 138 | 139 | # 复合类型的定义和依赖 140 | 格式: 141 | 142 | Example: 143 | - id: int, item 144 | - num: int 145 | - rate: float 146 | 复合类型的字段类型一定是基础类型,如果某字段有依赖的话,用逗号隔开输入依赖项的名字。 147 | -------------------------------------------------------------------------------- /converter/dumper.lua: -------------------------------------------------------------------------------- 1 | --[[ DataDumper.lua 2 | Copyright (c) 2007 Olivetti-Engineering SA 3 | 4 | Permission is hereby granted, free of charge, to any person 5 | obtaining a copy of this software and associated documentation 6 | files (the "Software"), to deal in the Software without 7 | restriction, including without limitation the rights to use, 8 | copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the 10 | Software is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 18 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 20 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 21 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | OTHER DEALINGS IN THE SOFTWARE. 24 | ]] 25 | 26 | local dumplua_closure = [[ 27 | local closures = {} 28 | local function closure(t) 29 | closures[#closures+1] = t 30 | t[1] = assert(loadstring(t[1])) 31 | return t[1] 32 | end 33 | 34 | for _,t in pairs(closures) do 35 | for i = 2,#t do 36 | debug.setupvalue(t[1], i-1, t[i]) 37 | end 38 | end 39 | ]] 40 | 41 | local lua_reserved_keywords = { 42 | 'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for', 43 | 'function', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat', 44 | 'return', 'then', 'true', 'until', 'while' } 45 | 46 | local function keys(t) 47 | local res = {} 48 | local oktypes = { stringstring = true, numbernumber = true } 49 | local function cmpfct(a,b) 50 | if oktypes[type(a)..type(b)] then 51 | return a < b 52 | else 53 | return type(a) < type(b) 54 | end 55 | end 56 | for k in pairs(t) do 57 | res[#res+1] = k 58 | end 59 | table.sort(res, cmpfct) 60 | return res 61 | end 62 | 63 | local c_functions = {} 64 | for _,lib in pairs{'_G', 'string', 'table', 'math', 65 | 'io', 'os', 'coroutine', 'package', 'debug'} do 66 | local t = _G[lib] or {} 67 | lib = lib .. "." 68 | if lib == "_G." then lib = "" end 69 | for k,v in pairs(t) do 70 | if type(v) == 'function' and not pcall(string.dump, v) then 71 | c_functions[v] = lib..k 72 | end 73 | end 74 | end 75 | 76 | local function DataDumper(value, varname, fastmode, ident) 77 | local defined, dumplua = {} 78 | -- Local variables for speed optimization 79 | local string_format, type, string_dump, string_rep = 80 | string.format, type, string.dump, string.rep 81 | local tostring, pairs, table_concat = 82 | tostring, pairs, table.concat 83 | local keycache, strvalcache, out, closure_cnt = {}, {}, {}, 0 84 | setmetatable(strvalcache, {__index = function(t,value) 85 | local res = string_format('%q', value) 86 | t[value] = res 87 | return res 88 | end}) 89 | local fcts = { 90 | string = function(value) return strvalcache[value] end, 91 | number = function(value) return value end, 92 | boolean = function(value) return tostring(value) end, 93 | ['nil'] = function(value) return 'nil' end, 94 | ['function'] = function(value) 95 | return string_format("loadstring(%q)", string_dump(value)) 96 | end, 97 | userdata = function() error("Cannot dump userdata") end, 98 | thread = function() error("Cannot dump threads") end, 99 | } 100 | local function test_defined(value, path) 101 | if defined[value] then 102 | if path:match("^getmetatable.*%)$") then 103 | out[#out+1] = string_format("s%s, %s)\n", path:sub(2,-2), defined[value]) 104 | else 105 | out[#out+1] = path .. " = " .. defined[value] .. "\n" 106 | end 107 | return true 108 | end 109 | defined[value] = path 110 | end 111 | local function make_key(t, key) 112 | local s 113 | if type(key) == 'string' and key:match('^[_%a][_%w]*$') then 114 | s = key .. "=" 115 | else 116 | s = "[" .. dumplua(key, 0) .. "]=" 117 | end 118 | t[key] = s 119 | return s 120 | end 121 | for _,k in ipairs(lua_reserved_keywords) do 122 | keycache[k] = '["'..k..'"] = ' 123 | end 124 | if fastmode then 125 | fcts.table = function (value) 126 | -- Table value 127 | local numidx = 1 128 | out[#out+1] = "{" 129 | for key,val in pairs(value) do 130 | if key == numidx then 131 | numidx = numidx + 1 132 | else 133 | out[#out+1] = keycache[key] 134 | end 135 | local str = dumplua(val) 136 | out[#out+1] = str.."," 137 | end 138 | if string.sub(out[#out], -1) == "," then 139 | out[#out] = string.sub(out[#out], 1, -2); 140 | end 141 | out[#out+1] = "}" 142 | return "" 143 | end 144 | else 145 | fcts.table = function (value, ident, path) 146 | if test_defined(value, path) then return "nil" end 147 | -- Table value 148 | local sep, str, numidx, totallen = " ", {}, 1, 0 149 | local meta, metastr = (debug or getfenv()).getmetatable(value) 150 | if meta then 151 | ident = ident + 1 152 | metastr = dumplua(meta, ident, "getmetatable("..path..")") 153 | totallen = totallen + #metastr + 16 154 | end 155 | for _,key in pairs(keys(value)) do 156 | local val = value[key] 157 | local s = "" 158 | local subpath = path 159 | if key == numidx then 160 | subpath = subpath .. "[" .. numidx .. "]" 161 | numidx = numidx + 1 162 | else 163 | s = keycache[key] 164 | if not s:match "^%[" then subpath = subpath .. "." end 165 | subpath = subpath .. s:gsub("%s*=%s*$","") 166 | end 167 | s = s .. dumplua(val, ident+1, subpath) 168 | str[#str+1] = s 169 | totallen = totallen + #s + 2 170 | end 171 | if totallen > 80 then 172 | sep = "\n" .. string_rep(" ", ident+1) 173 | end 174 | str = "{"..sep..table_concat(str, ","..sep).." "..sep:sub(1,-3).."}" 175 | if meta then 176 | sep = sep:sub(1,-3) 177 | return "setmetatable("..sep..str..","..sep..metastr..sep:sub(1,-3)..")" 178 | end 179 | return str 180 | end 181 | fcts['function'] = function (value, ident, path) 182 | if test_defined(value, path) then return "nil" end 183 | if c_functions[value] then 184 | return c_functions[value] 185 | elseif debug == nil or debug.getupvalue(value, 1) == nil then 186 | return string_format("loadstring(%q)", string_dump(value)) 187 | end 188 | closure_cnt = closure_cnt + 1 189 | local res = {string.dump(value)} 190 | for i = 1,math.huge do 191 | local name, v = debug.getupvalue(value,i) 192 | if name == nil then break end 193 | res[i+1] = v 194 | end 195 | return "closure " .. dumplua(res, ident, "closures["..closure_cnt.."]") 196 | end 197 | end 198 | function dumplua(value, ident, path) 199 | return fcts[type(value)](value, ident, path) 200 | end 201 | if varname == nil then 202 | varname = "return " 203 | elseif varname:match("^[%a_][%w_]*$") then 204 | varname = varname .. " = " 205 | end 206 | if fastmode then 207 | setmetatable(keycache, {__index = make_key }) 208 | out[1] = varname 209 | table.insert(out,dumplua(value, 0)) 210 | return table.concat(out) 211 | else 212 | setmetatable(keycache, {__index = make_key }) 213 | local items = {} 214 | for i=1,10 do items[i] = '' end 215 | items[3] = dumplua(value, ident or 0, "t") 216 | if closure_cnt > 0 then 217 | items[1], items[6] = dumplua_closure:match("(.*\n)\n(.*)") 218 | out[#out+1] = "" 219 | end 220 | if #out > 0 then 221 | items[2], items[4] = "local t = ", "\n" 222 | items[5] = table.concat(out) 223 | items[7] = varname .. "t" 224 | else 225 | items[2] = varname 226 | end 227 | return table.concat(items) 228 | end 229 | end 230 | 231 | return DataDumper 232 | -------------------------------------------------------------------------------- /converter/xlsparse.py: -------------------------------------------------------------------------------- 1 | #coding=utf8 2 | from __future__ import unicode_literals 3 | import sys 4 | import os 5 | import shutil 6 | import re 7 | import xlrd 8 | import yaml 9 | import alias 10 | import typedef 11 | import lseri 12 | 13 | reload(sys) 14 | sys.setdefaultencoding("utf8") 15 | import codecs 16 | def cp65001(name): 17 | if name.lower() == "cp65001": 18 | return codecs.lookup("utf8") 19 | codecs.register(cp65001) 20 | 21 | g_error_l = [] 22 | g_alias_d = None 23 | g_alias_deps = None 24 | g_struct_deps = {} 25 | g_sheet_struct_deps = {} 26 | 27 | fp_log = None 28 | def fprint(msg): 29 | global fp_log 30 | if not fp_log: 31 | fp_log = open("log.txt", "w") 32 | fp_log.write(msg) 33 | fp_log.write(os.linesep) 34 | fp_log.flush() 35 | 36 | output_list = [] 37 | def output(msg): 38 | output_list.append(msg) 39 | return 40 | 41 | def flush_output(): 42 | s = lseri.tolua(output_list) 43 | #fprint(s) 44 | sys.stdout.write(s) 45 | sys.stdout.flush() 46 | 47 | def error(msg): 48 | s = lseri.tolua({"error":msg}) 49 | sys.stdout.write(s) 50 | sys.stdout.flush() 51 | sys.exit(1) 52 | 53 | def print_exc(): 54 | import traceback 55 | traceback.print_exc() 56 | 57 | # 基础数据类型:int, float, string 58 | _bool_d = {"true":True,"false":False,"1":True,"0":False,"1.0":True,"0.0":False} 59 | def _conv_bool(s): 60 | return _bool_d[str(s).lower()] 61 | basictype_convert_tbl = { 62 | "int":int, 63 | "float":float, 64 | "bool":_conv_bool, 65 | "string":lambda s:str(s).encode("utf8"), 66 | } 67 | # 自定义struct 68 | g_struct_d = None 69 | 70 | # 容器数据类型: list, dict 71 | CONTAINER_RE = re.compile("^(list|dict)<(.+?)(?:,\s*(.+?))??>$") 72 | #m = CONTAINER_RE.match(u"list") 73 | #print(m.groups()) 74 | 75 | def find_struct_deps(type_s): 76 | if type_s in basictype_convert_tbl: 77 | return None 78 | if type_s in g_struct_d: 79 | if type_s in g_struct_deps: 80 | return ["s", g_struct_deps[type_s]] 81 | else: 82 | return None 83 | m = CONTAINER_RE.match(type_s) 84 | sg = m.groups() 85 | if sg[0] == "list": 86 | if sg[1] in g_struct_deps: 87 | return ["l", g_struct_deps[sg[1]]] 88 | elif sg[0] == "dict": 89 | if sg[2] in g_struct_deps: 90 | return ["d", g_struct_deps[sg[2]]] 91 | return None 92 | 93 | def get_basic_or_struct_cf(s): 94 | if s in basictype_convert_tbl: 95 | return basictype_convert_tbl[s] 96 | if s not in g_struct_d: 97 | return None 98 | cfg = g_struct_d[s] 99 | def cf(cont): 100 | if cont == "": 101 | return None 102 | l = cont.split("|") 103 | assert len(l)==len(cfg), cont 104 | ret = {} 105 | for idx, v in enumerate(l): 106 | ret[cfg[idx].keys()[0]] = basictype_convert_tbl[cfg[idx].values()[0]](v) 107 | return ret 108 | return cf 109 | 110 | def make_convert_func(type_s): 111 | cf = get_basic_or_struct_cf(type_s) 112 | if cf: 113 | return cf 114 | m = CONTAINER_RE.match(type_s) 115 | sg = m.groups() 116 | assert len(sg) == 3, "类型解析错误" 117 | if sg[0] == "list": 118 | assert not sg[2], "list定义有误" 119 | typ = sg[1] 120 | f = get_basic_or_struct_cf(typ) 121 | assert f, "list元素类型定义有误:%s"%type_s 122 | def cf(s): 123 | # xls单元格会默认把数字格式转换成float 124 | if typ == "int" and isinstance(s, float): 125 | s = str(int(s)) 126 | return [f(i) for i in s.split()] 127 | return cf 128 | elif sg[0] == "dict": 129 | k_f = basictype_convert_tbl[sg[1]] 130 | assert k_f, "dict key类型定义有误%s"%type_s 131 | v_f = get_basic_or_struct_cf(sg[2]) 132 | assert v_f, "dict value类型定义有误:%s"%type_s 133 | def cf(s): 134 | d = {} 135 | for i in s.split(): 136 | l = i.split(":") 137 | d[k_f(l[0])] = v_f(l[1]) 138 | return d 139 | return cf 140 | raise Exception("未定义类型:%s"%type_s) 141 | 142 | TYPE_DEFAULT_RE = re.compile("^default(?:\((.*)\))?$") 143 | #m = TYPE_DEFAULT_RE.match("default") 144 | #print m.group(2) 145 | TAG_KEY_RE = re.compile("^key(?:\((.*)\))?$") 146 | 147 | type_default_tbl = { 148 | "int":0, 149 | "float":0.0, 150 | "string":"", 151 | "bool":False, 152 | } 153 | def parse_type_tag(ncol, tag_sl, type_s, conv_f): 154 | ret = {} 155 | def _key_f(): 156 | assert ncol == 0, "key必须是第一列" 157 | assert type_s == "int" or type_s == "string", "类型:<%s>不能做key"%type_s 158 | ret["key"] = True 159 | def _ignore_f(): 160 | ret["ignore"] = True 161 | def _raw_f(): 162 | ret["raw"] = True 163 | def _key_alias_f(): 164 | assert ncol == 1, "key_alias必须是第二列" 165 | ret["key_alias"] = True 166 | def _index_f(): 167 | ret["index"] = True 168 | def _defaultnil_f(): 169 | ret["default"] = None 170 | tag_fs = { 171 | #"key":_key_f, 172 | "ignore":_ignore_f, 173 | "raw":_raw_f, 174 | "key_alias":_key_alias_f, 175 | "index":_index_f, 176 | "defaultnil": _defaultnil_f, 177 | } 178 | 179 | for tag_s in tag_sl: 180 | if tag_s in tag_fs: 181 | #assert tag_s in tag_fs, "标签填写错误:<%s>"%tag_s 182 | tag_fs[tag_s]() 183 | continue 184 | # default 处理 185 | m = TYPE_DEFAULT_RE.match(tag_s) 186 | if m: 187 | assert "default" not in ret, "重复设置default" 188 | default_val = m.group(1) 189 | if not default_val and type_s in type_default_tbl: 190 | default_val = type_default_tbl[type_s] 191 | else: 192 | default_val = conv_f(default_val if default_val else "") 193 | ret["default"] = default_val 194 | continue 195 | 196 | # key 处理 197 | m = TAG_KEY_RE.match(tag_s) 198 | if m: 199 | assert ncol == 0, "key必须是第一列" 200 | assert type_s == "int" or type_s == "string", "类型:<%s>不能做key"%type_s 201 | d = {} 202 | key_attr = m.group(1) 203 | if key_attr: 204 | assert key_attr == "incr", "key的属性只能是incr" 205 | assert type_s == "int", "incr key只能是int类型" 206 | d["incr"] = True 207 | ret["key"] = d 208 | continue 209 | raise Exception(tag_s) 210 | # 容器类型list和dict默认就是空 211 | if get_basic_or_struct_cf(type_s) == None and "default" not in ret: 212 | ret["default"] = conv_f("") 213 | 214 | assert not ("key" in ret and "default" in ret), "key类型不能设置default" 215 | assert not ("key" in ret and "ignore" in ret), "key类型不能设置ignore" 216 | assert not ("key" in ret and "index" in ret), "key类型不能设置index" 217 | return ret 218 | 219 | def open_xls(filename): 220 | return xlrd.open_workbook(filename) 221 | 222 | # 返回的是下标 223 | def _find_dup_in_list(l): 224 | d = {} 225 | for n, i in enumerate(l): 226 | if i in d: 227 | return n 228 | d[i] = 1 229 | return -1 230 | 231 | def _num2colname(n): 232 | def _n2chr(i): 233 | return chr(65+i) 234 | if n < 26: 235 | return _n2chr(n) 236 | elif n < 26*27: 237 | return _n2chr(n/26-1) + _n2chr(n%26) 238 | return str(n) 239 | 240 | EXPORT_ALL_RE = re.compile("^all(?:-\[(.*)\])?$") 241 | def _parse_export(s): 242 | if not s: 243 | return None, None 244 | m = EXPORT_ALL_RE.match(s) 245 | if m: 246 | exclude = m.group(1) 247 | if not exclude: 248 | return "all", [] 249 | else: 250 | return "all", [i.strip() for i in exclude.split(",")] 251 | else: 252 | return None, None 253 | 254 | 255 | def sheet_to_dict(sheet, alias_d): 256 | conv_funcs = [] 257 | tags = [] 258 | struct_deps_l = [] 259 | struct_deps_d = {} 260 | alias_deps = alias_d.get("deps", {}) if alias_d else {} 261 | export_type = alias_d.get("export") if alias_d else None 262 | alias_d = alias_d.get("alias") if alias_d else None 263 | try: 264 | # 处理第一行,类型 265 | end_col = None 266 | for n, i in enumerate(sheet.row_values(0)): 267 | end_col = n + 1 268 | # 允许类型列填空,意味该列忽略 269 | if n > 0 and i == "" : 270 | end_col = n 271 | break 272 | type_sl = i.split("#") 273 | conv_f = make_convert_func(type_sl[0]) 274 | conv_funcs.append(conv_f) 275 | tags.append(parse_type_tag(n, type_sl[1:], type_sl[0], conv_f)) 276 | struct_deps_l.append(find_struct_deps(type_sl[0])) 277 | except Exception, e: 278 | raise Exception("sheet:<%s>类型行%s列填写错误, 内容:<%s>, msg:%s"%(sheet.name, _num2colname(n), i, e)) 279 | 280 | name_row = sheet.row_values(1,end_colx=end_col) 281 | dup_idx = _find_dup_in_list(name_row) 282 | if dup_idx != -1: 283 | raise Exception("sheet:<%s>列名重复:<%s>"%(sheet.name, name_row[dup_idx])) 284 | col_names = [] 285 | if alias_d: 286 | export_flag, export_exmsg = _parse_export(export_type) 287 | export_all = export_flag == "all" 288 | for name in name_row: 289 | col_names.append(alias_d.get(name, name if export_all and name not in export_exmsg else None)) 290 | else: 291 | col_names = name_row 292 | 293 | for n, i in enumerate(col_names): 294 | if i and struct_deps_l[n]: 295 | struct_deps_d[i] = struct_deps_l[n] 296 | 297 | check_tag_f = lambda x,s: True if s in x else False 298 | raw_flag = check_tag_f(tags[0],"raw") 299 | raw_keys = {} 300 | key_flag = check_tag_f(tags[0],"key") 301 | key_incr_flag = check_tag_f(tags[0]["key"], "incr") if key_flag else False 302 | last_key = [0,] 303 | key_alias_flag = check_tag_f(tags[1],"key_alias") if len(tags) > 1 else False 304 | ret = {} if key_flag and not raw_flag else [] 305 | key_alias_d = {} if key_alias_flag else None 306 | for nrow in xrange(2, sheet.nrows): 307 | row = sheet.row_values(nrow, end_colx=end_col) 308 | row_d = {} 309 | try: 310 | # 注释行忽略 311 | if isinstance(row[0], unicode) and row[0].startswith("//"): 312 | continue 313 | row_key = None 314 | row_key_alias = None 315 | for ncol, value in enumerate(row): 316 | tag = tags[ncol] 317 | col_name = col_names[ncol] 318 | if not col_name: 319 | # key和key_alias列走流程可以不导出 320 | is_key = key_flag and "key" in tag 321 | is_alias = key_alias_flag and "key_alias" in tag 322 | if not is_key and not is_alias: 323 | continue 324 | else: 325 | if "index" in tag and col_name not in struct_deps_d: 326 | if alias_d and col_name not in alias_deps: 327 | raise Exception("%s填写了index但没有定义依赖"%col_name) 328 | cv = None 329 | if "ignore" in tag: 330 | continue 331 | # 如果该格子不填,获取的是空串 332 | if value == "" and "default" in tag: 333 | cv = tag["default"] 334 | else: 335 | # raw key 列可以为空 336 | if not raw_flag: 337 | assert value != "", "表项为空" 338 | cv = conv_funcs[ncol](value) 339 | else: 340 | if value != "": 341 | cv = conv_funcs[ncol](value) 342 | if ncol == 0 and "key" in tag: 343 | row_key = cv 344 | if ncol == 1 and "key_alias" in tag: 345 | row_key_alias = cv 346 | if col_name: 347 | row_d[col_name] = cv 348 | def _check_key(check_d): 349 | # 检查key是否重复 350 | # raw表,key列可能是None,不用检查了 351 | if raw_flag and row_key == None: 352 | return 353 | assert row_key not in check_d, "key列内容重复, 行:%s,值:%s"%(nrow+1, row_key) 354 | check_d[row_key] = row_d 355 | if key_incr_flag: 356 | assert row_key == last_key[0] + 1, "incr key 不连续:%d"%row_key 357 | last_key[0] = row_key 358 | if key_alias_flag: 359 | assert row_key_alias not in key_alias_d, "key_alias列内容重复, 行:%s,值:%s"%(nrow+1, row_key_alias) 360 | key_alias_d[row_key_alias] = row_key 361 | if isinstance(ret, dict): 362 | _check_key(ret) 363 | else: 364 | if raw_flag and key_flag: 365 | _check_key(raw_keys) 366 | ret.append(row_d) 367 | except Exception, e: 368 | # print_exc() 369 | raise Exception("sheet:%s, cell:<行%s-列%s>, %s"%(sheet.name, nrow+1, _num2colname(ncol), e)) 370 | return ret, struct_deps_d, key_alias_d 371 | 372 | 373 | def get_alias_conf(fn, shname): 374 | if fn in g_alias_d: 375 | return g_alias_d[fn] 376 | key = alias.make_key(fn, shname) 377 | return g_alias_d.get(key) 378 | 379 | def convert_xls(filename): 380 | try: 381 | wb = open_xls(filename) 382 | ret = {} 383 | ext = {} 384 | for sheet in wb.sheets(): 385 | if sheet.name.startswith("_"): 386 | continue 387 | if sheet.nrows < 2: 388 | continue 389 | data, deps_d, key_alias_d = sheet_to_dict(sheet, get_alias_conf(filename, sheet.name)) 390 | ret[sheet.name] = data 391 | d = {} 392 | if len(deps_d) > 0: 393 | d["deps"] = deps_d 394 | d["typ"] = "l" if type(data) == type([]) else "d" 395 | d["key_alias"] = key_alias_d 396 | if d["typ"] == "l" and key_alias_d: 397 | tmp = {} 398 | for k, v in key_alias_d.iteritems(): 399 | tmp[v] = k 400 | d["key_check"] = tmp 401 | ext[sheet.name] = d 402 | return ret, ext 403 | except Exception, e: 404 | # print_exc() 405 | error("file:%s, error: %s"%(filename, e)) 406 | 407 | def run_dir(path): 408 | os.chdir(path) 409 | files = [] 410 | def visit(arg, dirname, names): 411 | for name in names: 412 | if name.endswith(".xls") and not name.startswith("_"): 413 | files.append(os.path.relpath(os.path.join(dirname, name), ".")) 414 | os.path.walk(".", visit, None) 415 | for fn in files: 416 | fn = fn.replace(os.sep, "/") 417 | data, ext = convert_xls(fn) 418 | out = {} 419 | out["filename"] = fn 420 | out["data"] = data 421 | out["ext"] = ext 422 | #if len(deps_d) > 0: 423 | #out["struct_deps"] = deps_d 424 | output(lseri._tolua(out)) 425 | 426 | if __name__ == "__main__": 427 | fpath = sys.argv[1] 428 | import platform 429 | if platform.system() == "Windows": 430 | fpath = fpath.decode("gbk") 431 | else: 432 | fpath = fpath.decode("utf8") 433 | try: 434 | alias_raw, g_alias_d, g_alias_deps = \ 435 | alias.parse(fpath, sys.argv[2]) 436 | if len(alias_raw) > 0: 437 | output(lseri._tolua({"alias_fields":alias_raw})) 438 | if len(g_alias_deps) > 0: 439 | output(lseri._tolua({"alias_deps":g_alias_deps})) 440 | g_struct_d = typedef.parse(os.path.join(fpath, "struct.yaml")) 441 | g_struct_deps = typedef.parse_deps(os.path.join(sys.argv[2], "struct_deps.yaml"), g_struct_d) 442 | if len(g_struct_deps) > 0: 443 | output(lseri._tolua({"struct_deps":g_struct_deps})) 444 | except Exception, e: 445 | error(str(e)) 446 | run_dir(fpath) 447 | flush_output() 448 | -------------------------------------------------------------------------------- /converter/main.lua: -------------------------------------------------------------------------------- 1 | local lua_path = os.getenv("LUA_PATH") 2 | if lua_path then 3 | package.path = string.format("%s;%s", package.path, lua_path) 4 | end 5 | local lua_cpath = os.getenv("LUA_CPATH") 6 | if lua_cpath then 7 | package.cpath = string.format("%s;%s", package.cpath, lua_cpath) 8 | end 9 | 10 | local DataDump = require "dumper" 11 | local sformat = string.format 12 | 13 | local cfg_fn, doc_dir, alias_dir, script_dir, outdir, json_outdir= ... 14 | json_outdir = json_outdir or (outdir and outdir.."_json") 15 | local cfg_env = {} 16 | print(cfg_fn) 17 | local cfg_f = loadfile(cfg_fn, "t", cfg_env) 18 | cfg_f() 19 | local export_cfg = cfg_env.export 20 | local global = {raw = {}, save = {}, cfg_env = cfg_env} 21 | local copy = {} 22 | local exts = {} 23 | local save= global.save 24 | local alias_deps = {} 25 | local struct_deps = {} 26 | local alias_fields = {} 27 | local sheet_struct_deps = {} 28 | local save_check_struct_deps = {} 29 | local cfg_exts = {} 30 | 31 | function tprint(t) 32 | local s = DataDump(t) 33 | print(s:sub(8, #s)) 34 | end 35 | 36 | local function info(...) 37 | print(...) 38 | end 39 | 40 | function error(msg) 41 | info("ERROR: "..msg) 42 | os.exit(1) 43 | end 44 | 45 | assert = function(cond, msg) 46 | if not cond then 47 | error(msg) 48 | end 49 | return cond 50 | end 51 | 52 | local function _get_or_create_key(d, key) 53 | local ret = d[key] 54 | if not ret then 55 | ret = {} 56 | d[key] = ret 57 | end 58 | return ret 59 | end 60 | 61 | local function _merge_hash(d1, d2, msg) 62 | msg = msg or "重复的key" 63 | for k, v in pairs(d2) do 64 | assert(d1[k] == nil, sformat("%s:<%s>", msg, v)) 65 | d1[k] = v 66 | end 67 | end 68 | 69 | local function _merge_array(l1, l2) 70 | for _, v in ipairs(l2) do 71 | table.insert(l1, v) 72 | end 73 | end 74 | 75 | -- py convert 76 | info("*****************python convert******************") 77 | local root_path = os.getenv("BIN_ROOT") or "./exporter" 78 | local fp = io.popen(sformat("python %s/xlsparse.py %s %s", root_path, doc_dir, alias_dir)) 79 | while true do 80 | local data = fp:read("*a") 81 | if not data then 82 | break 83 | end 84 | local f = load("return "..data, "=(load)", "t") 85 | local ok, all_data = pcall(f) 86 | if not ok or not all_data then 87 | info(ok, all_data) 88 | error("py convert error") 89 | end 90 | 91 | if all_data.error then 92 | error(all_data.error) 93 | end 94 | for _, ret in ipairs(all_data) do 95 | local fn = ret.filename 96 | if fn then 97 | -- global.raw[fn] = ret.data 98 | copy[fn] = ret.data 99 | exts[fn] = ret.ext 100 | sheet_struct_deps[fn] = ret.struct_deps 101 | else 102 | if ret.alias_deps then 103 | alias_deps = ret.alias_deps 104 | end 105 | if ret.struct_deps then 106 | struct_deps = ret.struct_deps 107 | end 108 | if ret.alias_fields then 109 | for name, cfg in pairs(ret.alias_fields) do 110 | local d = {} 111 | for field, alias in pairs(cfg) do 112 | d[alias] = field 113 | end 114 | alias_fields[name] = d 115 | end 116 | end 117 | end 118 | end 119 | break 120 | end 121 | fp:close() 122 | if not next(copy) then 123 | error("no xls file convert!") 124 | end 125 | 126 | -- lua prepare 127 | info("*****************lua prepare******************") 128 | local key_alias = {} 129 | local cfg_keys = {} 130 | 131 | local save_name_d = {} 132 | local merge_name_d = {} 133 | for cfg_idx, entry in ipairs(export_cfg) do 134 | local fn = entry[1] 135 | local snames = entry[2] 136 | local script = entry[3] 137 | local save_name = entry[4] 138 | info(string.format("lua prepare: %s < %s", save_name, fn)) 139 | assert(merge_name_d[save_name] == nil, 140 | "输出名字重复:"..save_name) 141 | save_name_d[save_name] = true 142 | local ext_d = entry[5] 143 | if next(ext_d) then 144 | cfg_exts[save_name] = ext_d 145 | local merge_name = ext_d.merge 146 | if merge_name then 147 | assert(save_name_d[merge_name] == nil, "merge name 重复") 148 | merge_name_d[merge_name] = true 149 | end 150 | end 151 | 152 | if type(snames) == "string" then 153 | if snames == "*" then 154 | snames = {} 155 | for k, v in pairs(copy[fn]) do 156 | table.insert(snames, k) 157 | end 158 | else 159 | snames = {snames} 160 | end 161 | elseif snames == nil then 162 | error("sheet名称配置不能为nil") 163 | snames = {2015} 164 | end 165 | entry[2] = snames 166 | 167 | for _, i in ipairs(snames) do 168 | -- info(sformat("lua prepare: %s-%s", fn, i)) 169 | local sheet = copy[fn] 170 | assert(sheet, "文件不存在!"..fn) 171 | local ext = exts[fn] 172 | if i ~= 2015 then 173 | sheet = sheet[i] 174 | assert(sheet, sformat("file:<%s> has no sheet:<%s>", fn, i)) 175 | ext = ext[i] 176 | if ext.deps then 177 | local kd = _get_or_create_key(save_check_struct_deps, save_name) 178 | for k, v in pairs(ext.deps) do 179 | kd[k] = kd[k] or v 180 | end 181 | end 182 | if ext.key_alias then 183 | local kd = _get_or_create_key(key_alias, save_name) 184 | _merge_hash(kd, ext.key_alias, "重复的key_alias") 185 | end 186 | if ext.typ == "d" then 187 | local ckd = _get_or_create_key(cfg_keys, save_name) 188 | for k, v in pairs(sheet) do 189 | assert(not ckd[k], sformat("重复的key:<%s>", k)) 190 | ckd[k] = k 191 | end 192 | end 193 | if ext.key_check then 194 | local ckd = _get_or_create_key(cfg_keys, save_name) 195 | _merge_hash(ckd, ext.key_check, "重复的key") 196 | end 197 | else 198 | -- 整个文件导出不处理ext 199 | assert(not pre, "whole workbook export save name dup!") 200 | end 201 | end 202 | end 203 | 204 | -- check deps cfgname 205 | for k, _ in pairs(alias_deps) do 206 | assert(save_name_d[k], sformat("deps所在的别名定义配置名称必须在config的导出名字中:<%s>", k)) 207 | end 208 | global.alias2key = key_alias 209 | 210 | -- check and convert depends 211 | local check = cfg_keys 212 | local convert = key_alias 213 | 214 | local log_alias = nil 215 | local log_idx = nil 216 | local log_field = nil 217 | local raw_assert = assert 218 | assert = function(v, msg) 219 | local field = log_alias and log_alias[log_field] or log_field 220 | return raw_assert(v, ("check depends error, idx:<%s>, field:<%s> %s"):format(log_idx, field, msg)) 221 | end 222 | 223 | local function _conv(check, convert, i, msg) 224 | assert(check, sformat("%s %s", msg, "依赖的表不存在,应该填写config中的存盘名字")) 225 | if type(i) == "number" then 226 | if i == 0 then 227 | return 0 228 | end 229 | assert(check[i], msg) 230 | return i 231 | elseif type(i) == "string" then 232 | if i == "" then 233 | if convert and type(({next(convert)})[2]) == "number" then 234 | return 0 235 | else 236 | return "" 237 | end 238 | end 239 | -- 兼容旧的写法 240 | -- not convert[i] 因为索引的是同一个table,可能已经被convert过了。 241 | if not convert or not convert[i] then 242 | return assert(check[i], msg) 243 | end 244 | return assert(convert[i], msg) 245 | end 246 | error(msg) 247 | end 248 | 249 | local function _make_check_f(deps) 250 | -- 简单类型或者列表类型 251 | if type(deps) ~= "table" then 252 | local src = check[deps] 253 | local dst = convert[deps] 254 | return function(ckd) 255 | if type(ckd) == "table" then 256 | local ret = {} 257 | for n, i in ipairs(ckd) do 258 | i = _conv(src, dst, i, sformat("list pos:<%d> value:<%s>", n, i)) 259 | table.insert(ret, i) 260 | end 261 | return ret 262 | else 263 | local tmp = _conv(src, dst, ckd, "simple error: "..(ckd or "nil")) 264 | return tmp 265 | end 266 | end 267 | -- 字典类型 268 | elseif deps.key or deps.value then 269 | local k_src, k_dst, v_src, v_dst 270 | if deps.key then 271 | k_src = check[deps.key] 272 | k_dst = convert[deps.key] 273 | end 274 | if deps.value then 275 | v_src = check[deps.value] 276 | v_dst = convert[deps.value] 277 | end 278 | return function(ckd) 279 | local ret = {} 280 | for k, v in pairs(ckd) do 281 | if deps.key then 282 | k = _conv(k_src, k_dst, k, "key error: "..k) 283 | end 284 | if deps.value then 285 | v = _conv(v_src, v_dst, v, "value error: "..v) 286 | end 287 | ret[k] = v 288 | end 289 | return ret 290 | end 291 | -- 指定列表位置 292 | else 293 | local check_d = {} 294 | for i, v in pairs(deps) do 295 | check_d[i] = {check[v], convert[v]} 296 | end 297 | return function(ckd) 298 | local ret = {} 299 | for i, v in ipairs(ckd) do 300 | local cd = check_d[i] 301 | if d then 302 | v = _conv(cd[1], cd[2], v, ("list pos:<%d> value:<%s>"):format(i, v)) 303 | end 304 | table.insert(ret, v) 305 | end 306 | return ret 307 | end 308 | end 309 | end 310 | 311 | local check_conf = {} 312 | for name, cfg in pairs(alias_deps) do 313 | local d = {} 314 | for field, deps in pairs(cfg) do 315 | d[field] = _make_check_f(deps) 316 | end 317 | check_conf[name] = d 318 | end 319 | 320 | local function _check_struct_value_deps(value, cfg) 321 | for k, v in pairs(cfg) do 322 | local src = check[v] 323 | local dst = convert[v] 324 | value[k] = _conv(src, dst, value[k], "struct error: "..value[k]) 325 | end 326 | end 327 | 328 | local function _check_alias_deps(sheet, check_fs) 329 | for idx, entry in pairs(sheet) do 330 | log_idx = idx 331 | for field, check_f in pairs(check_fs) do 332 | log_field = field 333 | if entry[field] then 334 | entry[field] = check_f(entry[field]) 335 | end 336 | end 337 | end 338 | end 339 | 340 | local function _check_struct_deps(sheet, deps_cfg) 341 | for idx, entry in pairs(sheet) do 342 | log_idx = idx 343 | for field, field_cfg in pairs(deps_cfg) do 344 | log_field = field 345 | local value = entry[field] 346 | if value then 347 | local flag, cfg = field_cfg[1], field_cfg[2] 348 | if flag == "s" then 349 | _check_struct_value_deps(value, cfg) 350 | elseif flag == "l" then 351 | for _, v in ipairs(value) do 352 | _check_struct_value_deps(v, cfg) 353 | end 354 | elseif flag == "d" then 355 | for _, v in pairs(value) do 356 | _check_struct_value_deps(v, cfg) 357 | end 358 | end 359 | end 360 | end 361 | end 362 | end 363 | 364 | 365 | info("*****************check and convert depends******************") 366 | for _, v in ipairs(export_cfg) do 367 | local save_name = v[4] 368 | local check_fs = check_conf[save_name] 369 | local struct_check_fs = save_check_struct_deps[save_name] 370 | log_alias = alias_fields[save_name] 371 | local fn = v[1] 372 | local snames = v[2] 373 | if #snames == 1 and snames[1] == 2015 then 374 | snames = {} 375 | end 376 | for _, sn in ipairs(snames) do 377 | local sheet = copy[fn][sn] 378 | info(("check depends, file:<%s>, sheet:<%s>"):format(fn, sn)) 379 | if check_fs then 380 | _check_alias_deps(sheet, check_fs) 381 | end 382 | if struct_check_fs then 383 | _check_struct_deps(sheet, struct_check_fs) 384 | end 385 | end 386 | end 387 | 388 | assert = raw_assert 389 | 390 | -- tprint(copy) 391 | 392 | -- lua convert 393 | info("*****************lua convert******************") 394 | local last_save_type = {} 395 | local post_convert_funcs = {} 396 | local post_convert_names = {} 397 | for cfg_idx, entry in ipairs(export_cfg) do 398 | local fn = entry[1] 399 | local snames = entry[2] 400 | local script = entry[3] 401 | local save_name = entry[4] 402 | 403 | if #snames == 1 and snames[1] == 2015 then 404 | snames = {} 405 | end 406 | local mod = setmetatable({}, {__index = _ENV}) 407 | local convert_f = nil 408 | if script ~= nil then 409 | local f, msg = loadfile(sformat("%s/%s", script_dir, script), "t", mod) 410 | assert(f, sformat("load script error, cfg idx:<%d>, script:<%s>\n%s", cfg_idx, script, msg)) 411 | local success, msg = xpcall(f, debug.traceback) 412 | assert(success, sformat("run script error, cfg idx:<%d>, script:<%s>\n%s", cfg_idx, script, msg)) 413 | --convert_f = assert(mod.convert, sformat("no convert func, cfg idx:<%d>, script:<%s>", cfg_idx, script)) 414 | convert_f = mod.convert 415 | end 416 | for _, i in ipairs(snames) do 417 | info(sformat("lua convert: %s-%s", fn, i)) 418 | local pre = save[save_name] 419 | local sheet = copy[fn] 420 | assert(sheet, "文件不存在!"..fn) 421 | local ext = exts[fn] 422 | if i ~= 2015 then 423 | sheet = sheet[i] 424 | assert(sheet, sformat("file:<%s> has no sheet:<%s>", fn, i)) 425 | ext = ext[i] 426 | else 427 | assert(not pre, "whole workbook export save name dup!") 428 | end 429 | local t 430 | if convert_f then 431 | ext_data = { 432 | filename = fn, 433 | sheetname = i, 434 | } 435 | t = convert_f(sheet, global, ext_data) 436 | else 437 | t = sheet 438 | end 439 | if pre then 440 | assert(last_save_type[save_name] == ext.typ, "多表页类型不匹配") 441 | if ext.typ == "d" then 442 | _merge_hash(pre, t) 443 | elseif ext.typ == "l" then 444 | _merge_array(pre, t) 445 | end 446 | else 447 | save[save_name] = t 448 | last_save_type[save_name] = ext.typ 449 | end 450 | end 451 | if mod.post_convert then 452 | local d = _get_or_create_key(post_convert_names, save_name) 453 | if not d[script] then 454 | d[script] = true 455 | local sheets = string.format("save:<%s> file:<%s> sheets:<%s>", save_name, fn, table.concat(snames, ",")) 456 | table.insert(post_convert_funcs, {mod.post_convert, save_name, sheets}) 457 | end 458 | end 459 | end 460 | 461 | -- post convert 462 | if #post_convert_funcs > 0 then 463 | info("*****************lua post convert******************") 464 | end 465 | for _, v in ipairs(post_convert_funcs) do 466 | info("lua post convert: ", v[3]) 467 | save[v[2]] = v[1](save[v[2]], global) 468 | end 469 | 470 | -- ext process 471 | local merge = {} 472 | local no_save = {} 473 | for k, v in pairs(cfg_exts) do 474 | if v.merge then 475 | -- 可以配置成weapon.attr这种 476 | local it = string.gmatch(v.merge, "[^.]+") 477 | local to = it() 478 | local alias = it() 479 | assert(it() == nil, sformat("export name:<%s> merge format error:<%s>", k, v.merge)) 480 | local d = _get_or_create_key(merge, to) 481 | alias = alias or k 482 | table.insert(d, {raw = k, alias = alias}) 483 | end 484 | if v.no_save then 485 | table.insert(no_save, k) 486 | end 487 | end 488 | for _, i in ipairs(no_save) do 489 | save[i] = nil 490 | end 491 | for k, v in pairs(merge) do 492 | for _, i in ipairs(v) do 493 | local d = _get_or_create_key(save, k) 494 | d[i.alias] = save[i.raw] 495 | save[i.raw] = nil 496 | end 497 | end 498 | 499 | -- post process script 500 | if cfg_env.post_convert_script then 501 | info("***************run post convert script******************") 502 | local env = setmetatable({}, {__index = _ENV}) 503 | local f, msg = loadfile(sformat("%s/%s", script_dir, cfg_env.post_convert_script), "t", env) 504 | assert(f, msg) 505 | local ok, msg = xpcall(f, debug.traceback) 506 | assert(ok, "post convert script call") 507 | assert(env.run, "post convert script has no run function") 508 | env.run(global) 509 | end 510 | 511 | if not outdir then 512 | info("check success!") 513 | os.exit(0) 514 | end 515 | -- tprint(save) 516 | 517 | info("*****************save lua file******************") 518 | local suffix = cfg_env.save_suffix or "" 519 | if cfg_env.all_in_one then 520 | local one_file = sformat("%s/%s", outdir, cfg_env.all_in_one) 521 | local fp = io.open(one_file, "w") 522 | local s = DataDump(save) 523 | fp:write(s) 524 | fp:close() 525 | else 526 | for k, v in pairs(save) do 527 | local s = DataDump(v) 528 | --local fp = io.open(sformat("%s/%s", cfg_env.output_dir, k), "w") 529 | local fp = io.open(sformat("%s/%s%s", outdir, k, suffix), "w") 530 | fp:write(s) 531 | fp:close() 532 | info("save file: "..k) 533 | end 534 | end 535 | 536 | local to_json_list = cfg_env.to_json_list 537 | if to_json_list and #to_json_list > 0 then 538 | info("*****************save json file******************") 539 | local Json = require "json" 540 | for _, i in ipairs(to_json_list) do 541 | assert(save[i], sformat("要保存json的数据不存在:<%s>", i)) 542 | local fp = io.open(sformat("%s/%s.json", json_outdir, i), "w") 543 | local s = Json.encode(save[i], {keyorder = {}}) 544 | fp:write(s) 545 | fp:close() 546 | end 547 | end 548 | info("xls convert success!") 549 | -------------------------------------------------------------------------------- /converter/json.lua: -------------------------------------------------------------------------------- 1 | -- Module options: 2 | local always_try_using_lpeg = true 3 | local register_global_module_table = false 4 | local global_module_name = 'json' 5 | 6 | --[==[ 7 | 8 | David Kolf's JSON module for Lua 5.1/5.2 9 | ======================================== 10 | 11 | *Version 2.4* 12 | 13 | In the default configuration this module writes no global values, not even 14 | the module table. Import it using 15 | 16 | json = require ("dkjson") 17 | 18 | In environments where `require` or a similiar function are not available 19 | and you cannot receive the return value of the module, you can set the 20 | option `register_global_module_table` to `true`. The module table will 21 | then be saved in the global variable with the name given by the option 22 | `global_module_name`. 23 | 24 | Exported functions and values: 25 | 26 | `json.encode (object [, state])` 27 | -------------------------------- 28 | 29 | Create a string representing the object. `Object` can be a table, 30 | a string, a number, a boolean, `nil`, `json.null` or any object with 31 | a function `__tojson` in its metatable. A table can only use strings 32 | and numbers as keys and its values have to be valid objects as 33 | well. It raises an error for any invalid data types or reference 34 | cycles. 35 | 36 | `state` is an optional table with the following fields: 37 | 38 | - `indent` 39 | When `indent` (a boolean) is set, the created string will contain 40 | newlines and indentations. Otherwise it will be one long line. 41 | - `keyorder` 42 | `keyorder` is an array to specify the ordering of keys in the 43 | encoded output. If an object has keys which are not in this array 44 | they are written after the sorted keys. 45 | - `level` 46 | This is the initial level of indentation used when `indent` is 47 | set. For each level two spaces are added. When absent it is set 48 | to 0. 49 | - `buffer` 50 | `buffer` is an array to store the strings for the result so they 51 | can be concatenated at once. When it isn't given, the encode 52 | function will create it temporary and will return the 53 | concatenated result. 54 | - `bufferlen` 55 | When `bufferlen` is set, it has to be the index of the last 56 | element of `buffer`. 57 | - `tables` 58 | `tables` is a set to detect reference cycles. It is created 59 | temporary when absent. Every table that is currently processed 60 | is used as key, the value is `true`. 61 | 62 | When `state.buffer` was set, the return value will be `true` on 63 | success. Without `state.buffer` the return value will be a string. 64 | 65 | `json.decode (string [, position [, null]])` 66 | -------------------------------------------- 67 | 68 | Decode `string` starting at `position` or at 1 if `position` was 69 | omitted. 70 | 71 | `null` is an optional value to be returned for null values. The 72 | default is `nil`, but you could set it to `json.null` or any other 73 | value. 74 | 75 | The return values are the object or `nil`, the position of the next 76 | character that doesn't belong to the object, and in case of errors 77 | an error message. 78 | 79 | Two metatables are created. Every array or object that is decoded gets 80 | a metatable with the `__jsontype` field set to either `array` or 81 | `object`. If you want to provide your own metatables use the syntax 82 | 83 | json.decode (string, position, null, objectmeta, arraymeta) 84 | 85 | To prevent the assigning of metatables pass `nil`: 86 | 87 | json.decode (string, position, null, nil) 88 | 89 | `.__jsonorder` 90 | ------------------------- 91 | 92 | `__jsonorder` can overwrite the `keyorder` for a specific table. 93 | 94 | `.__jsontype` 95 | ------------------------ 96 | 97 | `__jsontype` can be either `"array"` or `"object"`. This value is only 98 | checked for empty tables. (The default for empty tables is `"array"`). 99 | 100 | `.__tojson (self, state)` 101 | ------------------------------------ 102 | 103 | You can provide your own `__tojson` function in a metatable. In this 104 | function you can either add directly to the buffer and return true, 105 | or you can return a string. On errors nil and a message should be 106 | returned. 107 | 108 | `json.null` 109 | ----------- 110 | 111 | You can use this value for setting explicit `null` values. 112 | 113 | `json.version` 114 | -------------- 115 | 116 | Set to `"dkjson 2.4"`. 117 | 118 | `json.quotestring (string)` 119 | --------------------------- 120 | 121 | Quote a UTF-8 string and escape critical characters using JSON 122 | escape sequences. This function is only necessary when you build 123 | your own `__tojson` functions. 124 | 125 | `json.addnewline (state)` 126 | ------------------------- 127 | 128 | When `state.indent` is set, add a newline to `state.buffer` and spaces 129 | according to `state.level`. 130 | 131 | LPeg support 132 | ------------ 133 | 134 | When the local configuration variable `always_try_using_lpeg` is set, 135 | this module tries to load LPeg to replace the `decode` function. The 136 | speed increase is significant. You can get the LPeg module at 137 | . 138 | When LPeg couldn't be loaded, the pure Lua functions stay active. 139 | 140 | In case you don't want this module to require LPeg on its own, 141 | disable the option `always_try_using_lpeg` in the options section at 142 | the top of the module. 143 | 144 | In this case you can later load LPeg support using 145 | 146 | ### `json.use_lpeg ()` 147 | 148 | Require the LPeg module and replace the functions `quotestring` and 149 | and `decode` with functions that use LPeg patterns. 150 | This function returns the module table, so you can load the module 151 | using: 152 | 153 | json = require "dkjson".use_lpeg() 154 | 155 | Alternatively you can use `pcall` so the JSON module still works when 156 | LPeg isn't found. 157 | 158 | json = require "dkjson" 159 | pcall (json.use_lpeg) 160 | 161 | ### `json.using_lpeg` 162 | 163 | This variable is set to `true` when LPeg was loaded successfully. 164 | 165 | --------------------------------------------------------------------- 166 | 167 | Contact 168 | ------- 169 | 170 | You can contact the author by sending an e-mail to 'david' at the 171 | domain 'dkolf.de'. 172 | 173 | --------------------------------------------------------------------- 174 | 175 | *Copyright (C) 2010-2013 David Heiko Kolf* 176 | 177 | Permission is hereby granted, free of charge, to any person obtaining 178 | a copy of this software and associated documentation files (the 179 | "Software"), to deal in the Software without restriction, including 180 | without limitation the rights to use, copy, modify, merge, publish, 181 | distribute, sublicense, and/or sell copies of the Software, and to 182 | permit persons to whom the Software is furnished to do so, subject to 183 | the following conditions: 184 | 185 | The above copyright notice and this permission notice shall be 186 | included in all copies or substantial portions of the Software. 187 | 188 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 189 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 190 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 191 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 192 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 193 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 194 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 195 | SOFTWARE. 196 | 197 | 202 | 203 | 863 | --------------------------------------------------------------------------------