Repository: tableauio/tableau
Pull request #117 (2024-08-03 07:39:55)

XML: improve XML importer and document parser
- fix: keep node's children order stable
- chore: remove deprecated XML test cases in test/dev

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/tableauio/tableau/pull/117?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio) Report
Attention: Patch coverage is `92.85714%` with `2 lines` in your changes missing coverage. Please review.
> Project coverage is 66.77%. Comparing base [(`2845eed`)](https://app.codecov.io/gh/tableauio/tableau/commit/2845eed7ed3d1a54795667c7f970c73ddbe39394?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio) to head [(`7f7b9d6`)](https://app.codecov.io/gh/tableauio/tableau/commit/7f7b9d630b14972881aaca2ea5cd00ee3b6ce6cf?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio).

| [Files](https://app.codecov.io/gh/tableauio/tableau/pull/117?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio) | Patch % | Lines |
|---|---|---|
| [internal/importer/xml.go](https://app.codecov.io/gh/tableauio/tableau/pull/117?src=pr&el=tree&filepath=internal%2Fimporter%2Fxml.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio#diff-aW50ZXJuYWwvaW1wb3J0ZXIveG1sLmdv) | 92.85% | [1 Missing and 1 partial :warning: ](https://app.codecov.io/gh/tableauio/tableau/pull/117?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio) |

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##           master     #117   +/-   ##
=======================================
  Coverage   66.77%   66.77%           
=======================================
  Files          44       44           
  Lines        5532     5532           
=======================================
  Hits         3694     3694           
  Misses       1357     1357           
  Partials      481      481           
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/tableauio/tableau/pull/117?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio).


Patch:
diff --git a/internal/importer/xml.go b/internal/importer/xml.go
index 5b558da..775278e 100644
--- a/internal/importer/xml.go
+++ b/internal/importer/xml.go
@@ -12,7 +12,6 @@ import (
 	"strings"
 
 	"github.com/antchfx/xmlquery"
-	"github.com/emirpasic/gods/sets/treeset"
 	"github.com/pkg/errors"
 	"github.com/tableauio/tableau/internal/importer/book"
 	"github.com/tableauio/tableau/internal/types"
@@ -229,56 +228,55 @@ func readXMLFile(metasheet, content string, newBook *book.Book, mode ImporterMod
 //	    <Item Sheet="XXXConf" />
 //	</@TABLEAU>`
 func genMetasheet(tableauNode *xmlquery.Node) (map[string]map[string]string, *book.Sheet, error) {
+	root := &book.Node{
+		Kind: book.MapNode,
+		Children: []*book.Node{
+			{
+				Kind:  book.MapNode,
+				Name:  book.KeywordSheet,
+				Value: book.MetasheetName,
+			},
+		},
+	}
 	// sheetName -> {colName -> val}
 	metasheetMap := make(map[string]map[string]string)
-	set := treeset.NewWithStringComparator()
 	for n := tableauNode.FirstChild; n != nil; n = n.NextSibling {
 		if n.Type != xmlquery.ElementNode {
 			continue
 		}
+		var children []*book.Node
 		sheetMap := make(map[string]string)
+		var sheetName string
 		for _, attr := range n.Attr {
-			sheetMap[attr.Name.Local] = attr.Value
+			name := attr.Name.Local
+			value := attr.Value
+			children = append(children, &book.Node{
+				Name:  name,
+				Value: value,
+			})
+			sheetMap[name] = value
+
+			if name == "Sheet" {
+				sheetName = value
+			}
 		}
-		sheetName, ok := sheetMap["Sheet"]
-		if !ok {
-			return metasheetMap, nil, fmt.Errorf("@TABLEAU not specified sheetName by keyword `Sheet`")
+		if sheetName == "" {
+			return metasheetMap, nil, errors.Errorf("field `Sheet` not specified in metasheet @TABLEAU")
 		}
 		metasheetMap[sheetName] = sheetMap
-		for k := range sheetMap {
-			set.Add(k)
+		sheetNode := &book.Node{
+			Kind:     book.MapNode,
+			Name:     sheetName,
+			Children: children,
 		}
+		root.Children = append(root.Children, sheetNode)
 	}
-	root := &book.Node{
-		Kind: book.MapNode,
-		Children: []*book.Node{
-			{
-				Kind:  book.MapNode,
-				Name:  book.KeywordSheet,
-				Value: book.MetasheetName,
-			},
-		},
-	}
-	bdoc := &book.Node{
+	doc := &book.Node{
 		Kind:     book.DocumentNode,
 		Name:     book.MetasheetName,
 		Children: []*book.Node{root},
 	}
-	for sheetName, sheet := range metasheetMap {
-		sheetNode := &book.Node{
-			Kind: book.MapNode,
-			Name: sheetName,
-		}
-		root.Children = append(root.Children, sheetNode)
-		for k, v := range sheet {
-			sheetNode.Children = append(sheetNode.Children, &book.Node{
-				Kind:  book.MapNode,
-				Name:  k,
-				Value: v,
-			})
-		}
-	}
-	sheet := book.NewDocumentSheet(book.MetasheetName, bdoc)
+	sheet := book.NewDocumentSheet(book.MetasheetName, doc)
 	return metasheetMap, sheet, nil
 }
 


Test patch:
diff --git a/internal/importer/xml_test.go b/internal/importer/xml_test.go
index 0173904..1f03e40 100644
--- a/internal/importer/xml_test.go
+++ b/internal/importer/xml_test.go
@@ -2,13 +2,12 @@ package importer
 
 import (
 	"fmt"
-	"os"
-	"path/filepath"
 	"reflect"
 	"strings"
 	"testing"
 
 	"github.com/antchfx/xmlquery"
+	"github.com/stretchr/testify/require"
 	"github.com/tableauio/tableau/internal/importer/book"
 	"github.com/tableauio/tableau/log"
 	"github.com/tableauio/tableau/proto/tableaupb"
@@ -665,12 +664,10 @@ func Test_genMetasheet(t *testing.T) {
 									Name: "ServerConf",
 									Children: []*book.Node{
 										{
-											Kind:  book.MapNode,
 											Name:  "Sheet",
 											Value: "ServerConf",
 										},
 										{
-											Kind:  book.MapNode,
 											Name:  "Sep",
 											Value: "|",
 										},
@@ -694,9 +691,7 @@ func Test_genMetasheet(t *testing.T) {
 			if !reflect.DeepEqual(got, tt.want) {
 				t.Errorf("genMetasheet() got = %v, want %v", got, tt.want)
 			}
-			if !reflect.DeepEqual(got1, tt.want1) {
-				t.Errorf("genMetasheet() got1 = %v, want %v", got1, tt.want1)
-			}
+			require.Equalf(t, got1, tt.want1, "genMetasheet() got1 = %v, want %v", got1, tt.want1)
 		})
 	}
 }
@@ -1280,8 +1275,6 @@ func TestNewXMLImporter(t *testing.T) {
 					t.Errorf("NewXMLImporter() GetSheet = nil, want %v", sheet)
 					return
 				}
-				csvName := fmt.Sprintf("%s#%s.csv", strings.TrimSuffix(tt.args.filename, filepath.Ext(tt.args.filename)), sheet)
-				os.Remove(csvName)
 			}
 		})
 	}
diff --git a/test/dev/proto/common/cs_dbkeyword.proto b/test/dev/proto/common/cs_dbkeyword.proto
deleted file mode 100644
index 832b078..0000000
--- a/test/dev/proto/common/cs_dbkeyword.proto
+++ /dev/null
@@ -1,102 +0,0 @@
-syntax = "proto3";
-package protoconf;
-option go_package = "github.com/tableauio/tableau/test/dev/protoconf/common";
-
-import "tableau/protobuf/tableau.proto";
-
-// 由gen_feature_toggle自动生成，请勿手动添加
-enum FeatureToggleMacroType {
-    TOGGLE_SAMPLE = 0;
-    TOGGLE_TEST = 1; //测试
-    TOGGLE_MATCH = 2; //匹配
-    TOGGLE_EVENT = 3; //事件
-    TOGGLE_GUILD = 4; //公会
-    TOGGLE_EQUIP_SELL = 5; //记忆(意志)出售
-    TOGGLE_EQUIP_SWITCH = 6; //记忆(意志)装配
-    TOGGLE_EQUIP_UPGRADE = 7; //记忆(意志)强化
-    TOGGLE_WEAPON_STAR_UP = 8; //意志进阶
-    TOGGLE_TEAM_CREATE_TEAM = 9; //创建队伍
-    TOGGLE_TEAM_REQ_MATCH = 10; //队伍匹配
-    TOGGLE_TEAM_JOIN_TEAM = 11; //加入队伍
-    TOGGLE_BOUNTY_UNLOCK = 12; //悬赏解锁
-    TOGGLE_RANK = 13; //排行榜
-    TOGGLE_MALL_SHOP = 14; //商城
-    TOGGLE_MALL_BUY = 15; //商城购买
-    TOGGLE_VIP_ENTRY = 40; //会员、超会入口
-    TOGGLE_INVITE = 41; //邀请
-    TOGGLE_GAME_PRIVILEGE = 42; //游戏特权
-    TOGGLE_PANDORA = 43; //潘多拉开关
-    TOGGLE_MICRO_COMMUNITY = 44; //微社区入口开关
-    TOGGLE_TENCENT_ESPORT = 45; //企鹅电竞
-    TOGGLE_XINYUE_PRIVILEGE = 46; //心悦特权
-    TOGGLE_WEIXIN_MEDIA_PLATFORM = 47; //微信公众号
-    TOGGLE_PACKAGE_CENTER = 48; //礼包中心
-    TOGGLE_ANNOUNCEMENT = 49; //公告(包括:登录前公告+登录后公告)
-    TOGGLE_ACTIVITY_ENTRY = 50; //活动入口
-}
-
-// 单局类型 @ name=MissionType
-enum MissionType {
-    MT_EMMT_MIN = 0 [(tableau.evalue).name = "最小值"];
-    MT_MAIN_FUBEN = 1 [(tableau.evalue).name = "循环副本"];
-    MT_PLOT_FUBEN = 2 [(tableau.evalue).name = "剧情副本"];
-    MT_ABYSS_FUBEN = 3 [(tableau.evalue).name = "深渊副本"];
-    MT_ANCIENT_FUBEN = 4 [(tableau.evalue).name = "远古副本"];
-    MT_RAID_FUBEN = 5 [(tableau.evalue).name = "团队副本"];
-    MT_CLASSIC_PVP = 6 [(tableau.evalue).name = "经典PVP"];
-    MT_UNKNOWN_LANDS = 7 [(tableau.evalue).name = "异界副本"];
-    MT_BOUNTY_FUBEN = 8 [(tableau.evalue).name = "悬赏副本"];
-    MT_CLASSIC_PVP_1V1 = 9 [(tableau.evalue).name = "经典1V1"];
-    MT_CLASSIC_PVP_3v3 = 10 [(tableau.evalue).name = "经典3V3"];
-    MT_CLASSIC_PVP_HeadCount = 11 [(tableau.evalue).name = "人头战"];
-    MT_PVP_Low_Rank_RoundBattle_1V1 = 20 [(tableau.evalue).name = "车轮战低端排位赛1v1"];
-}
-
-// 匹配策略
-enum ENMMatchStrategy {
-    MatchStrategy_Unify = 0; //无差别匹配
-    MatchStrategy_Glicko2 = 1; //Glicko2匹配
-    MatchStrategy_RoleHonorPower = 2; //英雄荣耀战力匹配
-    MatchStrategy_MMR = 3; //MMR匹配
-    MatchStrategy_TrueSkill = 4; //TrueSkill匹配
-    MatchStrategy_Max = 5; //最大值
-}
-
-// 匹配范围
-enum ENMMatchScope {
-    MatchScope_Zone = 0; //小区级匹配
-    MatchScope_World = 1; //大区级匹配
-    MatchScope_All = 2; //全大区级匹配
-    MatchScope_Max = 3; //最大值
-}
-
-// 匹配过滤器
-enum ENMMatchFilter {
-    MatchFilter_MaxRoleNum = 0; //最大角色数过滤器
-    MatchFilter_MaxLevelGap = 1; //最大等级差过滤器
-    MatchFilter_VersionConsistent = 2; //版本一致性过滤器 资源版本号
-    MatchFilter_MaxRatingGap = 3; //Elo隐藏分过滤器
-    MatchFilter_SelectedRole = 4; //已选角色滤器
-    MatchFilter_MaxHornorPowerGap = 5; //最大荣耀战力差过滤器
-    MatchFilter_Platform = 6; //平台过滤器
-    MatchFilter_MaxMMRGap = 7; //版本一致性过滤器
-    MatchFilter_TryNoDupRole = 8; //优先匹配不重复角色过滤器
-    MatchFilter_MatchVersion = 9; //上行版本一致过滤器 请求的版本号
-    MatchFilter_TrySameCluster = 10; //优先匹配同一个选择同一个集群ID过滤器
-    MatchFilter_DiffGuild = 11; //战队赛，相同战队不匹配
-    MatchFilter_Max = 12; //最大值
-}
-
-// AI温暖局类型
-enum ENMAIWarmType {
-    ENM_AI_NONE = 0; //@cname=无AI @display=无AI
-    ENM_AI_NEWER = 1; //@cname=新手温暖局 @display=新手温暖局
-    ENM_AI_DAILY = 2; //@cname=每日温暖局 @display=每日温暖局
-    ENM_AI_TIMEOUT = 3; //@cname=匹配超时AI @display=匹配超时AI
-    ENM_AI_CMD = 4; //@cname=cmd匹配AI @display=cmd匹配AI
-    ENM_AI_RETURN = 5; //@cname=回归玩家 @display=回归玩家
-    ENM_AI_TRAIN = 6; //@cname=训练局 @display=训练局
-    ENM_AI_GUARANTEE = 7; //@cname=保底AI @display=保底AI
-    ENM_AI_RATING = 8; //@cname=AI评级 @display=AI评级
-    ENM_AI_MAX = 9; //@cname=MAX @display=MAX
-}
diff --git a/test/dev/proto/feature_toggle_conf.proto b/test/dev/proto/feature_toggle_conf.proto
deleted file mode 100644
index 4059103..0000000
--- a/test/dev/proto/feature_toggle_conf.proto
+++ /dev/null
@@ -1,36 +0,0 @@
-// Code generated by tableau (protogen v0.5.0). DO NOT EDIT.
-// clang-format off
-
-syntax = "proto3";
-
-package protoconf;
-
-import "common/cs_dbkeyword.proto";
-import "google/protobuf/timestamp.proto";
-import "tableau/protobuf/tableau.proto";
-
-option go_package = "github.com/tableauio/tableau/test/dev/protoconf";
-option (tableau.workbook) = {name:"xml/tableau_xml/FeatureToggle.xml"};
-
-message ToggleCfg {
-  option (tableau.worksheet) = {name:"ToggleCfg" namerow:1 typerow:2 noterow:3 datarow:4 nameline:1 typeline:1 nested:true};
-
-  map<uint32, FeatureToggle> feature_toggle_map = 1 [(tableau.field) = {name:"FeatureToggle" key:"EnvID" layout:LAYOUT_VERTICAL}];
-  message FeatureToggle {
-    uint32 env_id = 1 [(tableau.field) = {name:"EnvID"}];
-    string desc = 2 [(tableau.field) = {name:"Desc"}];
-    map<int32, Toggle> toggle_map = 3 [(tableau.field) = {name:"Toggle" key:"ID" layout:LAYOUT_VERTICAL}];
-    message Toggle {
-      protoconf.FeatureToggleMacroType id = 1 [(tableau.field) = {name:"ID"}];
-      uint32 world_id = 2 [(tableau.field) = {name:"WorldID"}];
-      uint32 zone_id = 3 [(tableau.field) = {name:"ZoneID"}];
-      google.protobuf.Timestamp open_time = 4 [(tableau.field) = {name:"OpenTime" prop:{default:"2022-01-23 15:40:00"}}];
-      google.protobuf.Timestamp close_time = 5 [(tableau.field) = {name:"CloseTime" prop:{default:"2022-01-23 15:40:00"}}];
-      int32 open_rate = 6 [(tableau.field) = {name:"OpenRate"}];
-      string desc = 7 [(tableau.field) = {name:"Desc"}];
-      bool notify_closed = 8 [(tableau.field) = {name:"NotifyClosed"}];
-      string name = 9 [(tableau.field) = {name:"name"}];
-      repeated int64 sys_key_list = 10 [(tableau.field) = {name:"SysKey" layout:LAYOUT_HORIZONTAL}];
-    }
-  }
-}
diff --git a/test/dev/proto/hero_conf.proto b/test/dev/proto/hero_conf.proto
index bbc6add..d2cacf4 100644
--- a/test/dev/proto/hero_conf.proto
+++ b/test/dev/proto/hero_conf.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.5.0). DO NOT EDIT.
+// Code generated by tableau (protogen v0.6.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/dev/proto/list_conf.proto b/test/dev/proto/list_conf.proto
index 04fc5c3..7ea70ad 100644
--- a/test/dev/proto/list_conf.proto
+++ b/test/dev/proto/list_conf.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.5.0). DO NOT EDIT.
+// Code generated by tableau (protogen v0.6.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/dev/proto/map_conf.proto b/test/dev/proto/map_conf.proto
index 18b0540..e4a9bbc 100644
--- a/test/dev/proto/map_conf.proto
+++ b/test/dev/proto/map_conf.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.5.0). DO NOT EDIT.
+// Code generated by tableau (protogen v0.6.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/dev/proto/match_conf.proto b/test/dev/proto/match_conf.proto
deleted file mode 100644
index f28b557..0000000
--- a/test/dev/proto/match_conf.proto
+++ /dev/null
@@ -1,116 +0,0 @@
-// Code generated by tableau (protogen v0.5.0). DO NOT EDIT.
-// clang-format off
-
-syntax = "proto3";
-
-package protoconf;
-
-import "common/cs_dbkeyword.proto";
-import "google/protobuf/duration.proto";
-import "google/protobuf/timestamp.proto";
-import "tableau/protobuf/tableau.proto";
-
-option go_package = "github.com/tableauio/tableau/test/dev/protoconf";
-option (tableau.workbook) = {name:"xml/tableau_xml/match/Match.xml"};
-
-message MatchCfg {
-  option (tableau.worksheet) = {name:"MatchCfg" namerow:1 typerow:2 noterow:3 datarow:4 nameline:1 typeline:1 nested:true};
-
-  ConfirmTimeOutPunish confirm_time_out_punish = 1 [(tableau.field) = {name:"ConfirmTimeOutPunish"}];
-  message ConfirmTimeOutPunish {
-    google.protobuf.Duration time = 1 [(tableau.field) = {name:"Time"}];
-  }
-  MatchStatInfo match_stat_info = 2 [(tableau.field) = {name:"MatchStatInfo"}];
-  message MatchStatInfo {
-    google.protobuf.Duration report_interval = 1 [(tableau.field) = {name:"ReportInterval"}];
-  }
-  map<int32, MatchMode> match_mode_map = 3 [(tableau.field) = {name:"MatchMode" key:"MissionType" layout:LAYOUT_VERTICAL}];
-  message MatchMode {
-    protoconf.MissionType mission_type = 1 [(tableau.field) = {name:"MissionType"}];
-    string desc = 2 [(tableau.field) = {name:"Desc"}];
-    Time time = 3 [(tableau.field) = {name:"Time"}];
-    message Time {
-      google.protobuf.Duration min_time = 1 [(tableau.field) = {name:"MinTime"}];
-      google.protobuf.Duration max_time = 2 [(tableau.field) = {name:"MaxTime"}];
-      google.protobuf.Duration confirm_time = 3 [(tableau.field) = {name:"ConfirmTime"}];
-      int32 freq = 4 [(tableau.field) = {name:"Freq"}];
-    }
-    Open open = 4 [(tableau.field) = {name:"Open"}];
-    message Open {
-      google.protobuf.Timestamp begin_date = 1 [(tableau.field) = {name:"BeginDate"}];
-      google.protobuf.Duration begin_time = 2 [(tableau.field) = {name:"BeginTime"}];
-      google.protobuf.Timestamp end_date = 3 [(tableau.field) = {name:"EndDate"}];
-      google.protobuf.Duration end_time = 4 [(tableau.field) = {name:"EndTime"}];
-    }
-    repeated Camp camp_list = 5 [(tableau.field) = {name:"Camp" key:"CampID" layout:LAYOUT_VERTICAL}];
-    message Camp {
-      uint32 camp_id = 1 [(tableau.field) = {name:"CampID"}];
-      int32 player_num = 2 [(tableau.field) = {name:"PlayerNum"}];
-      string desc = 3 [(tableau.field) = {name:"Desc"}];
-    }
-    Strategy strategy = 6 [(tableau.field) = {name:"Strategy"}];
-    message Strategy {
-      protoconf.ENMMatchStrategy strategy_id = 1 [(tableau.field) = {name:"StrategyID"}];
-      string desc = 2 [(tableau.field) = {name:"Desc"}];
-    }
-    Scope scope = 7 [(tableau.field) = {name:"Scope"}];
-    message Scope {
-      protoconf.ENMMatchScope scope_id = 1 [(tableau.field) = {name:"ScopeID"}];
-      string desc = 2 [(tableau.field) = {name:"Desc"}];
-    }
-    repeated Filter filter_list = 8 [(tableau.field) = {name:"Filter" key:"FilterID" layout:LAYOUT_VERTICAL}];
-    message Filter {
-      protoconf.ENMMatchFilter filter_id = 1 [(tableau.field) = {name:"FilterID"}];
-      bool open = 2 [(tableau.field) = {name:"Open"}];
-      google.protobuf.Duration upd_interval = 3 [(tableau.field) = {name:"UpdInterval"}];
-      int32 value = 4 [(tableau.field) = {name:"Value"}];
-      string desc = 5 [(tableau.field) = {name:"Desc"}];
-      repeated Power power_list = 6 [(tableau.field) = {name:"Power" key:"HornorPower" layout:LAYOUT_VERTICAL}];
-      message Power {
-        int32 hornor_power = 1 [(tableau.field) = {name:"HornorPower"}];
-        repeated Param param_list = 2 [(tableau.field) = {name:"Param" key:"Value" layout:LAYOUT_VERTICAL}];
-        message Param {
-          int32 value = 1 [(tableau.field) = {name:"Value"}];
-        }
-      }
-    }
-    MatchAI match_ai = 9 [(tableau.field) = {name:"MatchAI"}];
-    message MatchAI {
-      bool is_open = 1 [(tableau.field) = {name:"IsOpen"}];
-      bool player_only_one_camp = 2 [(tableau.field) = {name:"PlayerOnlyOneCamp"}];
-      int32 guarantee_ai_ratio = 3 [(tableau.field) = {name:"GuaranteeAIRatio"}];
-      repeated AI ai_list = 4 [(tableau.field) = {name:"AI" key:"Type" layout:LAYOUT_VERTICAL}];
-      message AI {
-        protoconf.ENMAIWarmType type = 1 [(tableau.field) = {name:"Type"}];
-        bool is_open = 2 [(tableau.field) = {name:"IsOpen"}];
-        google.protobuf.Duration min_time = 3 [(tableau.field) = {name:"MinTime"}];
-        google.protobuf.Duration max_time = 4 [(tableau.field) = {name:"MaxTime"}];
-        string desc = 5 [(tableau.field) = {name:"Desc"}];
-      }
-    }
-    repeated Random random_list = 10 [(tableau.field) = {name:"Random" key:"MissionType" layout:LAYOUT_VERTICAL}];
-    message Random {
-      protoconf.MissionType mission_type = 1 [(tableau.field) = {name:"MissionType"}];
-    }
-    Route route = 11 [(tableau.field) = {name:"Route"}];
-    message Route {
-      int32 key = 1 [(tableau.field) = {name:"Key"}];
-      string desc = 2 [(tableau.field) = {name:"Desc"}];
-    }
-  }
-  MatchRoom match_room = 4 [(tableau.field) = {name:"MatchRoom"}];
-  message MatchRoom {
-    int32 max_loop = 1 [(tableau.field) = {name:"MaxLoop"}];
-  }
-  TeamRatingWeight team_rating_weight = 5 [(tableau.field) = {name:"TeamRatingWeight"}];
-  message TeamRatingWeight {
-    repeated Weight weight_list = 1 [(tableau.field) = {name:"Weight" key:"Num" layout:LAYOUT_VERTICAL}];
-    message Weight {
-      int32 num = 1 [(tableau.field) = {name:"Num"}];
-      repeated Param param_list = 2 [(tableau.field) = {name:"Param" key:"Value" layout:LAYOUT_VERTICAL}];
-      message Param {
-        int32 value = 1 [(tableau.field) = {name:"Value"}];
-      }
-    }
-  }
-}
diff --git a/test/dev/proto/metasheet_conf.proto b/test/dev/proto/metasheet_conf.proto
index 166ca7f..8c036bc 100644
--- a/test/dev/proto/metasheet_conf.proto
+++ b/test/dev/proto/metasheet_conf.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.5.0). DO NOT EDIT.
+// Code generated by tableau (protogen v0.6.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/dev/proto/multi_sheet_conf.proto b/test/dev/proto/multi_sheet_conf.proto
deleted file mode 100644
index 48a83af..0000000
--- a/test/dev/proto/multi_sheet_conf.proto
+++ /dev/null
@@ -1,67 +0,0 @@
-// Code generated by tableau (protogen v0.5.0). DO NOT EDIT.
-// clang-format off
-
-syntax = "proto3";
-
-package protoconf;
-
-import "tableau/protobuf/tableau.proto";
-
-option go_package = "github.com/tableauio/tableau/test/dev/protoconf";
-option (tableau.workbook) = {name:"xml/tableau_xml/MultiSheet.xml"};
-
-message Server {
-  option (tableau.worksheet) = {name:"Server" namerow:1 typerow:2 noterow:3 datarow:4 nameline:1 typeline:1 nested:true};
-
-  string desc = 1 [(tableau.field) = {name:"Desc"}];
-  MapConf map_conf = 2 [(tableau.field) = {name:"MapConf"}];
-  message MapConf {
-    repeated int64 param_list = 1 [(tableau.field) = {name:"Param" layout:LAYOUT_INCELL}];
-    map<int32, int32> type_num_map = 2 [(tableau.field) = {name:"TypeNum" layout:LAYOUT_INCELL}];
-    map<int32, Weight> weight_map = 3 [(tableau.field) = {name:"Weight" key:"Num" layout:LAYOUT_VERTICAL}];
-    message Weight {
-      int32 num = 1 [(tableau.field) = {name:"Num"}];
-      repeated Param param_list = 2 [(tableau.field) = {name:"Param" key:"Value" layout:LAYOUT_VERTICAL}];
-      message Param {
-        int32 value = 1 [(tableau.field) = {name:"Value"}];
-      }
-    }
-  }
-  repeated Node node_list = 3 [(tableau.field) = {name:"Node" key:"Addr" layout:LAYOUT_VERTICAL}];
-  message Node {
-    string addr = 1 [(tableau.field) = {name:"Addr"}];
-    int32 proc_num = 2 [(tableau.field) = {name:"ProcNum"}];
-  }
-  StructConf struct_conf = 4 [(tableau.field) = {name:"StructConf"}];
-  message StructConf {
-    Weight weight = 1 [(tableau.field) = {name:"Weight"}];
-    message Weight {
-      int32 num = 1 [(tableau.field) = {name:"Num"}];
-      Param param = 2 [(tableau.field) = {name:"Param"}];
-      message Param {
-        int32 value = 1 [(tableau.field) = {name:"Value"}];
-      }
-    }
-  }
-  ListConf list_conf = 5 [(tableau.field) = {name:"ListConf"}];
-  message ListConf {
-    repeated Weight weight_list = 1 [(tableau.field) = {name:"Weight" key:"Num" layout:LAYOUT_VERTICAL}];
-    message Weight {
-      int32 num = 1 [(tableau.field) = {name:"Num"}];
-      repeated Param param_list = 2 [(tableau.field) = {name:"Param" key:"Value" layout:LAYOUT_VERTICAL}];
-      message Param {
-        int32 value = 1 [(tableau.field) = {name:"Value"}];
-      }
-    }
-  }
-}
-
-message Client {
-  option (tableau.worksheet) = {name:"Client" namerow:1 typerow:2 noterow:3 datarow:4 nameline:1 typeline:1 nested:true};
-
-  Version version = 1 [(tableau.field) = {name:"Version"}];
-  message Version {
-    string min = 1 [(tableau.field) = {name:"Min"}];
-    string max = 2 [(tableau.field) = {name:"Max"}];
-  }
-}
diff --git a/test/dev/proto/simple_match_conf.proto b/test/dev/proto/simple_match_conf.proto
deleted file mode 100644
index 201b69b..0000000
--- a/test/dev/proto/simple_match_conf.proto
+++ /dev/null
@@ -1,16 +0,0 @@
-// Code generated by tableau (protogen v0.5.0). DO NOT EDIT.
-// clang-format off
-
-syntax = "proto3";
-
-package protoconf;
-
-import "tableau/protobuf/tableau.proto";
-
-option go_package = "github.com/tableauio/tableau/test/dev/protoconf";
-option (tableau.workbook) = {name:"xml/tableau_xml/match/SimpleMatch.xml"};
-
-message SimpleMatchConf {
-  option (tableau.worksheet) = {name:"SimpleMatchConf" namerow:1 typerow:2 noterow:3 datarow:5 nameline:2 typeline:2};
-
-}
diff --git a/test/dev/proto/test_conf.proto b/test/dev/proto/test_conf.proto
index 1bafda3..5b8161b 100644
--- a/test/dev/proto/test_conf.proto
+++ b/test/dev/proto/test_conf.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.5.0). DO NOT EDIT.
+// Code generated by tableau (protogen v0.6.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/dev/tableau_test.go b/test/dev/tableau_test.go
index 486fb74..779ce2b 100644
--- a/test/dev/tableau_test.go
+++ b/test/dev/tableau_test.go
@@ -25,17 +25,16 @@ func Test_GenProto(t *testing.T) {
 			&options.ProtoOption{
 				Input: &options.ProtoInputOption{
 					ProtoFiles: []string{
-						"common/cs_dbkeyword.proto",
 						"common/common.proto",
 						"common/time.proto",
 					},
 					Formats: []format.Format{
 						// format.Excel,
 						format.CSV,
-						format.XML,
+						// format.XML,
 					},
 					// Formats: []format.Format{format.CSV},
-					Subdirs: []string{`xml/tableau_xml`, `excel`},
+					Subdirs: []string{`excel`},
 					// SubdirRewrites: map[string]string{
 					// 	`excel/`: ``,
 					// },
@@ -191,7 +190,7 @@ func Test_GenJSON_Subdir(t *testing.T) {
 		options.Conf(
 			&options.ConfOption{
 				Input: &options.ConfInputOption{
-					Formats: []format.Format{format.XML},
+					Formats: []format.Format{format.CSV},
 					// Subdirs: []string{`excel/`},
 					// SubdirRewrites: map[string]string{
 					// 	`excel/`: ``,
diff --git a/test/dev/testdata/xml/non_tableau_xml/Client.xml b/test/dev/testdata/xml/non_tableau_xml/Client.xml
deleted file mode 100644
index 6289049..0000000
--- a/test/dev/testdata/xml/non_tableau_xml/Client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<ClientCfg>
-    <Version Min="1.0.0.1" Max="1.0.0.3"/>
-    <String value="<>"/>
-</ClientCfg>
\ No newline at end of file
diff --git a/test/dev/testdata/xml/tableau_xml/FeatureToggle.xml b/test/dev/testdata/xml/tableau_xml/FeatureToggle.xml
deleted file mode 100644
index e4c1b2e..0000000
--- a/test/dev/testdata/xml/tableau_xml/FeatureToggle.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<!--
-<@TABLEAU>
-    <Item Sheet="ToggleCfg" />
-</@TABLEAU>
-    
-<ToggleCfg>
-    <FeatureToggle EnvID="map<uint32,FeatureToggle>" name="string">
-        <Toggle ID="map<enum<.FeatureToggleMacroType>, Toggle>" WorldID="uint32" ZoneID="uint32" OpenTime="datetime|{default:"2022-01-23 15:40:00"}" CloseTime="datetime|{default:"2022-01-23 15:40:00"}" SysKey1="int64" SysKey2="int64" NotifyClosed="bool"/>
-    </FeatureToggle>
-</ToggleCfg>
--->
-
-<ToggleCfg>
-    <FeatureToggle EnvID="0" Desc="common: 通用环境的配置，需要包含全量的开关。common没有且对应环境也没有的开关会被默认为关闭。">
-        <!--
-        - ID				系统对应的toggleid
-        - OpenRate			开放比例[0,10000], 0表示关闭，10000全开
-        - WorldID			大区id, 0表示所有大区，支持','分隔配置多个，示例: 1,2；支持'-'配置连续大区,示例: 1-2 相当于配置1,2两个大区
-        - ZoneID			小区id, 0表示所有小区，支持','分隔配置多个，示例: 1,2；支持'-'配置连续大区,示例: 1-2 相当于配置1,2两个小区
-        - OpenTime			开启时间，逗号分隔日期和时间，示例：20190101, 0 表示20190101 0点开启; 20190101, 100000 表示20190101 10点0分0秒开启
-        - CloseTime			关闭时间，逗号分隔日期和时间，示例：20190101, 0 表示20190101 0点关闭ui9; 20190101, 100000 表示20190101 10点0分0秒关闭
-        - SysKey1-3			系统自定义筛选key
-        - NotifyClosed		开关关闭是否通知客户端，0不通知，其他通知
-        -->
-        <Toggle name="测试" ID="TOGGLE_TEST" OpenRate="10000" />
-        <Toggle name="匹配" ID="TOGGLE_MATCH" OpenRate="10000" />
-        <Toggle name="事件" ID="TOGGLE_EVENT" OpenRate="10000" />
-        <Toggle name="公会" ID="TOGGLE_GUILD" OpenRate="10000"/>
-        <Toggle name="记忆(意志)出售" ID="TOGGLE_EQUIP_SELL" OpenRate="10000" />
-        <Toggle name="记忆(意志)装配" ID="TOGGLE_EQUIP_SWITCH" OpenRate="10000" SysKey1="0" Desc="SysKey1->EquipID"/>
-        <Toggle name="记忆(意志)强化" ID="TOGGLE_EQUIP_UPGRADE" OpenRate="10000" />
-        <Toggle name="意志进阶" ID="TOGGLE_WEAPON_STAR_UP" OpenRate="10000" SysKey1="0" Desc="SysKey1->EquipID"/>
-        <Toggle name="创建队伍" ID="TOGGLE_TEAM_CREATE_TEAM" OpenRate="10000" />
-        <Toggle name="队伍匹配" ID="TOGGLE_TEAM_REQ_MATCH" OpenRate="10000" />
-    </FeatureToggle>
-
-    <FeatureToggle EnvID="21" Desc="审核服: 以下开关配置只会在审核服生效，会覆盖上面common的配置。">
-        <Toggle name="会员、超会入口" ID="TOGGLE_VIP_ENTRY" OpenRate="10000" />
-        <Toggle name="邀请" ID="TOGGLE_INVITE" OpenRate="10000" />
-        <Toggle name="游戏特权" ID="TOGGLE_GAME_PRIVILEGE" OpenRate="10000" />
-        <Toggle name="潘多拉开关" ID="TOGGLE_PANDORA" OpenRate="0" />
-        <Toggle name="微社区入口开关" ID="TOGGLE_MICRO_COMMUNITY" OpenRate="10000" />
-        <Toggle name="企鹅电竞" ID="TOGGLE_TENCENT_ESPORT" OpenRate="10000" />
-        <Toggle name="心悦特权" ID="TOGGLE_XINYUE_PRIVILEGE" OpenRate="10000" />
-        <Toggle name="微信公众号" ID="TOGGLE_WEIXIN_MEDIA_PLATFORM" OpenRate="10000" />
-        <Toggle name="礼包中心" ID="TOGGLE_PACKAGE_CENTER" OpenRate="10000" />
-        <Toggle name="公告(包括:登录前公告+登录后公告)" ID="TOGGLE_ANNOUNCEMENT" OpenRate="0" />
-        <Toggle name="活动入口111" ID="TOGGLE_ACTIVITY_ENTRY" OpenRate="0" />
-    </FeatureToggle>
-</ToggleCfg>
diff --git a/test/dev/testdata/xml/tableau_xml/MultiSheet.xml b/test/dev/testdata/xml/tableau_xml/MultiSheet.xml
deleted file mode 100644
index ebb1ef8..0000000
--- a/test/dev/testdata/xml/tableau_xml/MultiSheet.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<!--
-<@TABLEAU>
-    <Item Sheet="Server" />
-    <Item Sheet="Client" />
-</@TABLEAU>
-    
-<Server>
-    <MapConf Param="[]int64" TypeNum="map<int32, int32>">
-        <Weight Num="map<int32,Weight>"/>
-    </MapConf>
-</Server>
-<Client/>
--->
-
-<Server Desc="test">
-    <Node Addr="9.123.134.1" ProcNum="2"/>
-    <Node Addr="9.168.134.1" ProcNum="5"/>
-
-    <StructConf>
-        <Weight Num="1">
-            <Param Value="100"/>
-        </Weight>
-    </StructConf>
-
-    <ListConf>
-        <Weight Num="1">
-            <Param Value="100"/>
-        </Weight>
-        <Weight Num="2">
-            <Param Value="30"/>
-            <Param Value="70"/>
-        </Weight>
-    </ListConf>
-
-    <MapConf Param="1,2,3" TypeNum="1:1,2:2">
-        <Weight Num="1">
-            <Param Value="100"/>
-        </Weight>
-        <Weight Num="2">
-            <Param Value="30"/>
-            <Param Value="70"/>
-        </Weight>
-    </MapConf>
-</Server>
-
-<Client>
-    <Version Min="1.0.0.1" Max="1.0.0.3"/>
-</Client>
-
-<TrashBin>
-    <Trash Name="Banana"/>
-    <Trash Name="Apple"/>
-    <Trash Name="Bottle"/>
-</TrashBin>
diff --git a/test/dev/testdata/xml/tableau_xml/match/Match.xml b/test/dev/testdata/xml/tableau_xml/match/Match.xml
deleted file mode 100644
index 9c3e25b..0000000
--- a/test/dev/testdata/xml/tableau_xml/match/Match.xml
+++ /dev/null
@@ -1,88 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<!--
-<@TABLEAU>
-    <Item Sheet="MatchCfg" />
-</@TABLEAU>
-    
-<MatchCfg>
-    <ConfirmTimeOutPunish Time="duration" />
-    <MatchStatInfo ReportInterval="duration"/>
-    <MatchMode MissionType="map<enum<.MissionType>,MatchMode>">
-        <Time MinTime="duration" MaxTime="duration" ConfirmTime="duration" />
-        <Open BeginDate="date" BeginTime="time" EndDate="date" EndTime="time" />
-        <Camp CampID="[Camp]<uint32>" />
-        <Strategy StrategyID="enum<.ENMMatchStrategy>" />
-
-        <Scope ScopeID="enum<.ENMMatchScope>" />
-        <Filter FilterID="[Filter]<enum<.ENMMatchFilter>>" Open="bool" UpdInterval="duration">
-            <Power HornorPower="[Power]<int32>">
-                <Param Value="[Param]<int32>"/>
-            </Power>
-        </Filter>
-        <MatchAI IsOpen="bool" PlayerOnlyOneCamp="bool">
-            <AI Type="[AI]<enum<.ENMAIWarmType>>" IsOpen="bool" MinTime="duration" MaxTime="duration" />
-        </MatchAI>
-        <Random MissionType="[Random]<enum<.MissionType>>" />
-    </MatchMode>
-</MatchCfg>
--->
-
-<MatchCfg>
-    <ConfirmTimeOutPunish Time="2s" />
-    <MatchRoom MaxLoop="100" />
-
-    <TeamRatingWeight>
-        <Weight Num="1">
-            <Param Value="100"/>
-        </Weight>
-        <Weight Num="2">
-            <Param Value="30"/>
-            <Param Value="70"/>
-        </Weight>
-    </TeamRatingWeight>
-
-    <MatchMode MissionType="MT_PVP_Low_Rank_RoundBattle_1V1" Desc="排位赛">
-        <Time MinTime="5s" MaxTime="90s" Freq="3" ConfirmTime="15s" />
-        <Open BeginDate="20200506" BeginTime="000000" EndDate="20500506" EndTime="235959" />
-        <Camp CampID="0" PlayerNum="1" Desc="阵营1" />
-        <Camp CampID="1" PlayerNum="1" Desc="阵营2" />
-        <Strategy StrategyID="MatchStrategy_Glicko2" Desc="匹配策略" />
-        <Scope ScopeID="MatchScope_All" Desc="范围" />
-        <Filter FilterID="MatchFilter_Platform" Open="false" Value="1" Desc="平台过滤器Open为1表示开启" />
-        <Filter FilterID="MatchFilter_MaxRatingGap" Open="true" UpdInterval="5s" Desc="过滤器"/>
-        <Route Key="1" Desc="路由哈希值" />
-
-        <MatchAI IsOpen="true" PlayerOnlyOneCamp="false" GuaranteeAIRatio="0">
-            <AI Type="ENM_AI_NEWER" IsOpen="true" MinTime="2s" MaxTime="5s" Desc="新手温暖局" />
-            <AI Type="ENM_AI_DAILY" IsOpen="true" MinTime="5s" MaxTime="15s" Desc="每日温暖局" />
-            <AI Type="ENM_AI_TIMEOUT" IsOpen="true" MinTime="30s" MaxTime="50s" Desc="匹配超时AI" />
-        </MatchAI>
-    </MatchMode>
-
-    <MatchMode MissionType="MT_CLASSIC_PVP_1V1" Desc="旧经典1v1">
-        <Time MinTime="8s" MaxTime="70s" Freq="3"/>
-        <Open BeginDate="20201216" BeginTime="200000" EndDate="20201220" EndTime="203000" />
-        <Camp CampID="0" PlayerNum="1" Desc="阵营1" />
-        <Camp CampID="1" PlayerNum="1" Desc="阵营2" />
-        <Strategy StrategyID="MatchStrategy_RoleHonorPower" Desc="荣耀战力匹配策略" />
-        <Scope ScopeID="MatchScope_All" Desc="范围" />
-        <Filter FilterID="MatchFilter_Platform" Open="false" Value="1" Desc="平台过滤器Open为1表示开启" />
-        <Filter FilterID="MatchFilter_MaxHornorPowerGap" Open="true" UpdInterval="10s" Desc="荣耀战力匹配过滤器">
-            <Power HornorPower="300">
-                <Param Value="100"/>
-                <Param Value="200"/>
-            </Power>
-            <Power HornorPower="600">
-                <Param Value="130"/>
-                <Param Value="300"/>
-            </Power>
-        </Filter>
-        <Route Key="1" Desc="路由哈希值" />
-
-        <MatchAI IsOpen="true" PlayerOnlyOneCamp="false" GuaranteeAIRatio="0">
-            <AI Type="ENM_AI_NEWER" IsOpen="true" MinTime="2s" MaxTime="5s" Desc="新手温暖局" />
-            <AI Type="ENM_AI_DAILY" IsOpen="true" MinTime="5s" MaxTime="15s" Desc="每日温暖局" />
-            <AI Type="ENM_AI_TRAIN" IsOpen="false" MinTime="5s" MaxTime="15s" Desc="训练玩家AI" />
-        </MatchAI>
-    </MatchMode>
-</MatchCfg>
diff --git a/test/dev/testdata/xml/tableau_xml/match/SimpleMatch.xml b/test/dev/testdata/xml/tableau_xml/match/SimpleMatch.xml
deleted file mode 100644
index 651a197..0000000
--- a/test/dev/testdata/xml/tableau_xml/match/SimpleMatch.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<!--
-<@TABLEAU />
--->
-
-<SimpleMatchConf>
-    <!-- 匹配确认超时惩罚时间 -->
-    <ConfirmTimeOutPunish Time="2s" />
-    <MatchRoom MaxLoop="100" />
-
-    <TeamRatingWeight>
-        <Weight Num="1">
-            <Param Value="100"/>
-        </Weight>
-        <Weight Num="2">
-            <Param Value="30"/>
-            <Param Value="70"/>
-        </Weight>
-    </TeamRatingWeight>
-</SimpleMatchConf>