Unity에서 '딸깍'으로 Excel을 Json으로 바꾸기

Ricon·2024년 11월 4일

Unity

목록 보기
1/7

서론

프로젝트 진행중 Excel을 Json으로 바꾸는 툴을 만들게 되었다.
우선 GPT한테 물어보니 파이썬으로 Excel을 Json으로 바꾸고, 해당 Json을 유니티에서 사용하는 방법을 추천해줬는데.. 아무리 생각해도 이건 아닌거 같아서 다른 방법을 찾아봤다.

찾아보니 C#에서는 NPOI를 사용한 방법이 제일 보편적인 거 같아서 NPOI를 이용해서 Excel To Json Converter를 만들었다.


NPOI, Newtonsoft.Json

C#에서 Excel파일(.xlsx)과 Json을 사용하려면 NPOI, Newtonsoft.Json이라는 .NET Library가 필요한데, 유니티에서는 Visual Studio처럼 NuGet Package를 사용할 수 없다.

이를 해결하기 위해 NuGet For Unity를 사용하여 Unity Editor에서 NuGet을 사용할 수 있도록 했다


Excel To Json

우선 핵심 기능인 Convert Method부터 만들었다
기본적인 Flow는 다음과 같다

  1. Input, Output File Path가 존재하는지 확인
    1-1. 존재하지 않는다면 Return
  2. FileSteam을 사용하여 .xlsx파일을 Read Mode로 Open
  3. .xlsx파일을 읽기 위한 IWorkbook 선언
  4. Parameter로 Sheet Num을 받아와 GetSheetAt(sheetNum)을 통해.json파일로 변환할 Sheet 선택
    4-1. Sheet Num이 유효하지 않다면 LogError 출력후 Return
  5. List<Dictionary<string, object>> 리스트인 rowsData를 선언
  6. HeaderRow, CellCount등 필요한 변수를 설정
  7. for문을 돌며 각 Row 순회
  8. 이중 for문으로 각 행 cell값을 rowData에 저장
    8-1. private object GetValueFromCell(ICell cell) Method를 사용하여 각 Cell Type 판별
  9. rowsData.Add하여 데이터 삽입
  10. Data를 json으로 저장

이를 바탕으로 만들어진 Method 다음과 같다

public void ConvertExcelToJson(int sheetNum)
    {
        if (string.IsNullOrEmpty(excelFilePath) || string.IsNullOrEmpty(jsonOutputPath))
        {
            Debug.LogError("Excel Path, Json Path is NULL");
            return;
        }

        // Open Excel File
        using (FileStream stream = new FileStream(excelFilePath, FileMode.Open, FileAccess.Read))
        {
            IWorkbook workbook = new XSSFWorkbook(stream); //For .xlsx

            ISheet sheet;
            try
            {
                sheet = workbook.GetSheetAt(sheetNum); // Select Sheet
            }
            catch
            {
                Debug.LogError("Invalid Sheet Num");
                return;
            }

            // List for data from converted json
            var rowsData = new List<Dictionary<string, object>>();
            IRow headerRow = sheet.GetRow(0);
            int cellCount = headerRow.LastCellNum;

            // Extract Data looping every row in sheet
            for (int i = 1; i <= sheet.LastRowNum; i++) // 0 is Header
            {
                IRow row = sheet.GetRow(i);
                var rowData = new Dictionary<string, object>();

                for (int j = 0; j < cellCount; j++)
                {
                    string columnName = headerRow.GetCell(j).ToString(); //Get ColumnName from Header
                    ICell cell = row.GetCell(j);
                    rowData[columnName] = GetValueFromCell(cell); //Get Cell Value
                }

                rowsData.Add(rowData); // Add Row data to List
            }

            //save Data after convert Json
            string json = JsonConvert.SerializeObject(rowsData, Formatting.Indented);
            File.WriteAllText(jsonOutputPath, json);

            Debug.Log($"Convert Excel To Json : {jsonOutputPath}");
        }
    }

Editor Window

이제 해당 기능을 '딸깍'하기 위한 커스텀 에디터 윈도우를 만들 차례다
EditorWindow를 이용하여 GameObject Inspector가 아닌 Window로 사용할 수 있도록 한다.

우선 ShowWindow()를 사용하여 Window를 생성하고, OnGUI()를 통해 Window안에 들어갈 내용을 작성한다

Window의 큰 틀은 다음과 같다

  1. Excel File 선택 버튼
  2. Json으로 변경할 Excel Sheet 입력칸
  3. Json File Path 선택 버튼
  4. Convert 버튼

GUILayout을 사용하여 다음과 같이 작성하면

public static void ShowWindow()
{
    ExcelToJsonConverterWindow window = GetWindow<ExcelToJsonConverterWindow>("Excel to JSON Converter");
    window.Show();
}

