1. 程式人生 > >python + openpyxl + Jinja2(解析excel生成javaBean和json)

python + openpyxl + Jinja2(解析excel生成javaBean和json)

模版

package {{ package }};

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import
java.util.Map.Entry; import com.mongodb.BasicDBObject; import com.lehoo.util.io.resource.ResourceListener; import com.lehoo.util.io.resource.ResourceManager; import mmorpg.server.util.log.Logger; import mmorpg.server.util.log.Logger.LoggerSystem; import mmorpg.server.game.common.JSONListener; import
java.util.Collections; import java.util.Comparator; import java.lang.reflect.Field; import java.lang.reflect.Method; {% if superClass != "" %} import com.lehoo.sob.confsuper.{{ superClass }}; {% endif %} /** * excel|{{ excelName }} * @author administrator * 此類是系統自動生成類 不要直接修改,修改後也會被覆蓋 */ @JSONListener
{% if superClass == "" %}public class Conf{{ sheetName }} { {% else %} public class Conf{{ sheetName }} extends {{ superClass }} { {% endif %} /** 對應的資料檔案 */ private static final String JSON_NAME = "Conf{{ sheetName }}.json"; /**索引*/ private static final String [] INDEXS = {{ indexs }}; {% for prop in fileds %}/** {{ prop.note }} */ private {{ prop.type }} {{ prop.name }}; {% endfor %} /** 配置資料 */ private static Map<Object,Conf{{ sheetName }}> datas = new LinkedHashMap<>(); /**索引結構,加快查詢速度*/ private static Map<String,Map<Object,List<Conf{{ sheetName }}>>> indexs = new HashMap<>(); /** 私有建構函式 */ private Conf{{ sheetName }}(){ } /**初始化索引*/ private static void initIndex(){ //初始化索引結構 for(String index : INDEXS){ Map<Object,List<Conf{{ sheetName }}>> map = new HashMap<>(); indexs.put(index, map); } } /** * 載入資料,並註冊監聽 * @param file */ public static void load(String path) { final File file = new File(path + JSON_NAME); ResourceListener listener = new ResourceListener() { @Override public File listenedFile() { return file; } @Override public void onResourceChange(File file) { try { datas.clear(); indexs.clear(); initIndex(); BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file),"UTF-8")); String line = null; while ((line = reader.readLine()) != null) { BasicDBObject jsonObj = BasicDBObject.parse(line); Conf{{ sheetName }} conf{{ sheetName }} = new Conf{{ sheetName }}(); {% for prop in fileds %}{% if prop.type == "short" %} conf{{ sheetName }}.{{ prop.name }} = (short)jsonObj.getInt("{{ prop.name }}"); {% elif prop.type == "byte" %} conf{{ sheetName }}.{{ prop.name }} = (byte)jsonObj.getInt("{{ prop.name }}"); {% elif prop.type == "Date" %} conf{{ sheetName }}.{{ prop.name }} = DateUtil.parseDataTime(jsonObj.getString("{{ prop.name }}")); {% elif prop.type == "float" %} conf{{ sheetName }}.{{ prop.name }} = (float)jsonObj.getDouble("{{ prop.name }}"); {% else %} conf{{ sheetName }}.{{ prop.name }} = jsonObj.get{{ prop.utype }}("{{ prop.name }}");{% endif %}{% endfor %} datas.put(jsonObj.getString("sn"), conf{{ sheetName }}); //處理索引結構 for(String index : INDEXS){ Map<Object, List<Conf{{ sheetName }}>> indexMap = indexs.get(index); Object indexValue = conf{{ sheetName }}.getFieldValue(index); List<Conf{{ sheetName }}> list = indexMap.get(indexValue); if(list == null){ list = new ArrayList<Conf{{ sheetName }}>(); indexMap.put(indexValue, list); } list.add(conf{{ sheetName }}); } } reader.close(); // 此json檔案載入完成後,載入特定manager單例類的指定方法 ConfJSONLoad confJSONLoad = ConfJSONLoad.findBy(toString()); if (confJSONLoad != null) { Class<?> forName = Class.forName(confJSONLoad.getClassName().replaceAll("/", ".")); // getInstance方法獲取單例項 Method declaredMethod = forName.getDeclaredMethod("getInstance", new Class[]{}); Object obj = declaredMethod.invoke(null, new Object[]{}); // 執行指定方法 Method executeMethod = forName.getDeclaredMethod(confJSONLoad.getMethod(), new Class[]{}); executeMethod.setAccessible(true); executeMethod.invoke(obj, new Object[]{}); } Logger.info(Logger.LoggerSystem.LOADING, "==============載入"+file.getName()+"=============="); } catch (Exception e) { Logger.error(LoggerSystem.LOADING, e, "載入配置檔案失敗", file.getName()); } } @Override public String toString() { return "Conf{{ sheetName }}"; } }; listener.onResourceChange(file); ResourceManager.getInstance().registerResourceListener(listener); } /** * 根據主鍵獲得資料 * @param sn * @return */ public static Conf{{ sheetName }} getSn(String sn) { return datas.get(sn); } /** * 清除所有資料 * @return */ public static void clearAll() { datas.clear(); indexs.clear(); } /** * 資料大小 * @return */ public static int size(){ return datas.size(); } /** * 獲得所有資料 * @return */ public static Collection<Conf{{ sheetName }}> findAll() { Collection<Conf{{ sheetName }}> values = datas.values(); List<Conf{{ sheetName }}> result = new ArrayList<>(0); result.addAll(values); // 對結果進行排序,排序的規則是看本類中是否存在order欄位 // 如果有order欄位,那麼就按照此欄位排序,如果沒有則認為不需要進行排序。 try { final Field oderFiled = Conf{{ sheetName }}.class.getDeclaredField("order"); Collections.sort(result, new Comparator<Conf{{ sheetName }}>() { @Override public int compare(Conf{{ sheetName }} o1, Conf{{ sheetName }} o2) { oderFiled.setAccessible(true); try { int value1 = (int) oderFiled.get(o1); int value2 = (int) oderFiled.get(o2); return value1 - value2; } catch (Exception e) { e.printStackTrace(); } return 0; } }); } catch(NoSuchFieldException e) { // 沒有order 屬性不進行排序 } return result; } /** * 根據條件獲得單條資料 * @param params * @return */ public static Conf{{ sheetName }} findBy(Object object) { return datas.containsKey(object) ? datas.get(object) : null; } /** * 根據條件獲得多條資料 * @param params * @return */ public static Collection<Conf{{ sheetName }}> findBy(Object ... params) { return utilBase(params); } /** * 根據條件獲得一條資料 * @param params * @return */ public static Conf{{ sheetName }} getBy(Object ... params) { List<Conf{{ sheetName }}> utilBase = utilBase(params); if (utilBase.size() > 0) { return utilBase.get(0); } return null; } /** * 根據條件獲得第一條資料 * @param params * @return */ public static Conf{{ sheetName }} findFirst(Object ... params){ List<Conf{{ sheetName }}> result = utilBase(params); if(result.size() == 0){ return null; } return result.get(0); } /** * 根據條件獲得最後一條資料 * @param params * @return */ public static Conf{{ sheetName }} findLast(Object ... params){ List<Conf{{ sheetName }}> result = utilBase(params); if(result.size() == 0){ return null; } return result.get(result.size() - 1); } /** * 通過屬性獲取資料集合 支援排序 * @param params * @return */ public static List<Conf{{ sheetName }}> utilBase(Object...params) { List<Object> settings = new ArrayList<>(0); for (Object obj : params) { settings.add(obj); } // 查詢引數 final Map<String, Object> paramsFilter = new LinkedHashMap<>(0); //過濾條件 // 引數數量 int len = settings.size(); // 引數必須成對出現 if (len % 2 != 0) { String param = ""; for (Object p : params) { param += p + ","; } throw new RuntimeException("查詢引數必須成對出現:query={" + param +"}"); } // 處理成對引數 for (int i = 0; i < len; i += 2) { String key = (String)settings.get(i); Object val = settings.get(i + 1); // 引數 paramsFilter.put(key, val); } // 返回結果 List<Conf{{ sheetName }}> result = null; try { result = utilBase(paramsFilter); // 對結果進行排序,排序的規則是看本類中是否存在order欄位 // 如果有order欄位,那麼就按照此欄位排序,如果沒有則認為不需要進行排序。 try { final Field oderFiled = Conf{{ sheetName }}.class.getDeclaredField("order"); Collections.sort(result, new Comparator<Conf{{ sheetName }}>() { @Override public int compare(Conf{{ sheetName }} o1, Conf{{ sheetName }} o2) { oderFiled.setAccessible(true); try { int value1 = (int) oderFiled.get(o1); int value2 = (int) oderFiled.get(o2); return value1 - value2; } catch (Exception e) { e.printStackTrace(); } return 0; } }); } catch(NoSuchFieldException e) { // 沒有order 屬性不進行排序 } } catch (Exception e) { throw new RuntimeException(e); } // 對結果進行排序 return result; } /** * 查詢匹配的結果 * @param paramsFilter * @return */ private static List<Conf{{ sheetName }}> utilBase(Map<String, Object> paramsFilter){ List<String> indexHit = new ArrayList<>(); for(String index : INDEXS){ if(paramsFilter.containsKey(index)){ indexHit.add(index); } } //先找到索引命中的結果 List<Conf{{ sheetName }}> hitResult = null; if(indexHit.size() > 0){ for(String hit : indexHit){ Map<Object, List<Conf{{ sheetName }}>> map = indexs.get(hit); List<Conf{{ sheetName }}> list = map.get(paramsFilter.get(hit)); if(hitResult == null){ hitResult = list; }else{ hitResult.retainAll(list);//求交集 } paramsFilter.remove(hit); } } if(hitResult == null){ hitResult = new ArrayList<>(); } // 返回結果 if(paramsFilter.size() == 0){ return hitResult; } Collection<Conf{{ sheetName }}> loopCollections = null; if(hitResult.size() == 0){ loopCollections = datas.values(); }else{ loopCollections = hitResult; } List<Conf{{ sheetName }}> result = new ArrayList<>(0); // 通過條件獲取結果 for (Conf{{ sheetName }} c : loopCollections) { // 本行資料是否符合過濾條件 boolean bingo = true; // 判斷過濾條件 for (Entry<String, Object> p : paramsFilter.entrySet()) { // 實際結果 Object valTrue = c.getFieldValue(p.getKey()); // 期望結果 Object valWish = p.getValue(); // 有不符合過濾條件的 if (!valWish.toString().equals(valTrue.toString())) { bingo = false; break; } } // 記錄符合結果 if (bingo) { result.add(c); } } return result; } /** * 獲得欄位值 * @param key * @return */ private Object getFieldValue(String key) { Object value = null; switch (key) { {% for prop in fileds %} case "{{ prop.name }}": value = this.{{ prop.name }}; break; {% endfor %} default: break; } return value; } {% for prop in fileds %} /** *獲得{{ prop.note }} */ public {{ prop.type }} get{{ prop.uname }}() { return {{ prop.name }}; } {% endfor %} /** * byte 型別 * @param fieldName * @return */ public byte getByteValue(String fieldName) { return Byte.parseByte(getFieldValue(fieldName) + ""); } /** * short 型別 * @param fieldName * @return */ public short getShortValue(String fieldName) { return Short.parseShort(getFieldValue(fieldName) + ""); } /** * int 型別 * @param fieldName * @return */ public int getIntValue(String fieldName) { return Integer.parseInt(getFieldValue(fieldName) + ""); } /** * long 型別 * @param fieldName * @return */ public long getLongValue(String fieldName) { return Long.parseLong(getFieldValue(fieldName) + ""); } /** * boolean 型別 * @param fieldName * @return */ public boolean getBooleanValue(String fieldName) { return Boolean.parseBoolean(getFieldValue(fieldName) + ""); } /** * float 型別 * @param fieldName * @return */ public float getFloatValue(String fieldName) { return Float.parseFloat(getFieldValue(fieldName) + ""); } /** * double 型別 * @param fieldName * @return */ public double getDoubleValue(String fieldName) { return Double.parseDouble(getFieldValue(fieldName) + ""); } /** * String 型別 * @param fieldName * @return */ public String getStringValue(String fieldName) { return getFieldValue(fieldName) + ""; } /** * 獲得資料集中的第一條資料 * @return */ public static Conf{{ sheetName }} getFirst() { Collection<Conf{{ sheetName }}> all = findAll(); List<Conf{{ sheetName }}> result = new ArrayList<>(); result.addAll(all); return result.get(0); } /** * 獲得資料集中的最後一條資料 * @return */ public static Conf{{ sheetName }} getLast() { Collection<Conf{{ sheetName }}> all = findAll(); List<Conf{{ sheetName }}> result = new ArrayList<>(0); result.addAll(all); if (result.size() > 0) { return result.get(result.size() - 1); } return null; } /** * 資料欄位 * @author chuer */ public static final class K { {% for prop in fileds %} /**{{ prop.note }}*/ public static final String {{ prop.name }} = "{{ prop.name }}"; {% endfor %} } }

python程式碼

import jinja2
import openpyxl
import os
import sys
import json
import collections
import datetime


RESOURCE_PATH = "../resource"
TIME_FORMAT = "%Y-%m-%d %H:%M:%S"
PACKAGE = "mmorpg.server.game.common.conf"
JAVA_FILE_PATH = "../mmorpg/src/mmorpg/server/game/common/conf"

sheetNames = []
javaFiles = []
allFils = []


def allExcelFils(path):
    parents = os.listdir(path)
    for parent in parents:
        childPath = os.path.join(path,parent)
        if os.path.isdir(childPath):
            allExcelFils(childPath)
        elif childPath.endswith(".xlsx") and childPath.find("~") == -1:
            allFils.append(childPath)
        else:
            pass


def getRowContents(row):
    contents = []

    for cell in row:
        if cell.value == None:
            print("ERROR TITLE getRowContents:",row)
            sys.exit(0)
        contents.append(cell.value)
    return contents

def getNameAndIndexs(row):
    names = []
    indexs = []
    for cell in row:
        if cell.value == None:
            print("ERROR TITLE getNameAndIndexs:",row)
            sys.exit(0)
        pos = str(cell.value).find("-key")
        if pos != -1:
            names.append(cell.value[0:pos])
            indexs.append(cell.value[0:pos])
        else:
            names.append(cell.value)
    return names,indexs


def getIntValue(value):
    return int(value)

def getStrValue(value):
    if isinstance(value,datetime.datetime):
        return datetime.datetime.strftime(value,TIME_FORMAT)
    return str(value)

def getBoolValue(value):
    return bool(value)

def getFloatValue(value):
    return float(value)


def parseCellValue(type,value):
    if type in ("byte","short","int","long"):
        if value == None or value == "":
            return 0
        else:
            return getIntValue(value)
    elif type in ("float","double"):
        if value == None:
            return 0.0
        elif isinstance(value,str):
            value = value.replace(" ","")
        else:
            if value == "":
                return 0.0
            else:
                return getFloatValue(value)
    elif type == "String":
        if value == None:
            return ""
        else:
            return getStrValue(value)
    elif type == "boolean":
        if value == None or value == "":
            return False
        else:
            return getBoolValue(value)
    else:
        print("WARN:unkonw field type !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
        return value

def generateJSON(sheetName,names,types,rows):
    pos = sheetName.find("|")
    if pos != -1:
        sheetName = sheetName[0:pos]

    pos = sheetName.find("-")
    if pos != -1:
        sheetName = sheetName[0:pos]

    pathDir = RESOURCE_PATH+"/conf"
    if not os.path.exists(pathDir):
        os.makedirs(pathDir)

    filePah = pathDir+"/Conf"+sheetName+".json"
    print(filePah)
    file = open(filePah,"a+",encoding="utf8")
    for row in rows:
        cells = list(row)
        data = collections.OrderedDict()
        for index in range(len(names)):
            data[names[index]] = parseCellValue(types[index],cells[index].value)
        jsonData = json.dumps(data,ensure_ascii=False)
        file.write(jsonData+"\n")
    file.close()




"""
生成檔案內容
"""
def parseTemplate(templateFile,context):
    env = jinja2.Environment(loader=jinja2.FileSystemLoader("config"))
    template = env.get_template(templateFile)
    return template.render(context)


"""
生成java檔案
"""
def generateJAVA(excelPath,sheetName,names,noteds,indexs,types):
    superClassName = ""
    pos = sheetName.find("|")
    if pos != -1:
        superClassName = sheetName[pos+1:len(sheetName)]
        sheetName = sheetName[0:pos]

    pos = sheetName.find("-")
    if pos != -1:
        sheetName = sheetName[0:pos]

    if sheetName in javaFiles:
        return

    context = {}
    context["package"] = PACKAGE
    context["superClass"] = superClassName
    context["excelName"] = os.path.basename(excelPath)
    context["sheetName"] = sheetName

    indexStr = "{"
    for ind in range(len(indexs)):
        if ind == len(indexs) - 1:
            indexStr += "\""+indexs[ind]+"\""
        else:
            indexStr+= "\""+indexs[ind]+"\","
    indexStr += "}"
    context["indexs"] = indexStr
    fields = []
    for index in range(len(names)):
        data = {}
        data["name"] = names[index]
        data["uname"] = names[index][0].upper()+names[index][1:]
        data["note"] = noteds[index]
        data["type"] = types[index]
        data["utype"] = types[index][0].upper()+types[index][1:]
        fields.append(data)
    context["fileds"] = fields 
    javaClassContent = parseTemplate("template.html",context)
    filePah = JAVA_FILE_PATH+"/Conf"+sheetName+".java"
    print(filePah)
    file = open(filePah,"a+",encoding="utf8")
    file.write(javaClassContent)
    file.close()
    javaFiles.append(sheetName)


def generate():
    for excelPath in allFils:
        wb = openpyxl.load_workbook(excelPath,read_only=True,keep_links=False)
        for sheet in wb:
            if sheet.title.find("策劃") > -1 or sheet.title.startswith("sheet") or sheet.title.startswith("Sheet") or sheet.title.find("coder") > -1 or sheet.title[0].islower():
                continue
            if sheet.title in sheetNames:
                print("相同的sheet名稱:"+sheet.title)
                sys.exit(0)
            sheetNames.append(sheet.title)
            rows = list(sheet.rows)
            if len(rows) < 3:
                continue

            #註釋
            note_row = rows.pop(0)
            field_row = rows.pop(0)
            type_row = rows.pop(0)
            #名字
            names,indexs = getNameAndIndexs(field_row)
            if names[0] != "sn":
                continue

            notes = getRowContents(note_row)

            #型別
            row_field_types = getRowContents(type_row)
            if len(names) != len(row_field_types):
                print("excel 資料錯誤:"+excelPath)
                sys.exit(0)

            generateJSON(sheet.title,names,row_field_types,rows)
            generateJAVA(excelPath,sheet.title,names,notes,indexs,row_field_types)


def startup():
    allExcelFils(RESOURCE_PATH)
    generate()

#啟動
if __name__ == '__main__':
    startup()