private void OnGUI()
{
    GUILayout.Label("Select Excel File Path", EditorStyles.boldLabel);

    if (GUILayout.Button("Select Excel File"))
    {
        string path = EditorUtility.OpenFilePanel("Select Excel File", "", "xlsx");
        if (!string.IsNullOrEmpty(path))
        {
            excelFilePath = path;
        }
    }

    EditorGUILayout.TextField("Excel File Path", excelFilePath);

    GUILayout.Space(5);

    GUILayout.Label("Sheet Number", EditorStyles.boldLabel);
    sheetNum = EditorGUILayout.IntField("Enter Sheet Number", sheetNum);

    GUILayout.Space(10);

    GUILayout.Label("Json Output Path", EditorStyles.boldLabel);

    string outputFileName = Path.GetFileNameWithoutExtension(excelFilePath);
    if (GUILayout.Button("Select Json Output Path"))
    {
        string path = EditorUtility.SaveFilePanel("Select Json Output Path", "", outputFileName , "json");
        if (!string.IsNullOrEmpty(path))
        {
            jsonOutputPath = path;
        }
    }


    EditorGUILayout.TextField("Json File Path", jsonOutputPath);

    GUILayout.Space(10);

    if (GUILayout.Button("Convert Excel To Json"))
    {
        // Create ExcelToJson Class Instance and Call Convert Method 
        ExcelToJson converter = new ExcelToJson
        {
            excelFilePath = excelFilePath,
            jsonOutputPath = jsonOutputPath
        };

        // Call Convert Method 
        converter.ConvertExcelToJson(sheetNum);
    }
}

커스텀 윈도우가 만들어진다!

이제 Converter를 이용해서 '딸깍'으로 Excel을 Json으로 바꿀 수 있다!


문제점

문제점 1

이후 기획팀에서 전달해준 Excel파일을 변환해 봤는데..

Excel의 함수를 사용하면 함수 수식 그대로 문자열로 저장이 되어버렸다.

어디가 문제인가 봤더니, Cell값을 자동으로 적절한 자료형으로 변환해주는 GetValueFromCell()의 문제였다.

위 코드를 보면 CellType.Formula일때 다른 Case와 같이 수식값을 전달해주는데, 엑셀에서는 결과값이 나와도 Convert할때는 수식이 들어가버린다.

이를 해결하기 위해 우선 Cell값이 수식인 경우 수식을 계산하여 안에 있는 계산값을 다시 판별하는 방식으로 해결했다.

  1. Cell 내용이 Formula인지 확인
    1-1. Formula가 아니라면 -> 이전과 같이 적절한 자료형으로 캐스팅 후 json으로 변환
  2. Formula라면 evaluator를 사용하여 수식 계산
IFormulaEvaluator evaluator = workbook.GetCreationHelper().CreateFormulaEvaluator();
  1. 이후 결과값을 다시 적절한 자료형으로 캐스팅 하여 json으로 변환

이를 코드로 구현하면 다음과 같다

private object GetValueFromCell(CellValue cellValue)
{
    switch (cellValue.CellType)
    {
        case CellType.Numeric:
            return cellValue.NumberValue;
        case CellType.String:
            return cellValue.StringValue;
        case CellType.Boolean:
            return cellValue.BooleanValue;
        default:
            return null;
    }
}

private object GetValueFromCell(ICell cell, IFormulaEvaluator eval)
{
    if (cell == null) return null;

    switch (cell.CellType)
    {
        case CellType.Numeric:
            return cell.NumericCellValue;
        case CellType.String:
            return cell.StringCellValue;
        case CellType.Formula:
            var evaluatedCell = eval.Evaluate(cell);
            return GetValueFromCell(evaluatedCell);
        case CellType.Boolean:
            return cell.BooleanCellValue;
        default:
            return null;
    }
}

이제 함수로 계산된 Cell도 결과값을 json으로 저장할 수 있다!


문제점 2

기획팀에게 받은 Excel파일을 Convert해봤는데, 자꾸 NullReference Error가 떴다.

Convert Method의

ICell cell = row.GetCell(j);

부분에서 null이 잡히는 문제였는데, 이상하게 파일을 다시 만들어서 내용을 복붙을 하면 성공적으로 Convert가 되었다.

결국 Debug.Log를 찍어서 LastRowNum을 찍어보니, 본래 12개의 Row가 찍혀야 하는데 15개가 찍히는 것이다.

검색해보니 Excel에는 마지막 Row를 설정해야한다.
그리고 빈칸이여도 마지막 Row로 설정될 수 있으며, 이를 해결하기 위해서 마지막으로 설정하려는 행의 밑행을 클릭하고 Ctrl + Shift + ↓하여 아래 행들을 전부 선택한 후 Delete하면 해결된다.


결론

이로써 '딸깍!!!' 으로 Excel To Json Convert가 가능하게 되었다!
해당 기능을 Package로 Export하여 다른 Project에서 활용할 수 있을거같다.

0개의 댓글