Repository: tableauio/tableau
Pull request #20 (2022-09-22 08:32:18)

feat(xml): support metasheet `@TABLEAU` and template option
New features:
- support metasheet like `@TABLEAU` sheet in excel
- support xml template definition in metasheet which can ignore the contents in xml
- support `Sep` definition in metasheet which allows users to define whatever delimiter they like

Bug fixes:
- support list type instead of only keyed list
- support original name defined in xml file instead of auto-converted camel-case name

codecov[bot]: # [Codecov](https://codecov.io/gh/tableauio/tableau/pull/20?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio) Report
> Merging [#20](https://codecov.io/gh/tableauio/tableau/pull/20?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio) (6002f94) into [master](https://codecov.io/gh/tableauio/tableau/commit/70fb00d994b5fcf7b0e9cf5075a2b39cc8deab24?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio) (70fb00d) will **increase** coverage by `0.64%`.
> The diff coverage is `60.81%`.

```diff
@@            Coverage Diff             @@
##           master      #20      +/-   ##
==========================================
+ Coverage   17.86%   18.50%   +0.64%     
==========================================
  Files          19       19              
  Lines        2827     2853      +26     
==========================================
+ Hits          505      528      +23     
+ Misses       2283     2282       -1     
- Partials       39       43       +4     
```

| Flag | Coverage Δ | |
|---|---|---|
| unittests | `18.50% <60.81%> (+0.64%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/tableauio/tableau/pull/20?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio) | Coverage Δ | |
|---|---|---|
| [internal/importer/importer.go](https://codecov.io/gh/tableauio/tableau/pull/20/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio#diff-aW50ZXJuYWwvaW1wb3J0ZXIvaW1wb3J0ZXIuZ28=) | `0.00% <0.00%> (ø)` | |
| [internal/protogen/parser.go](https://codecov.io/gh/tableauio/tableau/pull/20/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio#diff-aW50ZXJuYWwvcHJvdG9nZW4vcGFyc2VyLmdv) | `0.00% <0.00%> (ø)` | |
| [internal/protogen/protogen.go](https://codecov.io/gh/tableauio/tableau/pull/20/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio#diff-aW50ZXJuYWwvcHJvdG9nZW4vcHJvdG9nZW4uZ28=) | `0.00% <0.00%> (ø)` | |
| [internal/importer/xml.go](https://codecov.io/gh/tableauio/tableau/pull/20/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio#diff-aW50ZXJuYWwvaW1wb3J0ZXIveG1sLmdv) | `50.31% <62.85%> (+2.28%)` | :arrow_up: |
| [test/functest/util.go](https://codecov.io/gh/tableauio/tableau/pull/20/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio#diff-dGVzdC9mdW5jdGVzdC91dGlsLmdv) | `72.72% <100.00%> (ø)` | |

:mega: We’re building smart automated test selection to slash your CI/CD build times. [Learn more](https://about.codecov.io/iterative-testing/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio)


Patch:
diff --git a/.gitignore b/.gitignore
index 7577968..0840cdf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,3 +23,4 @@ cmd/tableauc/tableauc
 test/dev/testpb/
 test/parallel/proto/
 test/parallel/protoconf/
+.vscode/
\ No newline at end of file
diff --git a/internal/importer/importer.go b/internal/importer/importer.go
index 5937bcc..f7346b9 100644
--- a/internal/importer/importer.go
+++ b/internal/importer/importer.go
@@ -37,7 +37,7 @@ func New(filename string, setters ...Option) (Importer, error) {
 	case format.CSV:
 		return NewCSVImporter(filename, opts.Sheets, opts.Parser)
 	case format.XML:
-		return NewXMLImporter(filename, opts.Sheets)
+		return NewXMLImporter(filename, opts.Sheets, opts.Parser)
 	default:
 		return nil, errors.Errorf("unsupported format: %v", fmt)
 	}
diff --git a/internal/importer/xml.go b/internal/importer/xml.go
index d50984c..2ff588f 100644
--- a/internal/importer/xml.go
+++ b/internal/importer/xml.go
@@ -11,7 +11,7 @@ import (
 	"strings"
 
 	"github.com/antchfx/xmlquery"
-	"github.com/iancoleman/strcase"
+	"github.com/emirpasic/gods/sets/treeset"
 	"github.com/pkg/errors"
 	"github.com/tableauio/tableau/internal/importer/book"
 	"github.com/tableauio/tableau/internal/types"
@@ -36,8 +36,9 @@ var attrRegexp *regexp.Regexp
 var scalarListRegexp *regexp.Regexp
 
 const (
-	xmlProlog         = `<?xml version='1.0' encoding='UTF-8'?>`
-	ungreedyPropGroup = `(\|\{[^\{\}]+\})?`
+	xmlProlog             = `<?xml version='1.0' encoding='UTF-8'?>`
+	ungreedyPropGroup     = `(\|\{[^\{\}]+\})?`
+	atTableauDisplacement = `ATABLEAU`
 )
 
 func init() {
@@ -46,8 +47,8 @@ func init() {
 }
 
 // TODO: options
-func NewXMLImporter(filename string, sheets []string) (*XMLImporter, error) {
-	newBook, err := parseXML(filename, sheets)
+func NewXMLImporter(filename string, sheets []string, parser book.SheetParser) (*XMLImporter, error) {
+	newBook, err := parseXML(filename, sheets, parser)
 	if err != nil {
 		return nil, errors.Wrapf(err, "failed to parse xml:%s", filename)
 	}
@@ -67,7 +68,7 @@ func NewXMLImporter(filename string, sheets []string) (*XMLImporter, error) {
 
 // parseXML parse sheets in the XML file named `filename` and return a book with multiple sheets
 // in TABLEAU grammar which can be exported to protobuf by excel parser.
-func parseXML(filename string, sheetNames []string) (*book.Book, error) {
+func parseXML(filename string, sheetNames []string, parser book.SheetParser) (*book.Book, error) {
 	log.Debugf("xml: %s", filename)
 	buf, err := os.ReadFile(filename)
 	if err != nil {
@@ -87,7 +88,9 @@ func parseXML(filename string, sheetNames []string) (*book.Book, error) {
 	}
 
 	// The first pass
-	xmlMeta, sheetNames, err := readXMLFile(root, sheetNames)
+	bookName := strings.TrimSuffix(filepath.Base(filename), filepath.Ext(filename))
+	newBook := book.NewBook(bookName, filename, parser)
+	xmlMeta, err := readXMLFile(root, newBook)
 	if err != nil {
 		switch e := err.(type) {
 		case *NoNeedParseError:
@@ -98,8 +101,6 @@ func parseXML(filename string, sheetNames []string) (*book.Book, error) {
 		}
 	}
 
-	bookName := strings.TrimSuffix(filepath.Base(filename), filepath.Ext(filename))
-	newBook := book.NewBook(bookName, filename, nil)
 	for _, xmlSheet := range xmlMeta.SheetList {
 		sheetName := xmlSheet.Meta.Name
 		// The second pass
@@ -113,7 +114,13 @@ func parseXML(filename string, sheetNames []string) (*book.Book, error) {
 		}
 		newBook.AddSheet(sheet)
 	}
-	log.Debug(sheetNames)
+
+	// parse meta sheet
+	if parser != nil {
+		if err := newBook.ParseMeta(); err != nil {
+			return nil, errors.WithMessage(err, "failed to parse metasheet")
+		}
+	}
 
 	if len(sheetNames) > 0 {
 		newBook.Squeeze(sheetNames)
@@ -123,57 +130,103 @@ func parseXML(filename string, sheetNames []string) (*book.Book, error) {
 }
 
 // --------------------------------------------- THE FIRST PASS ------------------------------------ //
-// The first pass simply reads xml file with xmlquery, construct a recursively self-described tree 
+// The first pass simply reads xml file with xmlquery, construct a recursively self-described tree
 // structure defined in xml.proto and put it into memory.
 //
 // readXMLFile read the raw xml rooted at `root`, specify which sheets to parse and return a XMLBook.
-func readXMLFile(root *xmlquery.Node, sheetNames []string) (*tableaupb.XMLBook, []string, error) {
+func readXMLFile(root *xmlquery.Node, newBook *book.Book) (*tableaupb.XMLBook, error) {
 	xmlMeta := &tableaupb.XMLBook{
 		SheetMap: make(map[string]int32),
 	}
-	noSheetByUser := len(sheetNames) == 0
 	foundMetaSheetName := false
+	metasheetMap := make(map[string]map[string]string)
 	for n := root.FirstChild; n != nil; n = n.NextSibling {
 		switch n.Type {
 		case xmlquery.CommentNode:
 			if !strings.Contains(n.Data, book.MetasheetName) {
 				continue
 			}
-			foundMetaSheetName = true
-			metaStr := xmlProlog + escapeAttrs(strings.ReplaceAll(n.Data, book.MetasheetName, ""))
-			log.Debug(metaStr)
+			metaStr := xmlProlog + escapeAttrs(strings.ReplaceAll(n.Data, book.MetasheetName, atTableauDisplacement))
 			metaRoot, err := xmlquery.Parse(strings.NewReader(metaStr))
 			if err != nil {
-				return nil, nil, errors.Wrapf(err, "failed to parse @TABLEAU string: %s", metaStr)
+				return nil, errors.Wrapf(err, "failed to parse @TABLEAU string: %s", metaStr)
 			}
 			for n := metaRoot.FirstChild; n != nil; n = n.NextSibling {
 				if n.Type != xmlquery.ElementNode {
 					continue
 				}
-				sheetName := strcase.ToCamel(n.Data)
+				if n.Data == atTableauDisplacement {
+					foundMetaSheetName = true
+					nameRow := book.MetasheetOptions().Namerow - 1
+					dataRow := book.MetasheetOptions().Datarow - 1
+					set := treeset.NewWithStringComparator()
+					for n := n.FirstChild; n != nil; n = n.NextSibling {
+						if n.Type != xmlquery.ElementNode {
+							continue
+						}
+						sheetMap := make(map[string]string)
+						for _, attr := range n.Attr {
+							sheetMap[attr.Name.Local] = attr.Value
+						}
+						sheetMap["Nameline"] = "1"
+						sheetMap["Typeline"] = "1"
+						sheetMap["Nested"] = "true"
+						sheetName, ok := sheetMap["Sheet"]
+						if !ok {
+							return nil, fmt.Errorf("@TABLEAU not specified sheetName by keyword `Sheet`")
+						}
+						metasheetMap[sheetName] = sheetMap
+						for k := range sheetMap {
+							set.Add(k)
+						}
+					}
+					rows := make([][]string, len(metasheetMap)+int(dataRow))
+					for _, k := range set.Values() {
+						rows[nameRow] = append(rows[nameRow], k.(string))
+					}
+					for _, sheet := range metasheetMap {
+						for _, k := range set.Values() {
+							if v, ok := sheet[k.(string)]; ok {
+								rows[dataRow] = append(rows[dataRow], v)
+							} else {
+								rows[dataRow] = append(rows[dataRow], "")
+							}
+						}
+						dataRow++
+					}
+					sheet := book.NewSheet(book.MetasheetName, rows)
+					newBook.AddSheet(sheet)
+					continue
+				}
+				sheetName := n.Data
 				xmlSheet := getXMLSheet(xmlMeta, sheetName)
 				if err := parseMetaNode(n, xmlSheet); err != nil {
-					return nil, nil, errors.Wrapf(err, "failed to parseMetaNode for sheet:%s", sheetName)
-				}
-				// append if user not specified
-				if noSheetByUser {
-					sheetNames = append(sheetNames, sheetName)
+					return nil, errors.Wrapf(err, "failed to parseMetaNode for sheet:%s", sheetName)
 				}
 			}
 		case xmlquery.ElementNode:
-			sheetName := strcase.ToCamel(n.Data)
+			sheet, ok := metasheetMap[n.Data]
+			// metasheet not empty and sheet not explicitly declared
+			if len(metasheetMap) != 0 && !ok {
+				log.Debugf("sheet not set in @TABLEAU, skilpped|sheetName:%v", n.Data)
+				continue
+			}
+			if template, ok := sheet["Template"]; ok && template == "true" {
+				continue
+			}
+			sheetName := n.Data
 			xmlSheet := getXMLSheet(xmlMeta, sheetName)
 			if err := parseDataNode(n, xmlSheet); err != nil {
-				return nil, nil, errors.Wrapf(err, "failed to parseDataNode for sheet:%s", sheetName)
+				return nil, errors.Wrapf(err, "failed to parseDataNode for sheet:%s", sheetName)
 			}
 		default:
 		}
 	}
-	if !foundMetaSheetName && noSheetByUser {
-		return nil, nil, &NoNeedParseError{}
+	if !foundMetaSheetName {
+		return nil, &NoNeedParseError{}
 	}
 
-	return xmlMeta, sheetNames, nil
+	return xmlMeta, nil
 }
 
 // parseMetaNode parse xml node `curr` and construct the meta tree in `xmlSheet`.
@@ -181,7 +234,7 @@ func parseMetaNode(curr *xmlquery.Node, xmlSheet *tableaupb.XMLSheet) error {
 	_, path := getNodePath(curr)
 	meta := xmlSheet.MetaNodeMap[path]
 	for _, attr := range curr.Attr {
-		attrName := strcase.ToCamel(attr.Name.Local)
+		attrName := attr.Name.Local
 		t := attr.Value
 		if len(meta.AttrMap.List) > 0 && isCrossCell(t) {
 			return fmt.Errorf("%s=\"%s\" is a complex type, must be the first attribute", attrName, t)
@@ -202,7 +255,7 @@ func parseMetaNode(curr *xmlquery.Node, xmlSheet *tableaupb.XMLSheet) error {
 		if n.Type != xmlquery.ElementNode {
 			continue
 		}
-		childName := strcase.ToCamel(n.Data)
+		childName := n.Data
 		if _, ok := meta.ChildMap[childName]; !ok {
 			newNode := newNode(childName, meta)
 			meta.ChildMap[childName] = &tableaupb.XMLNode_IndexList{
@@ -225,7 +278,7 @@ func parseDataNode(curr *xmlquery.Node, xmlSheet *tableaupb.XMLSheet) error {
 	data_nodes := xmlSheet.DataNodeMap[path].Nodes
 	data := data_nodes[len(data_nodes)-1]
 	for _, attr := range curr.Attr {
-		attrName := strcase.ToCamel(attr.Name.Local)
+		attrName := attr.Name.Local
 		t := inferType(attr.Value)
 		if _, ok := meta.AttrMap.Map[attrName]; !ok {
 			meta.AttrMap.Map[attrName] = int32(len(meta.AttrMap.List))
@@ -244,7 +297,7 @@ func parseDataNode(curr *xmlquery.Node, xmlSheet *tableaupb.XMLSheet) error {
 		if n.Type != xmlquery.ElementNode {
 			continue
 		}
-		childName := strcase.ToCamel(n.Data)
+		childName := n.Data
 		dataChild := newNode(childName, data)
 		registerDataNode(xmlSheet, dataChild)
 		if _, ok := meta.ChildMap[childName]; !ok {
@@ -306,7 +359,7 @@ func getNodePath(curr *xmlquery.Node) (root *xmlquery.Node, path string) {
 	return root, path
 }
 
-// inferType infer type from the node value, e.g.: 
+// inferType infer type from the node value, e.g.:
 // - 4324342: `int32`
 // - 4324324324324343243432: `int64`
 // - 4535ffdr43t3r: `string`
@@ -391,8 +444,8 @@ func getXMLSheet(xmlMeta *tableaupb.XMLBook, sheetName string) *tableaupb.XMLShe
 }
 
 // --------------------------------------------- THE SECOND PASS ------------------------------------ //
-// The second pass preprocesses the tree structure. In this phase the parser will do some necessary jobs 
-// before generating a 2-dimensional sheet, like correctType which make the types of attributes in the 
+// The second pass preprocesses the tree structure. In this phase the parser will do some necessary jobs
+// before generating a 2-dimensional sheet, like correctType which make the types of attributes in the
 // nodes meet the requirements of protogen.
 //
 func preprocess(xmlSheet *tableaupb.XMLSheet, node *tableaupb.XMLNode) error {
@@ -416,10 +469,10 @@ func preprocess(xmlSheet *tableaupb.XMLSheet, node *tableaupb.XMLNode) error {
 	return nil
 }
 
-// rearrangeAttrs change the order of attributes, e.g.: 
-// - attributes with cross-type types, such as cross-cell map (list, keyed-list, etc.), 
+// rearrangeAttrs change the order of attributes, e.g.:
+// - attributes with cross-type types, such as cross-cell map (list, keyed-list, etc.),
 //   will be placed at the first.
-// - simple list like `Param1, Param2, Param3, ...` will be grouped together and 
+// - simple list like `Param1, Param2, Param3, ...` will be grouped together and
 //   the type of `Param1` will be changed to something like `[]int32`.
 func rearrangeAttrs(attrMap *tableaupb.XMLNode_AttrMap) error {
 	typeMap := make(map[string]string)
@@ -462,17 +515,11 @@ func rearrangeAttrs(attrMap *tableaupb.XMLNode_AttrMap) error {
 // fixNodeType fix the type of `curr` in the `xmlSheet` based on its `oriType`. e.g.:
 // - map<uint32,Weight>: {Test}map<uint32,Weight>
 // - int32: {StructConf}{Weight}int32
-// - []int64: {MapConf}[]<int64>
+// - []int64: {MapConf}[]int64
+//
+// NOTE: list and keyedlist auto-deduction not supported temporarily
 func fixNodeType(xmlSheet *tableaupb.XMLSheet, curr *tableaupb.XMLNode, oriType string) (t string) {
 	t = oriType
-	if types.IsList(t) {
-		matches := types.MatchList(t)
-		colType := strings.TrimSpace(matches[2])
-		if types.IsScalarType(colType) || types.IsEnum(colType) {
-			// list in xml must be keyed list
-			t = fmt.Sprintf("[%s]<%s>", matches[1], colType)
-		}
-	}
 	// add type prefixes
 	for n, c := curr, curr; n != nil && n.Parent != nil; n, c = n.Parent, n {
 		if n == curr {
@@ -552,10 +599,10 @@ func swapAttr(attrMap *tableaupb.XMLNode_AttrMap, i, j int) {
 }
 
 // --------------------------------------------- THE THIRD PASS ------------------------------------ //
-// The third pass transforms the recursive tree structure into a 2-dimensional sheet, 
+// The third pass transforms the recursive tree structure into a 2-dimensional sheet,
 // which can be further processed into protoconf.
 //
-// genSheet generates a `book.Sheet` which can furtherly processed by `protogen`.
+// genSheet generates a `book.Sheet` which can be furtherly processed by `protogen`.
 func genSheet(xmlSheet *tableaupb.XMLSheet) (sheet *book.Sheet, err error) {
 	sheetName := xmlSheet.Meta.Name
 	header := options.NewDefault().Input.Proto.Header
@@ -579,17 +626,6 @@ func genSheet(xmlSheet *tableaupb.XMLSheet) (sheet *book.Sheet, err error) {
 	}
 	// insert sheets into map for importer
 	sheet = book.NewSheet(sheetName, rows)
-	sheet.Meta = &tableaupb.Metasheet{
-		Sheet:    sheetName,
-		Alias:    sheetName,
-		Namerow:  header.Namerow,
-		Typerow:  header.Typerow,
-		Noterow:  header.Noterow,
-		Datarow:  header.Datarow,
-		Nameline: 1,
-		Typeline: 1,
-		Nested:   true,
-	}
 	return sheet, nil
 }
 
diff --git a/internal/protogen/parser.go b/internal/protogen/parser.go
index 287846c..923161d 100644
--- a/internal/protogen/parser.go
+++ b/internal/protogen/parser.go
@@ -6,6 +6,7 @@ import (
 	"strings"
 
 	"github.com/iancoleman/strcase"
+	"github.com/tableauio/tableau/internal/fs"
 	"github.com/tableauio/tableau/internal/protogen/parseroptions"
 	"github.com/tableauio/tableau/internal/types"
 	"github.com/tableauio/tableau/log"
@@ -37,7 +38,7 @@ func newBookParser(bookName, relSlashPath string, gen *Generator) *bookParser {
 	filename := strcase.ToSnake(bookName)
 	if gen.OutputOpt.FilenameWithSubdirPrefix {
 		bookPath := filepath.Join(filepath.Dir(relSlashPath), bookName)
-		snakePath := strcase.ToSnake(bookPath)
+		snakePath := strcase.ToSnake(fs.GetCleanSlashPath(bookPath))
 		filename = strings.ReplaceAll(snakePath, "/", "__")
 	}
 	bp := &bookParser{
diff --git a/internal/protogen/protogen.go b/internal/protogen/protogen.go
index f255916..f2d3e7d 100644
--- a/internal/protogen/protogen.go
+++ b/internal/protogen/protogen.go
@@ -318,6 +318,7 @@ func (gen *Generator) convert(dir, filename string, checkProtoFileConflicts bool
 				Merger:        sheet.Meta.Merger,
 				AdjacentKey:   sheet.Meta.AdjacentKey,
 				FieldPresence: sheet.Meta.FieldPresence,
+				Template:      sheet.Meta.Template,
 				// Loader options:
 				OrderedMap: sheet.Meta.OrderedMap,
 				Index:      sheet.Meta.Index,
@@ -420,7 +421,7 @@ func (sh *sheetHeader) getNameCell(cursor int) string {
 	return getCell(sh.namerow, cursor, sh.meta.Nameline)
 }
 
-// getValidNameCell try best to get a none-empty cell, starting from 
+// getValidNameCell try best to get a none-empty cell, starting from
 // the specified cursor. Current and subsequent empty cells are skipped
 // to find the first none-empty name cell.
 func (sh *sheetHeader) getValidNameCell(cursor *int) string {
diff --git a/proto/tableau/protobuf/metabook.proto b/proto/tableau/protobuf/metabook.proto
index e23b7b7..b1bc497 100644
--- a/proto/tableau/protobuf/metabook.proto
+++ b/proto/tableau/protobuf/metabook.proto
@@ -51,6 +51,9 @@ message Metasheet {
   // with the optional label have explicit presence, like proto2 (this feature is enabled by default
   // as release 3.15). Refer: https://github.com/protocolbuffers/protobuf/blob/main/docs/field_presence.md
   bool field_presence = 15 [(tableau.field) = { name: "FieldPresence" optional: true }];
+  // declares if sheet is a template config, which only generates protobuf IDL and not generates json data.
+  // NOTE: currently only used for XML protogen.
+  bool template = 16 [(tableau.field) = { name: "Template" optional: true }];
 
   ////////// Loader related options below //////////
   // generate ordered map accessers
@@ -64,5 +67,5 @@ message Metasheet {
   // C++:
   // - const std::vector<const STRUCT_TYPE*>& Find<IndexName>(INDEX_TYPE index) const;
   // - const STRUCT_TYPE* FindFirst<IndexName>(INDEX_TYPE index);
-  repeated string index = 51 [(tableau.field) = { name: "Index" optional: true layout: LAYOUT_INCELL }];
+  repeated string index = 51 [(tableau.field) = { name: "Index" optional: true layout: LAYOUT_INCELL }];  
 }
\ No newline at end of file
diff --git a/proto/tableau/protobuf/tableau.proto b/proto/tableau/protobuf/tableau.proto
index 92de04b..84014d6 100644
--- a/proto/tableau/protobuf/tableau.proto
+++ b/proto/tableau/protobuf/tableau.proto
@@ -53,6 +53,7 @@ message WorksheetOptions {
   repeated string merger = 13;  // [default = ""]; // merge multiple sheets by the merger pattern.
   bool adjacent_key = 14;       // [default = false]; // whether the rows with the same key is adjacent together.
   bool field_presence = 15;     // [default = false]; // whether add the optional label to basic types.
+  bool template = 16; // [default = false]; // declares if sheet is a template config
 
   ////////// Loader related options below //////////
   bool ordered_map = 50;       // generate ordered map accessers.
diff --git a/proto/tableaupb/metabook.pb.go b/proto/tableaupb/metabook.pb.go
index f992d02..fffa627 100644
--- a/proto/tableaupb/metabook.pb.go
+++ b/proto/tableaupb/metabook.pb.go
@@ -107,6 +107,9 @@ type Metasheet struct {
 	// with the optional label have explicit presence, like proto2 (this feature is enabled by default
 	// as release 3.15). Refer: https://github.com/protocolbuffers/protobuf/blob/main/docs/field_presence.md
 	FieldPresence bool `protobuf:"varint,15,opt,name=field_presence,json=fieldPresence,proto3" json:"field_presence,omitempty"`
+	// declares if sheet is a template config, which only generates protobuf IDL and not generates json data.
+	// NOTE: currently only used for XML protogen.
+	Template bool `protobuf:"varint,16,opt,name=template,proto3" json:"template,omitempty"`
 	////////// Loader related options below //////////
 	// generate ordered map accessers
 	OrderedMap bool `protobuf:"varint,50,opt,name=ordered_map,json=orderedMap,proto3" json:"ordered_map,omitempty"`
@@ -259,6 +262,13 @@ func (x *Metasheet) GetFieldPresence() bool {
 	return false
 }
 
+func (x *Metasheet) GetTemplate() bool {
+	if x != nil {
+		return x.Template
+	}
+	return false
+}
+
 func (x *Metasheet) GetOrderedMap() bool {
 	if x != nil {
 		return x.OrderedMap
@@ -293,7 +303,7 @@ var file_tableau_protobuf_metabook_proto_rawDesc = []byte{
 	0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x4d,
 	0x65, 0x74, 0x61, 0x73, 0x68, 0x65, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a,
 	0x02, 0x38, 0x01, 0x3a, 0x12, 0x82, 0xb5, 0x18, 0x0e, 0x0a, 0x08, 0x40, 0x54, 0x41, 0x42, 0x4c,
-	0x45, 0x41, 0x55, 0x10, 0x01, 0x28, 0x02, 0x22, 0xf7, 0x05, 0x0a, 0x09, 0x4d, 0x65, 0x74, 0x61,
+	0x45, 0x41, 0x55, 0x10, 0x01, 0x28, 0x02, 0x22, 0xa5, 0x06, 0x0a, 0x09, 0x4d, 0x65, 0x74, 0x61,
 	0x73, 0x68, 0x65, 0x65, 0x74, 0x12, 0x21, 0x0a, 0x05, 0x73, 0x68, 0x65, 0x65, 0x74, 0x18, 0x01,
 	0x20, 0x01, 0x28, 0x09, 0x42, 0x0b, 0x82, 0xb5, 0x18, 0x07, 0x0a, 0x05, 0x53, 0x68, 0x65, 0x65,
 	0x74, 0x52, 0x05, 0x73, 0x68, 0x65, 0x65, 0x74, 0x12, 0x23, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61,
@@ -335,16 +345,19 @@ var file_tableau_protobuf_metabook_proto_rawDesc = []byte{
 	0x5f, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x42,
 	0x15, 0x82, 0xb5, 0x18, 0x11, 0x0a, 0x0d, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x50, 0x72, 0x65, 0x73,
 	0x65, 0x6e, 0x63, 0x65, 0x40, 0x01, 0x52, 0x0d, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x50, 0x72, 0x65,
-	0x73, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x33, 0x0a, 0x0b, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64,
-	0x5f, 0x6d, 0x61, 0x70, 0x18, 0x32, 0x20, 0x01, 0x28, 0x08, 0x42, 0x12, 0x82, 0xb5, 0x18, 0x0e,
-	0x0a, 0x0a, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x4d, 0x61, 0x70, 0x40, 0x01, 0x52, 0x0a,
-	0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x4d, 0x61, 0x70, 0x12, 0x25, 0x0a, 0x05, 0x69, 0x6e,
-	0x64, 0x65, 0x78, 0x18, 0x33, 0x20, 0x03, 0x28, 0x09, 0x42, 0x0f, 0x82, 0xb5, 0x18, 0x0b, 0x0a,
-	0x05, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x03, 0x40, 0x01, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65,
-	0x78, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
-	0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x69, 0x6f, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61,
-	0x75, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x70,
-	0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x73, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x2c, 0x0a, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74,
+	0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x42, 0x10, 0x82, 0xb5, 0x18, 0x0c, 0x0a, 0x08, 0x54,
+	0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x40, 0x01, 0x52, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c,
+	0x61, 0x74, 0x65, 0x12, 0x33, 0x0a, 0x0b, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x5f, 0x6d,
+	0x61, 0x70, 0x18, 0x32, 0x20, 0x01, 0x28, 0x08, 0x42, 0x12, 0x82, 0xb5, 0x18, 0x0e, 0x0a, 0x0a,
+	0x4f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x4d, 0x61, 0x70, 0x40, 0x01, 0x52, 0x0a, 0x6f, 0x72,
+	0x64, 0x65, 0x72, 0x65, 0x64, 0x4d, 0x61, 0x70, 0x12, 0x25, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65,
+	0x78, 0x18, 0x33, 0x20, 0x03, 0x28, 0x09, 0x42, 0x0f, 0x82, 0xb5, 0x18, 0x0b, 0x0a, 0x05, 0x49,
+	0x6e, 0x64, 0x65, 0x78, 0x20, 0x03, 0x40, 0x01, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x42,
+	0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x61,
+	0x62, 0x6c, 0x65, 0x61, 0x75, 0x69, 0x6f, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2f,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x70, 0x62, 0x62,
+	0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
diff --git a/proto/tableaupb/tableau.pb.go b/proto/tableaupb/tableau.pb.go
index 0e8a936..0e1b6ad 100644
--- a/proto/tableaupb/tableau.pb.go
+++ b/proto/tableaupb/tableau.pb.go
@@ -203,6 +203,7 @@ type WorksheetOptions struct {
 	Merger        []string `protobuf:"bytes,13,rep,name=merger,proto3" json:"merger,omitempty"`                                     // [default = ""]; // merge multiple sheets by the merger pattern.
 	AdjacentKey   bool     `protobuf:"varint,14,opt,name=adjacent_key,json=adjacentKey,proto3" json:"adjacent_key,omitempty"`       // [default = false]; // whether the rows with the same key is adjacent together.
 	FieldPresence bool     `protobuf:"varint,15,opt,name=field_presence,json=fieldPresence,proto3" json:"field_presence,omitempty"` // [default = false]; // whether add the optional label to basic types.
+	Template      bool     `protobuf:"varint,16,opt,name=template,proto3" json:"template,omitempty"`                                // [default = false]; // declares if sheet is a template config
 	////////// Loader related options below //////////
 	OrderedMap bool     `protobuf:"varint,50,opt,name=ordered_map,json=orderedMap,proto3" json:"ordered_map,omitempty"` // generate ordered map accessers.
 	Index      []string `protobuf:"bytes,51,rep,name=index,proto3" json:"index,omitempty"`                              // generate index accessers.
@@ -345,6 +346,13 @@ func (x *WorksheetOptions) GetFieldPresence() bool {
 	return false
 }
 
+func (x *WorksheetOptions) GetTemplate() bool {
+	if x != nil {
+		return x.Template
+	}
+	return false
+}
+
 func (x *WorksheetOptions) GetOrderedMap() bool {
 	if x != nil {
 		return x.OrderedMap
@@ -483,8 +491,15 @@ type FieldProp struct {
 	Range string `protobuf:"bytes,1,opt,name=range,proto3" json:"range,omitempty"`
 	// Local perspective: whether the map key's value is unique.
 	Unique bool `protobuf:"varint,2,opt,name=unique,proto3" json:"unique,omitempty"`
-	// Global perspective: ensure this field is in another message's field range.
-	// E.g: "XXXConf.ID".
+	// Global perspective: ensure this field value is in another sheet's
+	// column value space (aka message's field value space).
+	//
+	// Format: "SheetName(SheetAlias).ColumnName"
+	//
+	// E.g:
+	//  - "Item.ID" : sheet name is unique.
+	//  - "Item(ItemConf).ID" : different workbooks have the same sheet name,
+	//    but sheet alias is unique inherently.
 	Refer string `protobuf:"bytes,3,opt,name=refer,proto3" json:"refer,omitempty"`
 	// Local perspective: ensure this field's value is a sequence and begins with this value.
 	// Effect: key of map or keyed list.
@@ -758,7 +773,7 @@ var file_tableau_protobuf_tableau_proto_rawDesc = []byte{
 	0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x25, 0x0a, 0x0f, 0x57,
 	0x6f, 0x72, 0x6b, 0x62, 0x6f, 0x6f, 0x6b, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12,
 	0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
-	0x6d, 0x65, 0x22, 0xd3, 0x03, 0x0a, 0x10, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x68, 0x65, 0x65, 0x74,
+	0x6d, 0x65, 0x22, 0xef, 0x03, 0x0a, 0x10, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x68, 0x65, 0x65, 0x74,
 	0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
 	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6e,
 	0x61, 0x6d, 0x65, 0x72, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x6e, 0x61,
@@ -784,83 +799,85 @@ var file_tableau_protobuf_tableau_proto_rawDesc = []byte{
 	0x64, 0x6a, 0x61, 0x63, 0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x66, 0x69,
 	0x65, 0x6c, 0x64, 0x5f, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x0f, 0x20, 0x01,
 	0x28, 0x08, 0x52, 0x0d, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63,
-	0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x5f, 0x6d, 0x61, 0x70,
-	0x18, 0x32, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x4d,
-	0x61, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x33, 0x20, 0x03, 0x28,
-	0x09, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x82, 0x02, 0x0a, 0x0c, 0x46, 0x69, 0x65,
-	0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
-	0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a,
-	0x04, 0x6e, 0x6f, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x6f, 0x74,
-	0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
-	0x6b, 0x65, 0x79, 0x12, 0x27, 0x0a, 0x06, 0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x18, 0x04, 0x20,
-	0x01, 0x28, 0x0e, 0x32, 0x0f, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x4c, 0x61,
-	0x79, 0x6f, 0x75, 0x74, 0x52, 0x06, 0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x12, 0x21, 0x0a, 0x04,
-	0x73, 0x70, 0x61, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x74, 0x61, 0x62,
-	0x6c, 0x65, 0x61, 0x75, 0x2e, 0x53, 0x70, 0x61, 0x6e, 0x52, 0x04, 0x73, 0x70, 0x61, 0x6e, 0x12,
-	0x10, 0x0a, 0x03, 0x73, 0x65, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x65,
-	0x70, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x75, 0x62, 0x73, 0x65, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x06, 0x73, 0x75, 0x62, 0x73, 0x65, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x70, 0x74,
-	0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6f, 0x70, 0x74,
-	0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x12, 0x26, 0x0a, 0x04, 0x70, 0x72, 0x6f, 0x70, 0x18, 0x0f, 0x20,
-	0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x46, 0x69,
-	0x65, 0x6c, 0x64, 0x50, 0x72, 0x6f, 0x70, 0x52, 0x04, 0x70, 0x72, 0x6f, 0x70, 0x22, 0xc1, 0x01,
-	0x0a, 0x09, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x50, 0x72, 0x6f, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x72,
-	0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67,
-	0x65, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
-	0x08, 0x52, 0x06, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x65, 0x66,
-	0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x65, 0x66, 0x65, 0x72, 0x12,
-	0x1f, 0x0a, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28,
-	0x03, 0x48, 0x00, 0x52, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x88, 0x01, 0x01,
-	0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69,
-	0x78, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x69, 0x78, 0x65, 0x64,
-	0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04,
-	0x73, 0x69, 0x7a, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63,
-	0x65, 0x22, 0x21, 0x0a, 0x0b, 0x45, 0x6e, 0x75, 0x6d, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
-	0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
-	0x6e, 0x61, 0x6d, 0x65, 0x22, 0x26, 0x0a, 0x10, 0x45, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75,
-	0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
-	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x2a, 0x5b, 0x0a, 0x06,
-	0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x12, 0x12, 0x0a, 0x0e, 0x4c, 0x41, 0x59, 0x4f, 0x55, 0x54,
-	0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x41,
-	0x59, 0x4f, 0x55, 0x54, 0x5f, 0x56, 0x45, 0x52, 0x54, 0x49, 0x43, 0x41, 0x4c, 0x10, 0x01, 0x12,
-	0x15, 0x0a, 0x11, 0x4c, 0x41, 0x59, 0x4f, 0x55, 0x54, 0x5f, 0x48, 0x4f, 0x52, 0x49, 0x5a, 0x4f,
-	0x4e, 0x54, 0x41, 0x4c, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x4c, 0x41, 0x59, 0x4f, 0x55, 0x54,
-	0x5f, 0x49, 0x4e, 0x43, 0x45, 0x4c, 0x4c, 0x10, 0x03, 0x2a, 0x42, 0x0a, 0x04, 0x53, 0x70, 0x61,
-	0x6e, 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x50, 0x41, 0x4e, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c,
-	0x54, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x50, 0x41, 0x4e, 0x5f, 0x43, 0x52, 0x4f, 0x53,
-	0x53, 0x5f, 0x43, 0x45, 0x4c, 0x4c, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x50, 0x41, 0x4e,
-	0x5f, 0x49, 0x4e, 0x4e, 0x45, 0x52, 0x5f, 0x43, 0x45, 0x4c, 0x4c, 0x10, 0x02, 0x3a, 0x54, 0x0a,
-	0x08, 0x77, 0x6f, 0x72, 0x6b, 0x62, 0x6f, 0x6f, 0x6b, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
-	0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x6c, 0x65,
-	0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd0, 0x86, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32,
-	0x18, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x62, 0x6f,
-	0x6f, 0x6b, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x62,
-	0x6f, 0x6f, 0x6b, 0x3a, 0x5a, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x68, 0x65, 0x65, 0x74,
-	0x12, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
-	0x75, 0x66, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
-	0x73, 0x18, 0xd0, 0x86, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x61, 0x62, 0x6c,
-	0x65, 0x61, 0x75, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x68, 0x65, 0x65, 0x74, 0x4f, 0x70, 0x74,
-	0x69, 0x6f, 0x6e, 0x73, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x68, 0x65, 0x65, 0x74, 0x3a,
-	0x4c, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
-	0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64,
-	0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd0, 0x86, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32,
-	0x15, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f,
-	0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x3a, 0x48, 0x0a,
-	0x04, 0x65, 0x6e, 0x75, 0x6d, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
-	0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x4f, 0x70, 0x74, 0x69,
-	0x6f, 0x6e, 0x73, 0x18, 0xd0, 0x86, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x61,
-	0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
-	0x73, 0x52, 0x04, 0x65, 0x6e, 0x75, 0x6d, 0x3a, 0x56, 0x0a, 0x06, 0x65, 0x76, 0x61, 0x6c, 0x75,
-	0x65, 0x12, 0x21, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-	0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74,
-	0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd0, 0x86, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74,
-	0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65,
-	0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x06, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x42,
-	0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x61,
-	0x62, 0x6c, 0x65, 0x61, 0x75, 0x69, 0x6f, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2f,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x70, 0x62, 0x62,
-	0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x65, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x18, 0x10, 0x20,
+	0x01, 0x28, 0x08, 0x52, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x1f, 0x0a,
+	0x0b, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x32, 0x20, 0x01,
+	0x28, 0x08, 0x52, 0x0a, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x4d, 0x61, 0x70, 0x12, 0x14,
+	0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x33, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x69,
+	0x6e, 0x64, 0x65, 0x78, 0x22, 0x82, 0x02, 0x0a, 0x0c, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70,
+	0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x6f, 0x74,
+	0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x12, 0x10, 0x0a,
+	0x03, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
+	0x27, 0x0a, 0x06, 0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32,
+	0x0f, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74,
+	0x52, 0x06, 0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x12, 0x21, 0x0a, 0x04, 0x73, 0x70, 0x61, 0x6e,
+	0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75,
+	0x2e, 0x53, 0x70, 0x61, 0x6e, 0x52, 0x04, 0x73, 0x70, 0x61, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x73,
+	0x65, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x65, 0x70, 0x12, 0x16, 0x0a,
+	0x06, 0x73, 0x75, 0x62, 0x73, 0x65, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73,
+	0x75, 0x62, 0x73, 0x65, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61,
+	0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61,
+	0x6c, 0x12, 0x26, 0x0a, 0x04, 0x70, 0x72, 0x6f, 0x70, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32,
+	0x12, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x50,
+	0x72, 0x6f, 0x70, 0x52, 0x04, 0x70, 0x72, 0x6f, 0x70, 0x22, 0xc1, 0x01, 0x0a, 0x09, 0x46, 0x69,
+	0x65, 0x6c, 0x64, 0x50, 0x72, 0x6f, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x16, 0x0a,
+	0x06, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x75,
+	0x6e, 0x69, 0x71, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x65, 0x66, 0x65, 0x72, 0x18, 0x03,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x65, 0x66, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x08, 0x73,
+	0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52,
+	0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x88, 0x01, 0x01, 0x12, 0x18, 0x0a, 0x07,
+	0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64,
+	0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x78, 0x65, 0x64, 0x18,
+	0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x69, 0x78, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04,
+	0x73, 0x69, 0x7a, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65,
+	0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x22, 0x21, 0x0a,
+	0x0b, 0x45, 0x6e, 0x75, 0x6d, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04,
+	0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
+	0x22, 0x26, 0x0a, 0x10, 0x45, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74,
+	0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x2a, 0x5b, 0x0a, 0x06, 0x4c, 0x61, 0x79, 0x6f,
+	0x75, 0x74, 0x12, 0x12, 0x0a, 0x0e, 0x4c, 0x41, 0x59, 0x4f, 0x55, 0x54, 0x5f, 0x44, 0x45, 0x46,
+	0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x41, 0x59, 0x4f, 0x55, 0x54,
+	0x5f, 0x56, 0x45, 0x52, 0x54, 0x49, 0x43, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x4c,
+	0x41, 0x59, 0x4f, 0x55, 0x54, 0x5f, 0x48, 0x4f, 0x52, 0x49, 0x5a, 0x4f, 0x4e, 0x54, 0x41, 0x4c,
+	0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x4c, 0x41, 0x59, 0x4f, 0x55, 0x54, 0x5f, 0x49, 0x4e, 0x43,
+	0x45, 0x4c, 0x4c, 0x10, 0x03, 0x2a, 0x42, 0x0a, 0x04, 0x53, 0x70, 0x61, 0x6e, 0x12, 0x10, 0x0a,
+	0x0c, 0x53, 0x50, 0x41, 0x4e, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12,
+	0x13, 0x0a, 0x0f, 0x53, 0x50, 0x41, 0x4e, 0x5f, 0x43, 0x52, 0x4f, 0x53, 0x53, 0x5f, 0x43, 0x45,
+	0x4c, 0x4c, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x50, 0x41, 0x4e, 0x5f, 0x49, 0x4e, 0x4e,
+	0x45, 0x52, 0x5f, 0x43, 0x45, 0x4c, 0x4c, 0x10, 0x02, 0x3a, 0x54, 0x0a, 0x08, 0x77, 0x6f, 0x72,
+	0x6b, 0x62, 0x6f, 0x6f, 0x6b, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4f, 0x70, 0x74, 0x69,
+	0x6f, 0x6e, 0x73, 0x18, 0xd0, 0x86, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x61,
+	0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x62, 0x6f, 0x6f, 0x6b, 0x4f, 0x70,
+	0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x62, 0x6f, 0x6f, 0x6b, 0x3a,
+	0x5a, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x68, 0x65, 0x65, 0x74, 0x12, 0x1f, 0x2e, 0x67,
+	0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d,
+	0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd0, 0x86,
+	0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e,
+	0x57, 0x6f, 0x72, 0x6b, 0x73, 0x68, 0x65, 0x65, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+	0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x68, 0x65, 0x65, 0x74, 0x3a, 0x4c, 0x0a, 0x05, 0x66,
+	0x69, 0x65, 0x6c, 0x64, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69,
+	0x6f, 0x6e, 0x73, 0x18, 0xd0, 0x86, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x61,
+	0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f,
+	0x6e, 0x73, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x3a, 0x48, 0x0a, 0x04, 0x65, 0x6e, 0x75,
+	0x6d, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
+	0xd0, 0x86, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61,
+	0x75, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x04, 0x65,
+	0x6e, 0x75, 0x6d, 0x3a, 0x56, 0x0a, 0x06, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x21, 0x2e,
+	0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
+	0x45, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+	0x18, 0xd0, 0x86, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65,
+	0x61, 0x75, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74, 0x69,
+	0x6f, 0x6e, 0x73, 0x52, 0x06, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x2e, 0x5a, 0x2c, 0x67,
+	0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61,
+	0x75, 0x69, 0x6f, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2f, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x33,
 }
 
 var (


Test patch:
diff --git a/internal/importer/xml_test.go b/internal/importer/xml_test.go
index 28986f5..8edbc5a 100644
--- a/internal/importer/xml_test.go
+++ b/internal/importer/xml_test.go
@@ -6,6 +6,7 @@ import (
 	"testing"
 
 	"github.com/antchfx/xmlquery"
+	"github.com/tableauio/tableau/internal/importer/book"
 	"github.com/tableauio/tableau/proto/tableaupb"
 )
 
@@ -88,7 +89,10 @@ func FindMetaNode(xmlSheet *tableaupb.XMLSheet, path string) *tableaupb.XMLNode
 func Test_isRepeated(t *testing.T) {
 	doc := `
 <?xml version='1.0' encoding='UTF-8'?>
-<!-- @TABLEAU -->
+<!-- 
+<@TABLEAU />
+-->
+
 <MatchCfg Open="true">
 	<TeamRatingWeight>
 		<Weight Num="1">
@@ -102,7 +106,8 @@ func Test_isRepeated(t *testing.T) {
 </MatchCfg>
 `
 	root, _ := xmlquery.Parse(strings.NewReader(doc))
-	xmlMeta, _, _ := readXMLFile(root, nil)
+	newBook := book.NewBook(`Test.xml`, `Test.xml`, nil)
+	xmlMeta, _ := readXMLFile(root, newBook)
 	sheet1 := getXMLSheet(xmlMeta, "MatchCfg")
 	node1 := FindMetaNode(sheet1, "MatchCfg/TeamRatingWeight/Weight")
 	node2 := FindMetaNode(sheet1, "MatchCfg/TeamRatingWeight/Weight/Param")
@@ -203,7 +208,11 @@ func Test_matchAttr(t *testing.T) {
 func Test_isFirstChild(t *testing.T) {
 	doc := `
 <?xml version='1.0' encoding='UTF-8'?>
-<!-- @TABLEAU
+<!--
+<@TABLEAU>
+	<Item Sheet="Server" />
+</@TABLEAU>
+
 <Server>
     <MapConf>
         <Weight Num="map&lt;uint32,Weight&gt;"/>
@@ -212,7 +221,8 @@ func Test_isFirstChild(t *testing.T) {
 -->
 `
 	root, _ := xmlquery.Parse(strings.NewReader(doc))
-	xmlMeta, _, _ := readXMLFile(root, nil)
+	newBook := book.NewBook(`Test.xml`, `Test.xml`, nil)
+	xmlMeta, _ := readXMLFile(root, newBook)
 	sheet1 := getXMLSheet(xmlMeta, "Server")
 	node1 := FindMetaNode(sheet1, "Server/MapConf/Weight")
 	type args struct {
@@ -244,7 +254,11 @@ func Test_isFirstChild(t *testing.T) {
 func Test_fixNodeType(t *testing.T) {
 	doc := `
 <?xml version='1.0' encoding='UTF-8'?>
-<!-- @TABLEAU
+<!--
+<@TABLEAU>
+	<Item Sheet="MatchCfg" />
+</@TABLEAU>
+
 <MatchCfg open="true">
 	<MatchMode MissionType="map&lt;enum&lt;.MissionType&gt;,MatchMode&gt;">
 		<MatchAI IsOpen="bool" PlayerOnlyOneCamp="bool">
@@ -292,7 +306,8 @@ func Test_fixNodeType(t *testing.T) {
 </MatchCfg>
 `
 	root, _ := xmlquery.Parse(strings.NewReader(doc))
-	xmlMeta, _, _ := readXMLFile(root, nil)
+	newBook := book.NewBook(`Test.xml`, `Test.xml`, nil)
+	xmlMeta, _ := readXMLFile(root, newBook)
 	sheet1 := getXMLSheet(xmlMeta, "MatchCfg")
 	node1 := FindMetaNode(sheet1, "MatchCfg/MatchMode/MatchAI/AI")
 	node2 := FindMetaNode(sheet1, "MatchCfg/MapConf/Test/Weight")
@@ -394,7 +409,7 @@ func Test_fixNodeType(t *testing.T) {
 				curr:     node9,
 				oriType:  `[]int64`,
 			},
-			want: `{MapConf}[]<int64>`,
+			want: `{MapConf}[]int64`,
 		},
 		{
 			name: "MatchCfg/Broadcast/Content@txt",
diff --git a/test/dev/proto/feature_toggle_conf.proto b/test/dev/proto/feature_toggle_conf.proto
index 29f56e1..dacef7d 100644
--- a/test/dev/proto/feature_toggle_conf.proto
+++ b/test/dev/proto/feature_toggle_conf.proto
@@ -12,15 +12,15 @@ option go_package = "github.com/tableauio/tableau/test/dev/protoconf";
 option (tableau.workbook) = {name:"xml/FeatureToggle.xml"};
 
 message ToggleCfg {
-  option (tableau.worksheet) = {name:"ToggleCfg" namerow:1 typerow:2 noterow:3 datarow:4 nameline:1 typeline:1 nested:true};
+  option (tableau.worksheet) = {name:"ToggleCfg" namerow:1 typerow:2 noterow:3 datarow:5 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}];
+    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"}];
+      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"}}];
@@ -28,7 +28,7 @@ message ToggleCfg {
       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"}];
+      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/match_conf.proto b/test/dev/proto/match_conf.proto
index ac77ea9..13c6d75 100644
--- a/test/dev/proto/match_conf.proto
+++ b/test/dev/proto/match_conf.proto
@@ -13,7 +13,7 @@ option go_package = "github.com/tableauio/tableau/test/dev/protoconf";
 option (tableau.workbook) = {name:"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};
+  option (tableau.worksheet) = {name:"MatchCfg" namerow:1 typerow:2 noterow:3 datarow:5 nameline:1 typeline:1 nested:true};
 
   ConfirmTimeOutPunish confirm_time_out_punish = 1 [(tableau.field) = {name:"ConfirmTimeOutPunish"}];
   message ConfirmTimeOutPunish {
diff --git a/test/dev/proto/multi_sheet_conf.proto b/test/dev/proto/multi_sheet_conf.proto
index 62ceef6..5f51e2c 100644
--- a/test/dev/proto/multi_sheet_conf.proto
+++ b/test/dev/proto/multi_sheet_conf.proto
@@ -10,12 +10,12 @@ option go_package = "github.com/tableauio/tableau/test/dev/protoconf";
 option (tableau.workbook) = {name:"xml/MultiSheet.xml"};
 
 message Server {
-  option (tableau.worksheet) = {name:"Server" namerow:1 typerow:2 noterow:3 datarow:4 nameline:1 typeline:1 nested:true};
+  option (tableau.worksheet) = {name:"Server" namerow:1 typerow:2 noterow:3 datarow:5 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" key:"Param" layout:LAYOUT_INCELL}];
+    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 {
@@ -56,7 +56,7 @@ message Server {
 }
 
 message Client {
-  option (tableau.worksheet) = {name:"Client" namerow:1 typerow:2 noterow:3 datarow:4 nameline:1 typeline:1 nested:true};
+  option (tableau.worksheet) = {name:"Client" namerow:1 typerow:2 noterow:3 datarow:5 nameline:1 typeline:1 nested:true};
 
   Version version = 1 [(tableau.field) = {name:"Version"}];
   message Version {
diff --git a/test/dev/proto/simple_match_conf.proto b/test/dev/proto/simple_match_conf.proto
index 6cdcc59..7005bce 100644
--- a/test/dev/proto/simple_match_conf.proto
+++ b/test/dev/proto/simple_match_conf.proto
@@ -10,25 +10,6 @@ option go_package = "github.com/tableauio/tableau/test/dev/protoconf";
 option (tableau.workbook) = {name:"xml/match/SimpleMatch.xml"};
 
 message SimpleMatchConf {
-  option (tableau.worksheet) = {name:"SimpleMatchConf" namerow:1 typerow:2 noterow:3 datarow:4 nameline:1 typeline:1 nested:true};
+  option (tableau.worksheet) = {name:"SimpleMatchConf" namerow:1 typerow:2 noterow:3 datarow:5 nameline:2 typeline:2};
 
-  ConfirmTimeOutPunish confirm_time_out_punish = 1 [(tableau.field) = {name:"ConfirmTimeOutPunish"}];
-  message ConfirmTimeOutPunish {
-    string time = 1 [(tableau.field) = {name:"Time"}];
-  }
-  MatchRoom match_room = 2 [(tableau.field) = {name:"MatchRoom"}];
-  message MatchRoom {
-    int32 max_loop = 1 [(tableau.field) = {name:"MaxLoop"}];
-  }
-  TeamRatingWeight team_rating_weight = 3 [(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/testdata/xml/FeatureToggle.xml b/test/dev/testdata/xml/FeatureToggle.xml
index 8d211aa..a3e8800 100644
--- a/test/dev/testdata/xml/FeatureToggle.xml
+++ b/test/dev/testdata/xml/FeatureToggle.xml
@@ -1,5 +1,9 @@
 <?xml version='1.0' encoding='UTF-8'?>
-<!-- @TABLEAU
+<!--
+<@TABLEAU>
+    <Item Sheet="ToggleCfg" />
+</@TABLEAU>
+    
 <ToggleCfg>
     <FeatureToggle EnvID="map<uint32,FeatureToggle>">
         <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"/>
@@ -8,7 +12,7 @@
 -->
 
 <ToggleCfg>
-    <FeatureToggle EnvID="0" desc="common: 通用环境的配置，需要包含全量的开关。common没有且对应环境也没有的开关会被默认为关闭。">
+    <FeatureToggle EnvID="0" Desc="common: 通用环境的配置，需要包含全量的开关。common没有且对应环境也没有的开关会被默认为关闭。">
         <!--
         - ID				系统对应的toggleid
         - OpenRate			开放比例[0,10000], 0表示关闭，10000全开
@@ -24,14 +28,14 @@
         <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_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_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的配置。">
+    <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" />
@@ -44,4 +48,4 @@
         <Toggle name="公告(包括:登录前公告+登录后公告)" ID="TOGGLE_ANNOUNCEMENT" OpenRate="0" />
         <Toggle name="活动入口111" ID="TOGGLE_ACTIVITY_ENTRY" OpenRate="0" />
     </FeatureToggle>
-</ToggleCfg>
\ No newline at end of file
+</ToggleCfg>
diff --git a/test/dev/testdata/xml/MultiSheet.xml b/test/dev/testdata/xml/MultiSheet.xml
index 9362773..ebb1ef8 100644
--- a/test/dev/testdata/xml/MultiSheet.xml
+++ b/test/dev/testdata/xml/MultiSheet.xml
@@ -1,5 +1,10 @@
 <?xml version='1.0' encoding='UTF-8'?>
-<!-- @TABLEAU
+<!--
+<@TABLEAU>
+    <Item Sheet="Server" />
+    <Item Sheet="Client" />
+</@TABLEAU>
+    
 <Server>
     <MapConf Param="[]int64" TypeNum="map<int32, int32>">
         <Weight Num="map<int32,Weight>"/>
@@ -8,7 +13,7 @@
 <Client/>
 -->
 
-<Server desc="test">
+<Server Desc="test">
     <Node Addr="9.123.134.1" ProcNum="2"/>
     <Node Addr="9.168.134.1" ProcNum="5"/>
 
diff --git a/test/dev/testdata/xml/match/Match.xml b/test/dev/testdata/xml/match/Match.xml
index 0e60588..9c3e25b 100644
--- a/test/dev/testdata/xml/match/Match.xml
+++ b/test/dev/testdata/xml/match/Match.xml
@@ -1,5 +1,9 @@
 <?xml version='1.0' encoding='UTF-8'?>
-<!-- @TABLEAU
+<!--
+<@TABLEAU>
+    <Item Sheet="MatchCfg" />
+</@TABLEAU>
+    
 <MatchCfg>
     <ConfirmTimeOutPunish Time="duration" />
     <MatchStatInfo ReportInterval="duration"/>
@@ -37,33 +41,33 @@
         </Weight>
     </TeamRatingWeight>
 
-    <MatchMode MissionType="MT_PVP_Low_Rank_RoundBattle_1V1" desc="排位赛">
+    <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="路由哈希值" />
+        <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" />
+            <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">
+    <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="荣耀战力匹配过滤器">
+        <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"/>
@@ -73,12 +77,12 @@
                 <Param Value="300"/>
             </Power>
         </Filter>
-        <Route Key="1" 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_TRAIN" IsOpen="false" MinTime="5s" MaxTime="15s" desc="训练玩家AI" />
+            <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/match/SimpleMatch.xml b/test/dev/testdata/xml/match/SimpleMatch.xml
index 4569431..651a197 100644
--- a/test/dev/testdata/xml/match/SimpleMatch.xml
+++ b/test/dev/testdata/xml/match/SimpleMatch.xml
@@ -1,5 +1,7 @@
 <?xml version='1.0' encoding='UTF-8'?>
-<!-- @TABLEAU -->
+<!--
+<@TABLEAU />
+-->
 
 <SimpleMatchConf>
     <!-- 匹配确认超时惩罚时间 -->
diff --git a/test/functest/conf/CrossCellStruct.json b/test/functest/conf/CrossCellStruct.json
index 6d823a1..8f98c71 100644
--- a/test/functest/conf/CrossCellStruct.json
+++ b/test/functest/conf/CrossCellStruct.json
@@ -1,7 +1,7 @@
 {
-    "item": {
-        "id": 1,
-        "name": "Orange",
-        "desc": "A kind of sour fruit."
+    "item":  {
+        "id":  1,
+        "name":  "Orange",
+        "desc":  "A kind of sour fruit."
     }
 }
\ No newline at end of file
diff --git a/test/functest/conf/EmptyKeyMap.json b/test/functest/conf/EmptyKeyMap.json
index 6764099..5808836 100644
--- a/test/functest/conf/EmptyKeyMap.json
+++ b/test/functest/conf/EmptyKeyMap.json
@@ -1,16 +1,16 @@
 {
-    "itemMap": {
-        "0": {
-            "id": 0,
-            "desc": "Orange"
+    "itemMap":  {
+        "0":  {
+            "id":  0,
+            "desc":  "Orange"
         },
-        "1": {
-            "id": 1,
-            "desc": "Apple"
+        "1":  {
+            "id":  1,
+            "desc":  "Apple"
         },
-        "3": {
-            "id": 3,
-            "desc": "Banana"
+        "3":  {
+            "id":  3,
+            "desc":  "Banana"
         }
     }
 }
\ No newline at end of file
diff --git a/test/functest/conf/EnumKeyMap.json b/test/functest/conf/EnumKeyMap.json
index 728d95c..01e7a74 100644
--- a/test/functest/conf/EnumKeyMap.json
+++ b/test/functest/conf/EnumKeyMap.json
@@ -1,16 +1,16 @@
 {
-    "itemMap": {
-        "1": {
-            "type": "FRUIT_TYPE_APPLE",
-            "price": 100
+    "itemMap":  {
+        "1":  {
+            "type":  "FRUIT_TYPE_APPLE",
+            "price":  100
         },
-        "3": {
-            "type": "FRUIT_TYPE_ORANGE",
-            "price": 200
+        "3":  {
+            "type":  "FRUIT_TYPE_ORANGE",
+            "price":  200
         },
-        "4": {
-            "type": "FRUIT_TYPE_BANANA",
-            "price": 300
+        "4":  {
+            "type":  "FRUIT_TYPE_BANANA",
+            "price":  300
         }
     }
 }
\ No newline at end of file
diff --git a/test/functest/conf/HPredefStructListInHListFF.json b/test/functest/conf/HPredefStructListInHListFF.json
index e433240..67dd32f 100644
--- a/test/functest/conf/HPredefStructListInHListFF.json
+++ b/test/functest/conf/HPredefStructListInHListFF.json
@@ -1,26 +1,26 @@
 {
-    "rewardList": [
+    "rewardList":  [
         {
-            "itemList": [
+            "itemList":  [
                 {
-                    "id": 1,
-                    "num": 10
+                    "id":  1,
+                    "num":  10
                 },
                 {
-                    "id": 2,
-                    "num": 20
+                    "id":  2,
+                    "num":  20
                 }
             ],
-            "name": "Lotto"
+            "name":  "Lotto"
         },
         {
-            "itemList": [
+            "itemList":  [
                 {
-                    "id": 10,
-                    "num": 100
+                    "id":  10,
+                    "num":  100
                 }
             ],
-            "name": "Super Lotto"
+            "name":  "Super Lotto"
         }
     ]
 }
\ No newline at end of file
diff --git a/test/functest/conf/HPredefStructMapInHListFF.json b/test/functest/conf/HPredefStructMapInHListFF.json
index 6b52da4..c0333a1 100644
--- a/test/functest/conf/HPredefStructMapInHListFF.json
+++ b/test/functest/conf/HPredefStructMapInHListFF.json
@@ -1,26 +1,26 @@
 {
-    "rewardList": [
+    "rewardList":  [
         {
-            "itemMap": {
-                "1": {
-                    "id": 1,
-                    "num": 10
+            "itemMap":  {
+                "1":  {
+                    "id":  1,
+                    "num":  10
                 },
-                "2": {
-                    "id": 2,
-                    "num": 20
+                "2":  {
+                    "id":  2,
+                    "num":  20
                 }
             },
-            "name": "Lotto"
+            "name":  "Lotto"
         },
         {
-            "itemMap": {
-                "10": {
-                    "id": 10,
-                    "num": 100
+            "itemMap":  {
+                "10":  {
+                    "id":  10,
+                    "num":  100
                 }
             },
-            "name": "Super Lotto"
+            "name":  "Super Lotto"
         }
     ]
 }
\ No newline at end of file
diff --git a/test/functest/conf/HorizontalColumnSkippedList.json b/test/functest/conf/HorizontalColumnSkippedList.json
index b5f9c26..005caa1 100644
--- a/test/functest/conf/HorizontalColumnSkippedList.json
+++ b/test/functest/conf/HorizontalColumnSkippedList.json
@@ -1,37 +1,37 @@
 {
-    "itemMap": {
-        "1": {
-            "id": 1,
-            "propList": [
+    "itemMap":  {
+        "1":  {
+            "id":  1,
+            "propList":  [
                 {
-                    "id": 1,
-                    "value": 100
+                    "id":  1,
+                    "value":  100
                 },
                 {
-                    "id": 2,
-                    "value": 200
+                    "id":  2,
+                    "value":  200
                 }
             ]
         },
-        "2": {
-            "id": 2,
-            "propList": [
+        "2":  {
+            "id":  2,
+            "propList":  [
                 {
-                    "id": 3,
-                    "value": 300
+                    "id":  3,
+                    "value":  300
                 },
                 {
-                    "id": 4,
-                    "value": 400
+                    "id":  4,
+                    "value":  400
                 }
             ]
         },
-        "3": {
-            "id": 3,
-            "propList": [
+        "3":  {
+            "id":  3,
+            "propList":  [
                 {
-                    "id": 5,
-                    "value": 500
+                    "id":  5,
+                    "value":  500
                 }
             ]
         }
diff --git a/test/functest/conf/HorizontalColumnSkippedMap.json b/test/functest/conf/HorizontalColumnSkippedMap.json
index 3d100d0..e755d6a 100644
--- a/test/functest/conf/HorizontalColumnSkippedMap.json
+++ b/test/functest/conf/HorizontalColumnSkippedMap.json
@@ -1,37 +1,37 @@
 {
-    "itemMap": {
-        "1": {
-            "id": 1,
-            "propMap": {
-                "1": {
-                    "id": 1,
-                    "value": 100
+    "itemMap":  {
+        "1":  {
+            "id":  1,
+            "propMap":  {
+                "1":  {
+                    "id":  1,
+                    "value":  100
                 },
-                "2": {
-                    "id": 2,
-                    "value": 200
+                "2":  {
+                    "id":  2,
+                    "value":  200
                 }
             }
         },
-        "2": {
-            "id": 2,
-            "propMap": {
-                "3": {
-                    "id": 3,
-                    "value": 300
+        "2":  {
+            "id":  2,
+            "propMap":  {
+                "3":  {
+                    "id":  3,
+                    "value":  300
                 },
-                "4": {
-                    "id": 4,
-                    "value": 400
+                "4":  {
+                    "id":  4,
+                    "value":  400
                 }
             }
         },
-        "3": {
-            "id": 3,
-            "propMap": {
-                "5": {
-                    "id": 5,
-                    "value": 500
+        "3":  {
+            "id":  3,
+            "propMap":  {
+                "5":  {
+                    "id":  5,
+                    "value":  500
                 }
             }
         }
diff --git a/test/functest/conf/HorizontalEnumList.json b/test/functest/conf/HorizontalEnumList.json
index d1ccbad..e55f415 100644
--- a/test/functest/conf/HorizontalEnumList.json
+++ b/test/functest/conf/HorizontalEnumList.json
@@ -1,5 +1,5 @@
 {
-    "paramList": [
+    "paramList":  [
         "FRUIT_TYPE_APPLE",
         "FRUIT_TYPE_ORANGE",
         "FRUIT_TYPE_BANANA"
diff --git a/test/functest/conf/HorizontalExplicitFixedSizeList.json b/test/functest/conf/HorizontalExplicitFixedSizeList.json
index 9c4e177..01535ea 100644
--- a/test/functest/conf/HorizontalExplicitFixedSizeList.json
+++ b/test/functest/conf/HorizontalExplicitFixedSizeList.json
@@ -1,12 +1,12 @@
 {
-    "itemList": [
+    "itemList":  [
         {
-            "id": 1,
-            "name": "Apple"
+            "id":  1,
+            "name":  "Apple"
         },
         {
-            "id": 0,
-            "name": ""
+            "id":  0,
+            "name":  ""
         }
     ]
 }
\ No newline at end of file
diff --git a/test/functest/conf/HorizontalExplicitFixedSizeMap.json b/test/functest/conf/HorizontalExplicitFixedSizeMap.json
index 51aad55..88133d7 100644
--- a/test/functest/conf/HorizontalExplicitFixedSizeMap.json
+++ b/test/functest/conf/HorizontalExplicitFixedSizeMap.json
@@ -1,8 +1,8 @@
 {
-    "itemMap": {
-        "1": {
-            "id": 1,
-            "name": "Apple"
+    "itemMap":  {
+        "1":  {
+            "id":  1,
+            "name":  "Apple"
         }
     }
 }
\ No newline at end of file
diff --git a/test/functest/conf/HorizontalImplicitFixedSizeList.json b/test/functest/conf/HorizontalImplicitFixedSizeList.json
index d773dc6..b2ca103 100644
--- a/test/functest/conf/HorizontalImplicitFixedSizeList.json
+++ b/test/functest/conf/HorizontalImplicitFixedSizeList.json
@@ -1,16 +1,16 @@
 {
-    "itemList": [
+    "itemList":  [
         {
-            "id": 1,
-            "name": "Apple"
+            "id":  1,
+            "name":  "Apple"
         },
         {
-            "id": 0,
-            "name": ""
+            "id":  0,
+            "name":  ""
         },
         {
-            "id": 3,
-            "name": "Banana"
+            "id":  3,
+            "name":  "Banana"
         }
     ]
 }
\ No newline at end of file
diff --git a/test/functest/conf/HorizontalImplicitFixedSizeMap.json b/test/functest/conf/HorizontalImplicitFixedSizeMap.json
index 51aad55..88133d7 100644
--- a/test/functest/conf/HorizontalImplicitFixedSizeMap.json
+++ b/test/functest/conf/HorizontalImplicitFixedSizeMap.json
@@ -1,8 +1,8 @@
 {
-    "itemMap": {
-        "1": {
-            "id": 1,
-            "name": "Apple"
+    "itemMap":  {
+        "1":  {
+            "id":  1,
+            "name":  "Apple"
         }
     }
 }
\ No newline at end of file
diff --git a/test/functest/conf/HorizontalIncellPdefStructList.json b/test/functest/conf/HorizontalIncellPdefStructList.json
index 8d931ee..b7af178 100644
--- a/test/functest/conf/HorizontalIncellPdefStructList.json
+++ b/test/functest/conf/HorizontalIncellPdefStructList.json
@@ -1,16 +1,16 @@
 {
-    "itemList": [
+    "itemList":  [
         {
-            "id": 1,
-            "num": 100
+            "id":  1,
+            "num":  100
         },
         {
-            "id": 2,
-            "num": 200
+            "id":  2,
+            "num":  200
         },
         {
-            "id": 3,
-            "num": 300
+            "id":  3,
+            "num":  300
         }
     ]
 }
\ No newline at end of file
diff --git a/test/functest/conf/HorizontalIncellStructList.json b/test/functest/conf/HorizontalIncellStructList.json
index 850cd0e..6f87a64 100644
--- a/test/functest/conf/HorizontalIncellStructList.json
+++ b/test/functest/conf/HorizontalIncellStructList.json
@@ -1,16 +1,16 @@
 {
-    "itemList": [
+    "itemList":  [
         {
-            "id": 1,
-            "name": "Apple"
+            "id":  1,
+            "name":  "Apple"
         },
         {
-            "id": 2,
-            "name": "Orange"
+            "id":  2,
+            "name":  "Orange"
         },
         {
-            "id": 3,
-            "name": "Banana"
+            "id":  3,
+            "name":  "Banana"
         }
     ]
 }
\ No newline at end of file
diff --git a/test/functest/conf/HorizontalListInHorizontaListFF.json b/test/functest/conf/HorizontalListInHorizontaListFF.json
index e433240..67dd32f 100644
--- a/test/functest/conf/HorizontalListInHorizontaListFF.json
+++ b/test/functest/conf/HorizontalListInHorizontaListFF.json
@@ -1,26 +1,26 @@
 {
-    "rewardList": [
+    "rewardList":  [
         {
-            "itemList": [
+            "itemList":  [
                 {
-                    "id": 1,
-                    "num": 10
+                    "id":  1,
+                    "num":  10
                 },
                 {
-                    "id": 2,
-                    "num": 20
+                    "id":  2,
+                    "num":  20
                 }
             ],
-            "name": "Lotto"
+            "name":  "Lotto"
         },
         {
-            "itemList": [
+            "itemList":  [
                 {
-                    "id": 10,
-                    "num": 100
+                    "id":  10,
+                    "num":  100
                 }
             ],
-            "name": "Super Lotto"
+            "name":  "Super Lotto"
         }
     ]
 }
\ No newline at end of file
diff --git a/test/functest/conf/HorizontalListInVerticalList.json b/test/functest/conf/HorizontalListInVerticalList.json
index 5a1d4d4..c61ac7f 100644
--- a/test/functest/conf/HorizontalListInVerticalList.json
+++ b/test/functest/conf/HorizontalListInVerticalList.json
@@ -1,33 +1,33 @@
 {
-    "itemList": [
+    "itemList":  [
         {
-            "id": 1,
-            "name": "Apple",
-            "propList": [
+            "id":  1,
+            "name":  "Apple",
+            "propList":  [
                 {
-                    "id": 1,
-                    "value": "10"
+                    "id":  1,
+                    "value":  "10"
                 },
                 {
-                    "id": 2,
-                    "value": "20"
+                    "id":  2,
+                    "value":  "20"
                 }
             ]
         },
         {
-            "id": 2,
-            "name": "Orange",
-            "propList": [
+            "id":  2,
+            "name":  "Orange",
+            "propList":  [
                 {
-                    "id": 3,
-                    "value": "30"
+                    "id":  3,
+                    "value":  "30"
                 }
             ]
         },
         {
-            "id": 3,
-            "name": "Banana",
-            "propList": []
+            "id":  3,
+            "name":  "Banana",
+            "propList":  []
         }
     ]
 }
\ No newline at end of file
diff --git a/test/functest/conf/HorizontalListInVerticalMap.json b/test/functest/conf/HorizontalListInVerticalMap.json
index 48297ca..70a8199 100644
--- a/test/functest/conf/HorizontalListInVerticalMap.json
+++ b/test/functest/conf/HorizontalListInVerticalMap.json
@@ -1,33 +1,33 @@
 {
-    "itemMap": {
-        "1": {
-            "id": 1,
-            "name": "Apple",
-            "propList": [
+    "itemMap":  {
+        "1":  {
+            "id":  1,
+            "name":  "Apple",
+            "propList":  [
                 {
-                    "id": 1,
-                    "value": "10"
+                    "id":  1,
+                    "value":  "10"
                 },
                 {
-                    "id": 2,
-                    "value": "20"
+                    "id":  2,
+                    "value":  "20"
                 }
             ]
         },
-        "2": {
-            "id": 2,
-            "name": "Orange",
-            "propList": [
+        "2":  {
+            "id":  2,
+            "name":  "Orange",
+            "propList":  [
                 {
-                    "id": 3,
-                    "value": "30"
+                    "id":  3,
+                    "value":  "30"
                 }
             ]
         },
-        "3": {
-            "id": 3,
-            "name": "Banana",
-            "propList": []
+        "3":  {
+            "id":  3,
+            "name":  "Banana",
+            "propList":  []
         }
     }
 }
\ No newline at end of file
diff --git a/test/functest/conf/HorizontalMapInHorizontalListFF.json b/test/functest/conf/HorizontalMapInHorizontalListFF.json
index 6b52da4..c0333a1 100644
--- a/test/functest/conf/HorizontalMapInHorizontalListFF.json
+++ b/test/functest/conf/HorizontalMapInHorizontalListFF.json
@@ -1,26 +1,26 @@
 {
-    "rewardList": [
+    "rewardList":  [
         {
-            "itemMap": {
-                "1": {
-                    "id": 1,
-                    "num": 10
+            "itemMap":  {
+                "1":  {
+                    "id":  1,
+                    "num":  10
                 },
-                "2": {
-                    "id": 2,
-                    "num": 20
+                "2":  {
+                    "id":  2,
+                    "num":  20
                 }
             },
-            "name": "Lotto"
+            "name":  "Lotto"
         },
         {
-            "itemMap": {
-                "10": {
-                    "id": 10,
-                    "num": 100
+            "itemMap":  {
+                "10":  {
+                    "id":  10,
+                    "num":  100
                 }
             },
-            "name": "Super Lotto"
+            "name":  "Super Lotto"
         }
     ]
 }
\ No newline at end of file
diff --git a/test/functest/conf/HorizontalMapInVerticalList.json b/test/functest/conf/HorizontalMapInVerticalList.json
index 4fa1f47..14efc09 100644
--- a/test/functest/conf/HorizontalMapInVerticalList.json
+++ b/test/functest/conf/HorizontalMapInVerticalList.json
@@ -1,33 +1,33 @@
 {
-    "itemList": [
+    "itemList":  [
         {
-            "id": 1,
-            "name": "Apple",
-            "propMap": {
-                "1": {
-                    "id": 1,
-                    "value": "10"
+            "id":  1,
+            "name":  "Apple",
+            "propMap":  {
+                "1":  {
+                    "id":  1,
+                    "value":  "10"
                 },
-                "2": {
-                    "id": 2,
-                    "value": "20"
+                "2":  {
+                    "id":  2,
+                    "value":  "20"
                 }
             }
         },
         {
-            "id": 2,
-            "name": "Orange",
-            "propMap": {
-                "3": {
-                    "id": 3,
-                    "value": "30"
+            "id":  2,
+            "name":  "Orange",
+            "propMap":  {
+                "3":  {
+                    "id":  3,
+                    "value":  "30"
                 }
             }
         },
         {
-            "id": 3,
-            "name": "Banana",
-            "propMap": {}
+            "id":  3,
+            "name":  "Banana",
+            "propMap":  {}
         }
     ]
 }
\ No newline at end of file
diff --git a/test/functest/conf/HorizontalMapInVerticalMap.json b/test/functest/conf/HorizontalMapInVerticalMap.json
index 10a8ec3..9276b5b 100644
--- a/test/functest/conf/HorizontalMapInVerticalMap.json
+++ b/test/functest/conf/HorizontalMapInVerticalMap.json
@@ -1,33 +1,33 @@
 {
-    "itemMap": {
-        "1": {
-            "id": 1,
-            "name": "Apple",
-            "propMap": {
-                "1": {
-                    "id": 1,
-                    "value": "10"
+    "itemMap":  {
+        "1":  {
+            "id":  1,
+            "name":  "Apple",
+            "propMap":  {
+                "1":  {
+                    "id":  1,
+                    "value":  "10"
                 },
-                "2": {
-                    "id": 2,
-                    "value": "20"
+                "2":  {
+                    "id":  2,
+                    "value":  "20"
                 }
             }
         },
-        "2": {
-            "id": 2,
-            "name": "Orange",
-            "propMap": {
-                "3": {
-                    "id": 3,
-                    "value": "30"
+        "2":  {
+            "id":  2,
+            "name":  "Orange",
+            "propMap":  {
+                "3":  {
+                    "id":  3,
+                    "value":  "30"
                 }
             }
         },
-        "3": {
-            "id": 3,
-            "name": "Banana",
-            "propMap": {}
+        "3":  {
+            "id":  3,
+            "name":  "Banana",
+            "propMap":  {}
         }
     }
 }
\ No newline at end of file
diff --git a/test/functest/conf/HorizontalPredefinedStructList.json b/test/functest/conf/HorizontalPredefinedStructList.json
index 8d931ee..b7af178 100644
--- a/test/functest/conf/HorizontalPredefinedStructList.json
+++ b/test/functest/conf/HorizontalPredefinedStructList.json
@@ -1,16 +1,16 @@
 {
-    "itemList": [
+    "itemList":  [
         {
-            "id": 1,
-            "num": 100
+            "id":  1,
+            "num":  100
         },
         {
-            "id": 2,
-            "num": 200
+            "id":  2,
+            "num":  200
         },
         {
-            "id": 3,
-            "num": 300
+            "id":  3,
+            "num":  300
         }
     ]
 }
\ No newline at end of file
diff --git a/test/functest/conf/HorizontalPredefinedStructMap.json b/test/functest/conf/HorizontalPredefinedStructMap.json
index 9bd5b87..a132d32 100644
--- a/test/functest/conf/HorizontalPredefinedStructMap.json
+++ b/test/functest/conf/HorizontalPredefinedStructMap.json
@@ -1,16 +1,16 @@
 {
-    "itemMap": {
-        "1": {
-            "id": 1,
-            "num": 100
+    "itemMap":  {
+        "1":  {
+            "id":  1,
+            "num":  100
         },
-        "2": {
-            "id": 2,
-            "num": 200
+        "2":  {
+            "id":  2,
+            "num":  200
         },
-        "3": {
-            "id": 3,
-            "num": 300
+        "3":  {
+            "id":  3,
+            "num":  300
         }
     }
 }
\ No newline at end of file
diff --git a/test/functest/conf/HorizontalScalarList.json b/test/functest/conf/HorizontalScalarList.json
index c467e17..748ac26 100644
--- a/test/functest/conf/HorizontalScalarList.json
+++ b/test/functest/conf/HorizontalScalarList.json
@@ -1,5 +1,5 @@
 {
-    "paramList": [
+    "paramList":  [
         1,
         2,
         3
diff --git a/test/functest/conf/HorizontalStructList.json b/test/functest/conf/HorizontalStructList.json
index 850cd0e..6f87a64 100644
--- a/test/functest/conf/HorizontalStructList.json
+++ b/test/functest/conf/HorizontalStructList.json
@@ -1,16 +1,16 @@
 {
-    "itemList": [
+    "itemList":  [
         {
-            "id": 1,
-            "name": "Apple"
+            "id":  1,
+            "name":  "Apple"
         },
         {
-            "id": 2,
-            "name": "Orange"
+            "id":  2,
+            "name":  "Orange"
         },
         {
-            "id": 3,
-            "name": "Banana"
+            "id":  3,
+            "name":  "Banana"
         }
     ]
 }
\ No newline at end of file
diff --git a/test/functest/conf/HorizontalStructMap.json b/test/functest/conf/HorizontalStructMap.json
index 53bb9d4..10b1221 100644
--- a/test/functest/conf/HorizontalStructMap.json
+++ b/test/functest/conf/HorizontalStructMap.json
@@ -1,16 +1,16 @@
 {
-    "itemMap": {
-        "1": {
-            "id": 1,
-            "name": "Apple"
+    "itemMap":  {
+        "1":  {
+            "id":  1,
+            "name":  "Apple"
         },
-        "2": {
-            "id": 2,
-            "name": "Orange"
+        "2":  {
+            "id":  2,
+            "name":  "Orange"
         },
-        "3": {
-            "id": 3,
-            "name": "Banana"
+        "3":  {
+            "id":  3,
+            "name":  "Banana"
         }
     }
 }
\ No newline at end of file
diff --git a/test/functest/conf/InCellStruct.json b/test/functest/conf/InCellStruct.json
index bbb3db5..0f5e9b3 100644
--- a/test/functest/conf/InCellStruct.json
+++ b/test/functest/conf/InCellStruct.json
@@ -1,27 +1,27 @@
 {
-    "itemMap": {
-        "1": {
-            "id": 1,
-            "property": {
-                "id": 1,
-                "name": "Apple",
-                "desc": "A kind of delicious fruit."
+    "itemMap":  {
+        "1":  {
+            "id":  1,
+            "property":  {
+                "id":  1,
+                "name":  "Apple",
+                "desc":  "A kind of delicious fruit."
             }
         },
-        "2": {
-            "id": 2,
-            "property": {
-                "id": 2,
-                "name": "Orange",
-                "desc": ""
+        "2":  {
+            "id":  2,
+            "property":  {
+                "id":  2,
+                "name":  "Orange",
+                "desc":  ""
             }
         },
-        "3": {
-            "id": 3,
-            "property": {
-                "id": 3,
-                "name": "",
-                "desc": ""
+        "3":  {
+            "id":  3,
+            "property":  {
+                "id":  3,
+                "name":  "",
+                "desc":  ""
             }
         }
     }
diff --git a/test/functest/conf/IncellListInVerticalList.json b/test/functest/conf/IncellListInVerticalList.json
index b3c36af..81dd3ec 100644
--- a/test/functest/conf/IncellListInVerticalList.json
+++ b/test/functest/conf/IncellListInVerticalList.json
@@ -1,23 +1,23 @@
 {
-    "itemList": [
+    "itemList":  [
         {
-            "id": 1,
-            "propList": [
+            "id":  1,
+            "propList":  [
                 10,
                 20,
                 30
             ]
         },
         {
-            "id": 2,
-            "propList": [
+            "id":  2,
+            "propList":  [
                 10,
                 20
             ]
         },
         {
-            "id": 3,
-            "propList": [
+            "id":  3,
+            "propList":  [
                 10
             ]
         }
diff --git a/test/functest/conf/IncellListInVerticalMap.json b/test/functest/conf/IncellListInVerticalMap.json
index a41e1b7..08ee6af 100644
--- a/test/functest/conf/IncellListInVerticalMap.json
+++ b/test/functest/conf/IncellListInVerticalMap.json
@@ -1,23 +1,23 @@
 {
-    "itemMap": {
-        "1": {
-            "id": 1,
-            "propList": [
+    "itemMap":  {
+        "1":  {
+            "id":  1,
+            "propList":  [
                 10,
                 20,
                 30
             ]
         },
-        "2": {
-            "id": 2,
-            "propList": [
+        "2":  {
+            "id":  2,
+            "propList":  [
                 10,
                 20
             ]
         },
-        "3": {
-            "id": 3,
-            "propList": [
+        "3":  {
+            "id":  3,
+            "propList":  [
                 10
             ]
         }
diff --git a/test/functest/conf/IncellMap.json b/test/functest/conf/IncellMap.json
index f19d335..bde1643 100644
--- a/test/functest/conf/IncellMap.json
+++ b/test/functest/conf/IncellMap.json
@@ -1,7 +1,7 @@
 {
-    "itemsMap": {
-        "1": "Apple",
-        "2": "Orange",
-        "3": "Banana"
+    "itemsMap":  {
+        "1":  "Apple",
+        "2":  "Orange",
+        "3":  "Banana"
     }
 }
\ No newline at end of file
diff --git a/test/functest/conf/IncellMapInVerticalList.json b/test/functest/conf/IncellMapInVerticalList.json
index 4ed7c27..9637847 100644
--- a/test/functest/conf/IncellMapInVerticalList.json
+++ b/test/functest/conf/IncellMapInVerticalList.json
@@ -1,24 +1,24 @@
 {
-    "itemList": [
+    "itemList":  [
         {
-            "id": 1,
-            "propsMap": {
-                "1": "sour",
-                "2": "sweet",
-                "3": "delicious"
+            "id":  1,
+            "propsMap":  {
+                "1":  "sour",
+                "2":  "sweet",
+                "3":  "delicious"
             }
         },
         {
-            "id": 2,
-            "propsMap": {
-                "1": "sour",
-                "2": "sweet"
+            "id":  2,
+            "propsMap":  {
+                "1":  "sour",
+                "2":  "sweet"
             }
         },
         {
-            "id": 3,
-            "propsMap": {
-                "1": "sour"
+            "id":  3,
+            "propsMap":  {
+                "1":  "sour"
             }
         }
     ]
diff --git a/test/functest/conf/IncellMapInVerticalMap.json b/test/functest/conf/IncellMapInVerticalMap.json
index f0c66d5..805deae 100644
--- a/test/functest/conf/IncellMapInVerticalMap.json
+++ b/test/functest/conf/IncellMapInVerticalMap.json
@@ -1,24 +1,24 @@
 {
-    "itemMap": {
-        "1": {
-            "id": 1,
-            "propsMap": {
-                "1": "sour",
-                "2": "sweet",
-                "3": "delicious"
+    "itemMap":  {
+        "1":  {
+            "id":  1,
+            "propsMap":  {
+                "1":  "sour",
+                "2":  "sweet",
+                "3":  "delicious"
             }
         },
-        "2": {
-            "id": 2,
-            "propsMap": {
-                "1": "sour",
-                "2": "sweet"
+        "2":  {
+            "id":  2,
+            "propsMap":  {
+                "1":  "sour",
+                "2":  "sweet"
             }
         },
-        "3": {
-            "id": 3,
-            "propsMap": {
-                "1": "sour"
+        "3":  {
+            "id":  3,
+            "propsMap":  {
+                "1":  "sour"
             }
         }
     }
diff --git a/test/functest/conf/IncellScalarList.json b/test/functest/conf/IncellScalarList.json
index 5f4299d..d824657 100644
--- a/test/functest/conf/IncellScalarList.json
+++ b/test/functest/conf/IncellScalarList.json
@@ -1,5 +1,5 @@
 {
-    "paramList": [
+    "paramList":  [
         1,
         2,
         3,
diff --git a/test/functest/conf/IncellStructInHorizontaListFF.json b/test/functest/conf/IncellStructInHorizontaListFF.json
index 7c6dd3a..8e03fdb 100644
--- a/test/functest/conf/IncellStructInHorizontaListFF.json
+++ b/test/functest/conf/IncellStructInHorizontaListFF.json
@@ -1,18 +1,18 @@
 {
-    "rewardList": [
+    "rewardList":  [
         {
-            "item": {
-                "id": 1,
-                "num": 10
+            "item":  {
+                "id":  1,
+                "num":  10
             },
-            "name": "Lotto"
+            "name":  "Lotto"
         },
         {
-            "item": {
-                "id": 2,
-                "num": 20
+            "item":  {
+                "id":  2,
+                "num":  20
             },
-            "name": "Super Lotto"
+            "name":  "Super Lotto"
         }
     ]
 }
\ No newline at end of file
diff --git a/test/functest/conf/IncellStructInStruct.json b/test/functest/conf/IncellStructInStruct.json
index ffb2b08..0bf4ab2 100644
--- a/test/functest/conf/IncellStructInStruct.json
+++ b/test/functest/conf/IncellStructInStruct.json
@@ -1,9 +1,9 @@
 {
-    "reward": {
-        "id": 1,
-        "item": {
-            "id": 2,
-            "num": 200
+    "reward":  {
+        "id":  1,
+        "item":  {
+            "id":  2,
+            "num":  200
         }
     }
 }
\ No newline at end of file
diff --git a/test/functest/conf/IncellStructInVerticalList.json b/test/functest/conf/IncellStructInVerticalList.json
index f88872f..f537c1d 100644
--- a/test/functest/conf/IncellStructInVerticalList.json
+++ b/test/functest/conf/IncellStructInVerticalList.json
@@ -1,25 +1,25 @@
 {
-    "itemList": [
+    "itemList":  [
         {
-            "id": 1,
-            "name": "Apple",
-            "propId": {
-                "id": 1,
-                "value": "100"
+            "id":  1,
+            "name":  "Apple",
+            "propId":  {
+                "id":  1,
+                "value":  "100"
             }
         },
         {
-            "id": 2,
-            "name": "Orange",
-            "propId": {
-                "id": 2,
-                "value": "200"
+            "id":  2,
+            "name":  "Orange",
+            "propId":  {
+                "id":  2,
+                "value":  "200"
             }
         },
         {
-            "id": 3,
-            "name": "Banana",
-            "propId": null
+            "id":  3,
+            "name":  "Banana",
+            "propId":  null
         }
     ]
 }
\ No newline at end of file
diff --git a/test/functest/conf/IncellStructInVerticalMap.json b/test/functest/conf/IncellStructInVerticalMap.json
index 47c9269..2c7bdf5 100644
--- a/test/functest/conf/IncellStructInVerticalMap.json
+++ b/test/functest/conf/IncellStructInVerticalMap.json
@@ -1,22 +1,22 @@
 {
-    "rewardMap": {
-        "1": {
-            "id": 1,
-            "item": {
-                "id": 1,
-                "num": 100
+    "rewardMap":  {
+        "1":  {
+            "id":  1,
+            "item":  {
+                "id":  1,
+                "num":  100
             }
         },
-        "2": {
-            "id": 2,
-            "item": {
-                "id": 2,
-                "num": 200
+        "2":  {
+            "id":  2,
+            "item":  {
+                "id":  2,
+                "num":  200
             }
         },
-        "3": {
-            "id": 3,
-            "item": null
+        "3":  {
+            "id":  3,
+            "item":  null
         }
     }
 }
\ No newline at end of file
diff --git a/test/functest/conf/KeyedListConf.json b/test/functest/conf/KeyedListConf.json
new file mode 100644
index 0000000..2218138
--- /dev/null
+++ b/test/functest/conf/KeyedListConf.json
@@ -0,0 +1,20 @@
+{
+    "parent": {
+        "parent2": {
+            "crossCellList": [
+                {
+                    "value": 1,
+                    "name": "Apple"
+                },
+                {
+                    "value": 2,
+                    "name": "Banana"
+                },
+                {
+                    "value": 3,
+                    "name": "Orange"
+                }
+            ]
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/functest/conf/KeyedListConf2.json b/test/functest/conf/KeyedListConf2.json
new file mode 100644
index 0000000..c2d4946
--- /dev/null
+++ b/test/functest/conf/KeyedListConf2.json
@@ -0,0 +1,13 @@
+{
+    "parent": {
+        "parent2": {
+            "incell": {
+                "valueList": [
+                    1,
+                    2,
+                    3
+                ]
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/functest/conf/ListConf.json b/test/functest/conf/ListConf.json
new file mode 100644
index 0000000..2824c92
--- /dev/null
+++ b/test/functest/conf/ListConf.json
@@ -0,0 +1,24 @@
+{
+    "parent": {
+        "parent2": {
+            "crossCellList": [
+                {
+                    "value": 1,
+                    "name": "Apple"
+                },
+                {
+                    "value": 2,
+                    "name": "Banana"
+                },
+                {
+                    "value": 3,
+                    "name": "Orange"
+                },
+                {
+                    "value": 1,
+                    "name": "Grape"
+                }
+            ]
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/functest/conf/ListConf2.json b/test/functest/conf/ListConf2.json
new file mode 100644
index 0000000..3d33a06
--- /dev/null
+++ b/test/functest/conf/ListConf2.json
@@ -0,0 +1,14 @@
+{
+    "parent": {
+        "parent2": {
+            "incell": {
+                "valueList": [
+                    1,
+                    2,
+                    3,
+                    1
+                ]
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/functest/conf/MapConf.json b/test/functest/conf/MapConf.json
new file mode 100644
index 0000000..7aeb2f2
--- /dev/null
+++ b/test/functest/conf/MapConf.json
@@ -0,0 +1,17 @@
+{
+    "open":  true,
+    "entryMap":  {
+        "1":  {
+            "id":  1,
+            "value":  1
+        },
+        "2":  {
+            "id":  2,
+            "value":  2
+        },
+        "3":  {
+            "id":  3,
+            "value":  3
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/functest/conf/MetasheetConf.json b/test/functest/conf/MetasheetConf.json
new file mode 100644
index 0000000..a6d61b2
--- /dev/null
+++ b/test/functest/conf/MetasheetConf.json
@@ -0,0 +1,9 @@
+{
+    "sepConf": {
+        "numberList": [
+            1,
+            2,
+            3
+        ]
+    }
+}
\ No newline at end of file
diff --git a/test/functest/conf/PredefStructInHorizontaListFF.json b/test/functest/conf/PredefStructInHorizontaListFF.json
index 3e483be..75d21b3 100644
--- a/test/functest/conf/PredefStructInHorizontaListFF.json
+++ b/test/functest/conf/PredefStructInHorizontaListFF.json
@@ -1,18 +1,18 @@
 {
-    "rewardList": [
+    "rewardList":  [
         {
-            "item": {
-                "id": 1,
-                "num": 10
+            "item":  {
+                "id":  1,
+                "num":  10
             },
-            "name": "Lotto"
+            "name":  "Lotto"
         },
         {
-            "item": {
-                "id": 10,
-                "num": 100
+            "item":  {
+                "id":  10,
+                "num":  100
             },
-            "name": "Super Lotto"
+            "name":  "Super Lotto"
         }
     ]
 }
\ No newline at end of file
diff --git a/test/functest/conf/PredefinedStructInStruct.json b/test/functest/conf/PredefinedStructInStruct.json
index 9550cc3..3e63abd 100644
--- a/test/functest/conf/PredefinedStructInStruct.json
+++ b/test/functest/conf/PredefinedStructInStruct.json
@@ -1,9 +1,9 @@
 {
-    "reward": {
-        "id": 1,
-        "item": {
-            "id": 1,
-            "num": 10
+    "reward":  {
+        "id":  1,
+        "item":  {
+            "id":  1,
+            "num":  10
         }
     }
 }
\ No newline at end of file
diff --git a/test/functest/conf/PredefinedStructInVerticalMap.json b/test/functest/conf/PredefinedStructInVerticalMap.json
index 74d318f..a61523b 100644
--- a/test/functest/conf/PredefinedStructInVerticalMap.json
+++ b/test/functest/conf/PredefinedStructInVerticalMap.json
@@ -1,22 +1,22 @@
 {
-    "rewardMap": {
-        "1": {
-            "id": 1,
-            "item": {
-                "id": 1,
-                "num": 10
+    "rewardMap":  {
+        "1":  {
+            "id":  1,
+            "item":  {
+                "id":  1,
+                "num":  10
             }
         },
-        "2": {
-            "id": 2,
-            "item": {
-                "id": 2,
-                "num": 20
+        "2":  {
+            "id":  2,
+            "item":  {
+                "id":  2,
+                "num":  20
             }
         },
-        "3": {
-            "id": 3,
-            "item": null
+        "3":  {
+            "id":  3,
+            "item":  null
         }
     }
 }
\ No newline at end of file
diff --git a/test/functest/conf/Scalar.json b/test/functest/conf/Scalar.json
index 041c93c..813096f 100644
--- a/test/functest/conf/Scalar.json
+++ b/test/functest/conf/Scalar.json
@@ -1,5 +1,5 @@
 {
-    "id": 1,
-    "name": "Apple",
-    "desc": "A kind of delicious fruit."
+    "id":  1,
+    "name":  "Apple",
+    "desc":  "A kind of delicious fruit."
 }
\ No newline at end of file
diff --git a/test/functest/conf/ScalarFieldProp.json b/test/functest/conf/ScalarFieldProp.json
index 9f32510..9bae2c2 100644
--- a/test/functest/conf/ScalarFieldProp.json
+++ b/test/functest/conf/ScalarFieldProp.json
@@ -1,20 +1,20 @@
 {
-    "itemMap": {
-        "1": {
-            "id": 1,
-            "propList": [
+    "itemMap":  {
+        "1":  {
+            "id":  1,
+            "propList":  [
                 {
-                    "id": 1,
-                    "flavor": "sour"
+                    "id":  1,
+                    "flavor":  "sour"
                 },
                 {
-                    "id": 2,
-                    "flavor": "sweet"
+                    "id":  2,
+                    "flavor":  "sweet"
                 }
             ],
-            "appearance": {
-                "color": "red",
-                "shape": "round"
+            "appearance":  {
+                "color":  "red",
+                "shape":  "round"
             }
         }
     }
diff --git a/test/functest/conf/ScopedTypeReuseListElem.json b/test/functest/conf/ScopedTypeReuseListElem.json
index 387d94c..3aae065 100644
--- a/test/functest/conf/ScopedTypeReuseListElem.json
+++ b/test/functest/conf/ScopedTypeReuseListElem.json
@@ -1,22 +1,22 @@
 {
-    "itemList": [
+    "itemList":  [
         {
-            "id": 1,
-            "name": "Apple"
+            "id":  1,
+            "name":  "Apple"
         },
         {
-            "id": 2,
-            "name": "Orange"
+            "id":  2,
+            "name":  "Orange"
         }
     ],
-    "rewardList": [
+    "rewardList":  [
         {
-            "id": 3,
-            "name": "Banana"
+            "id":  3,
+            "name":  "Banana"
         },
         {
-            "id": 4,
-            "name": "Watermelon"
+            "id":  4,
+            "name":  "Watermelon"
         }
     ]
 }
\ No newline at end of file
diff --git a/test/functest/conf/ScopedTypeReuseMapValue.json b/test/functest/conf/ScopedTypeReuseMapValue.json
index 554be30..e520a6f 100644
--- a/test/functest/conf/ScopedTypeReuseMapValue.json
+++ b/test/functest/conf/ScopedTypeReuseMapValue.json
@@ -1,22 +1,22 @@
 {
-    "itemMap": {
-        "1": {
-            "id": 1,
-            "name": "Apple"
+    "itemMap":  {
+        "1":  {
+            "id":  1,
+            "name":  "Apple"
         },
-        "2": {
-            "id": 2,
-            "name": "Orange"
+        "2":  {
+            "id":  2,
+            "name":  "Orange"
         }
     },
-    "rewardMap": {
-        "3": {
-            "id": 3,
-            "name": "Banana"
+    "rewardMap":  {
+        "3":  {
+            "id":  3,
+            "name":  "Banana"
         },
-        "4": {
-            "id": 4,
-            "name": "Watermelon"
+        "4":  {
+            "id":  4,
+            "name":  "Watermelon"
         }
     }
 }
\ No newline at end of file
diff --git a/test/functest/conf/ScopedTypeReuseStruct.json b/test/functest/conf/ScopedTypeReuseStruct.json
index 2899e77..b86741a 100644
--- a/test/functest/conf/ScopedTypeReuseStruct.json
+++ b/test/functest/conf/ScopedTypeReuseStruct.json
@@ -1,10 +1,10 @@
 {
-    "item": {
-        "id": 1,
-        "name": "Apple"
+    "item":  {
+        "id":  1,
+        "name":  "Apple"
     },
-    "reward": {
-        "id": 2,
-        "name": "Orange"
+    "reward":  {
+        "id":  2,
+        "name":  "Orange"
     }
 }
\ No newline at end of file
diff --git a/test/functest/conf/StructInHorizontaListFF.json b/test/functest/conf/StructInHorizontaListFF.json
index 3e483be..75d21b3 100644
--- a/test/functest/conf/StructInHorizontaListFF.json
+++ b/test/functest/conf/StructInHorizontaListFF.json
@@ -1,18 +1,18 @@
 {
-    "rewardList": [
+    "rewardList":  [
         {
-            "item": {
-                "id": 1,
-                "num": 10
+            "item":  {
+                "id":  1,
+                "num":  10
             },
-            "name": "Lotto"
+            "name":  "Lotto"
         },
         {
-            "item": {
-                "id": 10,
-                "num": 100
+            "item":  {
+                "id":  10,
+                "num":  100
             },
-            "name": "Super Lotto"
+            "name":  "Super Lotto"
         }
     ]
 }
\ No newline at end of file
diff --git a/test/functest/conf/StructInStruct.json b/test/functest/conf/StructInStruct.json
index 9550cc3..3e63abd 100644
--- a/test/functest/conf/StructInStruct.json
+++ b/test/functest/conf/StructInStruct.json
@@ -1,9 +1,9 @@
 {
-    "reward": {
-        "id": 1,
-        "item": {
-            "id": 1,
-            "num": 10
+    "reward":  {
+        "id":  1,
+        "item":  {
+            "id":  1,
+            "num":  10
         }
     }
 }
\ No newline at end of file
diff --git a/test/functest/conf/StructInVerticalList.json b/test/functest/conf/StructInVerticalList.json
index fb2bf81..703fae3 100644
--- a/test/functest/conf/StructInVerticalList.json
+++ b/test/functest/conf/StructInVerticalList.json
@@ -1,25 +1,25 @@
 {
-    "itemList": [
+    "itemList":  [
         {
-            "id": 1,
-            "name": "Apple",
-            "prop": {
-                "id": 1,
-                "value": "10"
+            "id":  1,
+            "name":  "Apple",
+            "prop":  {
+                "id":  1,
+                "value":  "10"
             }
         },
         {
-            "id": 2,
-            "name": "Orange",
-            "prop": {
-                "id": 2,
-                "value": "20"
+            "id":  2,
+            "name":  "Orange",
+            "prop":  {
+                "id":  2,
+                "value":  "20"
             }
         },
         {
-            "id": 3,
-            "name": "Banana",
-            "prop": null
+            "id":  3,
+            "name":  "Banana",
+            "prop":  null
         }
     ]
 }
\ No newline at end of file
diff --git a/test/functest/conf/StructInVerticalMap.json b/test/functest/conf/StructInVerticalMap.json
index 74d318f..a61523b 100644
--- a/test/functest/conf/StructInVerticalMap.json
+++ b/test/functest/conf/StructInVerticalMap.json
@@ -1,22 +1,22 @@
 {
-    "rewardMap": {
-        "1": {
-            "id": 1,
-            "item": {
-                "id": 1,
-                "num": 10
+    "rewardMap":  {
+        "1":  {
+            "id":  1,
+            "item":  {
+                "id":  1,
+                "num":  10
             }
         },
-        "2": {
-            "id": 2,
-            "item": {
-                "id": 2,
-                "num": 20
+        "2":  {
+            "id":  2,
+            "item":  {
+                "id":  2,
+                "num":  20
             }
         },
-        "3": {
-            "id": 3,
-            "item": null
+        "3":  {
+            "id":  3,
+            "item":  null
         }
     }
 }
\ No newline at end of file
diff --git a/test/functest/conf/TemplateConf.json b/test/functest/conf/TemplateConf.json
new file mode 100644
index 0000000..4b3c1b7
--- /dev/null
+++ b/test/functest/conf/TemplateConf.json
@@ -0,0 +1,3 @@
+{
+    "outputType":  null
+}
\ No newline at end of file
diff --git a/test/functest/conf/Transpose.json b/test/functest/conf/Transpose.json
index 658682b..30c4020 100644
--- a/test/functest/conf/Transpose.json
+++ b/test/functest/conf/Transpose.json
@@ -1,8 +1,8 @@
 {
-    "id": 123,
-    "name": "Robin",
-    "desc": "A big hero!",
-    "skillList": [
+    "id":  123,
+    "name":  "Robin",
+    "desc":  "A big hero!",
+    "skillList":  [
         100,
         101,
         102
diff --git a/test/functest/conf/VerticalIncellPredefStructList.json b/test/functest/conf/VerticalIncellPredefStructList.json
index 8d931ee..b7af178 100644
--- a/test/functest/conf/VerticalIncellPredefStructList.json
+++ b/test/functest/conf/VerticalIncellPredefStructList.json
@@ -1,16 +1,16 @@
 {
-    "itemList": [
+    "itemList":  [
         {
-            "id": 1,
-            "num": 100
+            "id":  1,
+            "num":  100
         },
         {
-            "id": 2,
-            "num": 200
+            "id":  2,
+            "num":  200
         },
         {
-            "id": 3,
-            "num": 300
+            "id":  3,
+            "num":  300
         }
     ]
 }
\ No newline at end of file
diff --git a/test/functest/conf/VerticalIncellScalarKeyedList.json b/test/functest/conf/VerticalIncellScalarKeyedList.json
index c467e17..748ac26 100644
--- a/test/functest/conf/VerticalIncellScalarKeyedList.json
+++ b/test/functest/conf/VerticalIncellScalarKeyedList.json
@@ -1,5 +1,5 @@
 {
-    "paramList": [
+    "paramList":  [
         1,
         2,
         3
diff --git a/test/functest/conf/VerticalIncellStructList.json b/test/functest/conf/VerticalIncellStructList.json
index 8d931ee..b7af178 100644
--- a/test/functest/conf/VerticalIncellStructList.json
+++ b/test/functest/conf/VerticalIncellStructList.json
@@ -1,16 +1,16 @@
 {
-    "itemList": [
+    "itemList":  [
         {
-            "id": 1,
-            "num": 100
+            "id":  1,
+            "num":  100
         },
         {
-            "id": 2,
-            "num": 200
+            "id":  2,
+            "num":  200
         },
         {
-            "id": 3,
-            "num": 300
+            "id":  3,
+            "num":  300
         }
     ]
 }
\ No newline at end of file
diff --git a/test/functest/conf/VerticalListInVerticalKeyedList.json b/test/functest/conf/VerticalListInVerticalKeyedList.json
index fcfad5d..06ca821 100644
--- a/test/functest/conf/VerticalListInVerticalKeyedList.json
+++ b/test/functest/conf/VerticalListInVerticalKeyedList.json
@@ -1,26 +1,26 @@
 {
-    "itemList": [
+    "itemList":  [
         {
-            "id": 1,
-            "name": "Apple",
-            "propList": [
+            "id":  1,
+            "name":  "Apple",
+            "propList":  [
                 {
-                    "propId": 1,
-                    "propValue": "10"
+                    "propId":  1,
+                    "propValue":  "10"
                 }
             ]
         },
         {
-            "id": 2,
-            "name": "Orange",
-            "propList": [
+            "id":  2,
+            "name":  "Orange",
+            "propList":  [
                 {
-                    "propId": 1,
-                    "propValue": "20"
+                    "propId":  1,
+                    "propValue":  "20"
                 },
                 {
-                    "propId": 2,
-                    "propValue": "30"
+                    "propId":  2,
+                    "propValue":  "30"
                 }
             ]
         }
diff --git a/test/functest/conf/VerticalListInVerticalMap.json b/test/functest/conf/VerticalListInVerticalMap.json
index 1a7781b..813af00 100644
--- a/test/functest/conf/VerticalListInVerticalMap.json
+++ b/test/functest/conf/VerticalListInVerticalMap.json
@@ -1,26 +1,26 @@
 {
-    "itemMap": {
-        "1": {
-            "id": 1,
-            "name": "Apple",
-            "propList": [
+    "itemMap":  {
+        "1":  {
+            "id":  1,
+            "name":  "Apple",
+            "propList":  [
                 {
-                    "propId": 1,
-                    "propValue": "10"
+                    "propId":  1,
+                    "propValue":  "10"
                 }
             ]
         },
-        "2": {
-            "id": 2,
-            "name": "Orange",
-            "propList": [
+        "2":  {
+            "id":  2,
+            "name":  "Orange",
+            "propList":  [
                 {
-                    "propId": 1,
-                    "propValue": "20"
+                    "propId":  1,
+                    "propValue":  "20"
                 },
                 {
-                    "propId": 2,
-                    "propValue": "30"
+                    "propId":  2,
+                    "propValue":  "30"
                 }
             ]
         }
diff --git a/test/functest/conf/VerticalMapInVerticalKeyedList.json b/test/functest/conf/VerticalMapInVerticalKeyedList.json
index 2aa90e9..333dc81 100644
--- a/test/functest/conf/VerticalMapInVerticalKeyedList.json
+++ b/test/functest/conf/VerticalMapInVerticalKeyedList.json
@@ -1,26 +1,26 @@
 {
-    "itemList": [
+    "itemList":  [
         {
-            "id": 1,
-            "name": "Apple",
-            "propMap": {
-                "1": {
-                    "propId": 1,
-                    "propValue": "10"
+            "id":  1,
+            "name":  "Apple",
+            "propMap":  {
+                "1":  {
+                    "propId":  1,
+                    "propValue":  "10"
                 }
             }
         },
         {
-            "id": 2,
-            "name": "Orange",
-            "propMap": {
-                "1": {
-                    "propId": 1,
-                    "propValue": "20"
+            "id":  2,
+            "name":  "Orange",
+            "propMap":  {
+                "1":  {
+                    "propId":  1,
+                    "propValue":  "20"
                 },
-                "2": {
-                    "propId": 2,
-                    "propValue": "30"
+                "2":  {
+                    "propId":  2,
+                    "propValue":  "30"
                 }
             }
         }
diff --git a/test/functest/conf/VerticalMapInVerticalMap.json b/test/functest/conf/VerticalMapInVerticalMap.json
index 08f83f3..b366c42 100644
--- a/test/functest/conf/VerticalMapInVerticalMap.json
+++ b/test/functest/conf/VerticalMapInVerticalMap.json
@@ -1,26 +1,26 @@
 {
-    "itemMap": {
-        "1": {
-            "id": 1,
-            "name": "Apple",
-            "propMap": {
-                "1": {
-                    "propId": 1,
-                    "propValue": "10"
+    "itemMap":  {
+        "1":  {
+            "id":  1,
+            "name":  "Apple",
+            "propMap":  {
+                "1":  {
+                    "propId":  1,
+                    "propValue":  "10"
                 }
             }
         },
-        "2": {
-            "id": 2,
-            "name": "Orange",
-            "propMap": {
-                "1": {
-                    "propId": 1,
-                    "propValue": "20"
+        "2":  {
+            "id":  2,
+            "name":  "Orange",
+            "propMap":  {
+                "1":  {
+                    "propId":  1,
+                    "propValue":  "20"
                 },
-                "2": {
-                    "propId": 2,
-                    "propValue": "30"
+                "2":  {
+                    "propId":  2,
+                    "propValue":  "30"
                 }
             }
         }
diff --git a/test/functest/conf/VerticalPredefinedStructList.json b/test/functest/conf/VerticalPredefinedStructList.json
index c37bdc3..b7b1ead 100644
--- a/test/functest/conf/VerticalPredefinedStructList.json
+++ b/test/functest/conf/VerticalPredefinedStructList.json
@@ -1,16 +1,16 @@
 {
-    "ItemList": [
+    "ItemList":  [
         {
-            "id": 1,
-            "num": 100
+            "id":  1,
+            "num":  100
         },
         {
-            "id": 2,
-            "num": 200
+            "id":  2,
+            "num":  200
         },
         {
-            "id": 3,
-            "num": 300
+            "id":  3,
+            "num":  300
         }
     ]
 }
\ No newline at end of file
diff --git a/test/functest/conf/VerticalPredefinedStructMap.json b/test/functest/conf/VerticalPredefinedStructMap.json
index 9bd5b87..a132d32 100644
--- a/test/functest/conf/VerticalPredefinedStructMap.json
+++ b/test/functest/conf/VerticalPredefinedStructMap.json
@@ -1,16 +1,16 @@
 {
-    "itemMap": {
-        "1": {
-            "id": 1,
-            "num": 100
+    "itemMap":  {
+        "1":  {
+            "id":  1,
+            "num":  100
         },
-        "2": {
-            "id": 2,
-            "num": 200
+        "2":  {
+            "id":  2,
+            "num":  200
         },
-        "3": {
-            "id": 3,
-            "num": 300
+        "3":  {
+            "id":  3,
+            "num":  300
         }
     }
 }
\ No newline at end of file
diff --git a/test/functest/conf/VerticalStructKeyedList.json b/test/functest/conf/VerticalStructKeyedList.json
index 5b3ff46..abbfdb5 100644
--- a/test/functest/conf/VerticalStructKeyedList.json
+++ b/test/functest/conf/VerticalStructKeyedList.json
@@ -1,24 +1,24 @@
 {
-    "itemList": [
+    "itemList":  [
         {
-            "id": 1,
-            "propMap": {
-                "1": {
-                    "propId": 1,
-                    "propName": "sweet"
+            "id":  1,
+            "propMap":  {
+                "1":  {
+                    "propId":  1,
+                    "propName":  "sweet"
                 }
             }
         },
         {
-            "id": 2,
-            "propMap": {
-                "1": {
-                    "propId": 1,
-                    "propName": "sweet"
+            "id":  2,
+            "propMap":  {
+                "1":  {
+                    "propId":  1,
+                    "propName":  "sweet"
                 },
-                "2": {
-                    "propId": 2,
-                    "propName": "delicious"
+                "2":  {
+                    "propId":  2,
+                    "propName":  "delicious"
                 }
             }
         }
diff --git a/test/functest/conf/VerticalStructList.json b/test/functest/conf/VerticalStructList.json
index 85fa874..9f99f3e 100644
--- a/test/functest/conf/VerticalStructList.json
+++ b/test/functest/conf/VerticalStructList.json
@@ -1,19 +1,19 @@
 {
-    "itemList": [
+    "itemList":  [
         {
-            "id": 1,
-            "name": "Apple",
-            "desc": "A kind of delicious fruit."
+            "id":  1,
+            "name":  "Apple",
+            "desc":  "A kind of delicious fruit."
         },
         {
-            "id": 2,
-            "name": "Orange",
-            "desc": "A kind of sour fruit."
+            "id":  2,
+            "name":  "Orange",
+            "desc":  "A kind of sour fruit."
         },
         {
-            "id": 3,
-            "name": "Banana",
-            "desc": "A kind of calorie-rich fruit."
+            "id":  3,
+            "name":  "Banana",
+            "desc":  "A kind of calorie-rich fruit."
         }
     ]
 }
\ No newline at end of file
diff --git a/test/functest/conf/VerticalStructMap.json b/test/functest/conf/VerticalStructMap.json
index 2101f85..f7baf58 100644
--- a/test/functest/conf/VerticalStructMap.json
+++ b/test/functest/conf/VerticalStructMap.json
@@ -1,19 +1,19 @@
 {
-    "itemMap": {
-        "1": {
-            "id": 1,
-            "name": "Apple",
-            "desc": "A kind of delicious fruit."
+    "itemMap":  {
+        "1":  {
+            "id":  1,
+            "name":  "Apple",
+            "desc":  "A kind of delicious fruit."
         },
-        "2": {
-            "id": 2,
-            "name": "Orange",
-            "desc": "A kind of sour fruit."
+        "2":  {
+            "id":  2,
+            "name":  "Orange",
+            "desc":  "A kind of sour fruit."
         },
-        "3": {
-            "id": 3,
-            "name": "Banana",
-            "desc": "A kind of calorie-rich fruit."
+        "3":  {
+            "id":  3,
+            "name":  "Banana",
+            "desc":  "A kind of calorie-rich fruit."
         }
     }
 }
\ No newline at end of file
diff --git a/test/functest/proto/field_prop.proto b/test/functest/proto/excel__fieldprop__field_prop.proto
similarity index 100%
rename from test/functest/proto/field_prop.proto
rename to test/functest/proto/excel__fieldprop__field_prop.proto
diff --git a/test/functest/proto/keyed_list.proto b/test/functest/proto/excel__keyedlist__keyed_list.proto
similarity index 100%
rename from test/functest/proto/keyed_list.proto
rename to test/functest/proto/excel__keyedlist__keyed_list.proto
diff --git a/test/functest/proto/horizontal_list.proto b/test/functest/proto/excel__list__horizontal_list.proto
similarity index 100%
rename from test/functest/proto/horizontal_list.proto
rename to test/functest/proto/excel__list__horizontal_list.proto
diff --git a/test/functest/proto/list.proto b/test/functest/proto/excel__list__list.proto
similarity index 100%
rename from test/functest/proto/list.proto
rename to test/functest/proto/excel__list__list.proto
diff --git a/test/functest/proto/vertical_list.proto b/test/functest/proto/excel__list__vertical_list.proto
similarity index 100%
rename from test/functest/proto/vertical_list.proto
rename to test/functest/proto/excel__list__vertical_list.proto
diff --git a/test/functest/proto/horizontal_map.proto b/test/functest/proto/excel__map__horizontal_map.proto
similarity index 100%
rename from test/functest/proto/horizontal_map.proto
rename to test/functest/proto/excel__map__horizontal_map.proto
diff --git a/test/functest/proto/map.proto b/test/functest/proto/excel__map__map.proto
similarity index 100%
rename from test/functest/proto/map.proto
rename to test/functest/proto/excel__map__map.proto
diff --git a/test/functest/proto/vertical_map.proto b/test/functest/proto/excel__map__vertical_map.proto
similarity index 100%
rename from test/functest/proto/vertical_map.proto
rename to test/functest/proto/excel__map__vertical_map.proto
diff --git a/test/functest/proto/metasheet.proto b/test/functest/proto/excel__metasheet__metasheet.proto
similarity index 100%
rename from test/functest/proto/metasheet.proto
rename to test/functest/proto/excel__metasheet__metasheet.proto
diff --git a/test/functest/proto/type_reuse.proto b/test/functest/proto/excel__misc__type_reuse.proto
similarity index 100%
rename from test/functest/proto/type_reuse.proto
rename to test/functest/proto/excel__misc__type_reuse.proto
diff --git a/test/functest/proto/list_in_list.proto b/test/functest/proto/excel__nesting__list_in_list.proto
similarity index 100%
rename from test/functest/proto/list_in_list.proto
rename to test/functest/proto/excel__nesting__list_in_list.proto
diff --git a/test/functest/proto/list_in_map.proto b/test/functest/proto/excel__nesting__list_in_map.proto
similarity index 100%
rename from test/functest/proto/list_in_map.proto
rename to test/functest/proto/excel__nesting__list_in_map.proto
diff --git a/test/functest/proto/map_in_list.proto b/test/functest/proto/excel__nesting__map_in_list.proto
similarity index 100%
rename from test/functest/proto/map_in_list.proto
rename to test/functest/proto/excel__nesting__map_in_list.proto
diff --git a/test/functest/proto/map_in_map.proto b/test/functest/proto/excel__nesting__map_in_map.proto
similarity index 100%
rename from test/functest/proto/map_in_map.proto
rename to test/functest/proto/excel__nesting__map_in_map.proto
diff --git a/test/functest/proto/struct_in_list.proto b/test/functest/proto/excel__nesting__struct_in_list.proto
similarity index 100%
rename from test/functest/proto/struct_in_list.proto
rename to test/functest/proto/excel__nesting__struct_in_list.proto
diff --git a/test/functest/proto/struct_in_map.proto b/test/functest/proto/excel__nesting__struct_in_map.proto
similarity index 100%
rename from test/functest/proto/struct_in_map.proto
rename to test/functest/proto/excel__nesting__struct_in_map.proto
diff --git a/test/functest/proto/struct_in_struct.proto b/test/functest/proto/excel__nesting__struct_in_struct.proto
similarity index 100%
rename from test/functest/proto/struct_in_struct.proto
rename to test/functest/proto/excel__nesting__struct_in_struct.proto
diff --git a/test/functest/proto/scalar.proto b/test/functest/proto/excel__scalar__scalar.proto
similarity index 100%
rename from test/functest/proto/scalar.proto
rename to test/functest/proto/excel__scalar__scalar.proto
diff --git a/test/functest/proto/struct.proto b/test/functest/proto/excel__struct__struct.proto
similarity index 100%
rename from test/functest/proto/struct.proto
rename to test/functest/proto/excel__struct__struct.proto
diff --git a/test/functest/proto/xml__keyedlist__keyed_list.proto b/test/functest/proto/xml__keyedlist__keyed_list.proto
new file mode 100644
index 0000000..e666728
--- /dev/null
+++ b/test/functest/proto/xml__keyedlist__keyed_list.proto
@@ -0,0 +1,41 @@
+// Code generated by tableau (protogen v0.4.2). DO NOT EDIT.
+
+syntax = "proto3";
+
+package protoconf;
+
+import "tableau/protobuf/tableau.proto";
+
+option go_package = "github.com/tableauio/tableau/test/functest/protoconf";
+option (tableau.workbook) = {name:"xml/keyedlist/KeyedList.xml"};
+
+message KeyedListConf {
+  option (tableau.worksheet) = {name:"KeyedListConf" namerow:1 typerow:2 noterow:3 datarow:4 nameline:1 typeline:1 nested:true};
+
+  Parent parent = 1 [(tableau.field) = {name:"Parent"}];
+  message Parent {
+    Parent2 parent_2 = 1 [(tableau.field) = {name:"Parent2"}];
+    message Parent2 {
+      repeated CrossCell cross_cell_list = 1 [(tableau.field) = {name:"CrossCell" key:"Value" layout:LAYOUT_VERTICAL}];
+      message CrossCell {
+        int32 value = 1 [(tableau.field) = {name:"Value"}];
+        string name = 2 [(tableau.field) = {name:"Name"}];
+      }
+    }
+  }
+}
+
+message KeyedListConf2 {
+  option (tableau.worksheet) = {name:"KeyedListConf2" namerow:1 typerow:2 noterow:3 datarow:4 nameline:1 typeline:1 nested:true};
+
+  Parent parent = 1 [(tableau.field) = {name:"Parent"}];
+  message Parent {
+    Parent2 parent_2 = 1 [(tableau.field) = {name:"Parent2"}];
+    message Parent2 {
+      Incell incell = 1 [(tableau.field) = {name:"Incell"}];
+      message Incell {
+        repeated int32 value_list = 1 [(tableau.field) = {name:"Value" key:"Value" layout:LAYOUT_INCELL}];
+      }
+    }
+  }
+}
diff --git a/test/functest/proto/xml__list__list.proto b/test/functest/proto/xml__list__list.proto
new file mode 100644
index 0000000..e640488
--- /dev/null
+++ b/test/functest/proto/xml__list__list.proto
@@ -0,0 +1,41 @@
+// Code generated by tableau (protogen v0.4.2). DO NOT EDIT.
+
+syntax = "proto3";
+
+package protoconf;
+
+import "tableau/protobuf/tableau.proto";
+
+option go_package = "github.com/tableauio/tableau/test/functest/protoconf";
+option (tableau.workbook) = {name:"xml/list/List.xml"};
+
+message ListConf {
+  option (tableau.worksheet) = {name:"ListConf" namerow:1 typerow:2 noterow:3 datarow:4 nameline:1 typeline:1 nested:true};
+
+  Parent parent = 1 [(tableau.field) = {name:"Parent"}];
+  message Parent {
+    Parent2 parent_2 = 1 [(tableau.field) = {name:"Parent2"}];
+    message Parent2 {
+      repeated CrossCell cross_cell_list = 1 [(tableau.field) = {name:"CrossCell" layout:LAYOUT_VERTICAL}];
+      message CrossCell {
+        int32 value = 1 [(tableau.field) = {name:"Value"}];
+        string name = 2 [(tableau.field) = {name:"Name"}];
+      }
+    }
+  }
+}
+
+message ListConf2 {
+  option (tableau.worksheet) = {name:"ListConf2" namerow:1 typerow:2 noterow:3 datarow:4 nameline:1 typeline:1 nested:true};
+
+  Parent parent = 1 [(tableau.field) = {name:"Parent"}];
+  message Parent {
+    Parent2 parent_2 = 1 [(tableau.field) = {name:"Parent2"}];
+    message Parent2 {
+      Incell incell = 1 [(tableau.field) = {name:"Incell"}];
+      message Incell {
+        repeated int32 value_list = 1 [(tableau.field) = {name:"Value" layout:LAYOUT_INCELL}];
+      }
+    }
+  }
+}
diff --git a/test/functest/proto/xml__map__map.proto b/test/functest/proto/xml__map__map.proto
new file mode 100644
index 0000000..4394eb7
--- /dev/null
+++ b/test/functest/proto/xml__map__map.proto
@@ -0,0 +1,21 @@
+// Code generated by tableau (protogen v0.4.2). DO NOT EDIT.
+
+syntax = "proto3";
+
+package protoconf;
+
+import "tableau/protobuf/tableau.proto";
+
+option go_package = "github.com/tableauio/tableau/test/functest/protoconf";
+option (tableau.workbook) = {name:"xml/map/Map.xml"};
+
+message MapConf {
+  option (tableau.worksheet) = {name:"MapConf" namerow:1 typerow:2 noterow:3 datarow:4 nameline:1 typeline:1 nested:true};
+
+  bool open = 1 [(tableau.field) = {name:"Open"}];
+  map<uint32, Entry> entry_map = 2 [(tableau.field) = {name:"Entry" key:"Id" layout:LAYOUT_VERTICAL}];
+  message Entry {
+    uint32 id = 1 [(tableau.field) = {name:"Id"}];
+    int32 value = 2 [(tableau.field) = {name:"Value"}];
+  }
+}
diff --git a/test/functest/proto/xml__metasheet__metasheet.proto b/test/functest/proto/xml__metasheet__metasheet.proto
new file mode 100644
index 0000000..31991d1
--- /dev/null
+++ b/test/functest/proto/xml__metasheet__metasheet.proto
@@ -0,0 +1,28 @@
+// Code generated by tableau (protogen v0.4.2). DO NOT EDIT.
+
+syntax = "proto3";
+
+package protoconf;
+
+import "tableau/protobuf/tableau.proto";
+
+option go_package = "github.com/tableauio/tableau/test/functest/protoconf";
+option (tableau.workbook) = {name:"xml/metasheet/Metasheet.xml"};
+
+message MetasheetConf {
+  option (tableau.worksheet) = {name:"MetasheetConf" namerow:1 typerow:2 noterow:3 datarow:4 nameline:1 typeline:1 nested:true sep:";"};
+
+  SepConf sep_conf = 1 [(tableau.field) = {name:"SepConf"}];
+  message SepConf {
+    repeated int32 number_list = 1 [(tableau.field) = {name:"number" layout:LAYOUT_INCELL}];
+  }
+}
+
+message TemplateConf {
+  option (tableau.worksheet) = {name:"TemplateConf" namerow:1 typerow:2 noterow:3 datarow:4 nameline:1 typeline:1 nested:true template:true};
+
+  OutputType output_type = 1 [(tableau.field) = {name:"OutputType"}];
+  message OutputType {
+    int32 type = 1 [(tableau.field) = {name:"Type"}];
+  }
+}
diff --git a/test/functest/testdata/xml/keyedlist/KeyedList.xml b/test/functest/testdata/xml/keyedlist/KeyedList.xml
new file mode 100644
index 0000000..fc280e7
--- /dev/null
+++ b/test/functest/testdata/xml/keyedlist/KeyedList.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+<@TABLEAU>
+    <Item Sheet="KeyedListConf" />
+    <Item Sheet="KeyedListConf2" />
+</@TABLEAU>
+
+<KeyedListConf>
+    <Parent>
+        <Parent2>
+            <CrossCell Value="[CrossCell]<int32>" Name="string" />
+        </Parent2>
+    </Parent>
+</KeyedListConf>
+
+<KeyedListConf2>
+    <Parent>
+        <Parent2>
+            <Incell Value="[]<int32>" />
+        </Parent2>
+    </Parent>
+</KeyedListConf2>
+-->
+
+<KeyedListConf>
+    <Parent>
+        <Parent2>
+            <CrossCell Value="1" Name="Apple" />
+            <CrossCell Value="2" Name="Banana" />
+            <CrossCell Value="3" Name="Orange" />
+            <CrossCell Value="1" Name="Grape" />    
+        </Parent2>
+    </Parent>
+</KeyedListConf>
+
+<KeyedListConf2>
+    <Parent>
+        <Parent2>
+            <Incell Value="1,2,3,1" />
+        </Parent2>
+    </Parent>
+</KeyedListConf2>
diff --git a/test/functest/testdata/xml/list/List.xml b/test/functest/testdata/xml/list/List.xml
new file mode 100644
index 0000000..96d60c7
--- /dev/null
+++ b/test/functest/testdata/xml/list/List.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+<@TABLEAU>
+    <Item Sheet="ListConf" />
+    <Item Sheet="ListConf2" />
+</@TABLEAU>
+
+<ListConf>
+    <Parent>
+        <Parent2>
+            <CrossCell Value="[CrossCell]int32" Name="string" />
+        </Parent2>
+    </Parent>
+</ListConf>
+
+<ListConf2>
+    <Parent>
+        <Parent2>
+            <Incell Value="[]int32" />
+        </Parent2>
+    </Parent>
+</ListConf2>
+-->
+
+<ListConf>
+    <Parent>
+        <Parent2>
+            <CrossCell Value="1" Name="Apple" />
+            <CrossCell Value="2" Name="Banana" />
+            <CrossCell Value="3" Name="Orange" />
+            <CrossCell Value="1" Name="Grape" />    
+        </Parent2>
+    </Parent>
+</ListConf>
+
+<ListConf2>
+    <Parent>
+        <Parent2>
+            <Incell Value="1,2,3,1" />
+        </Parent2>
+    </Parent>
+</ListConf2>
diff --git a/test/functest/testdata/xml/map/Map.xml b/test/functest/testdata/xml/map/Map.xml
new file mode 100644
index 0000000..f278b48
--- /dev/null
+++ b/test/functest/testdata/xml/map/Map.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+<@TABLEAU>
+    <Item Sheet="MapConf" />
+</@TABLEAU>
+
+<MapConf Open="bool">
+    <Entry Id="map<uint32, Entry>" Value="int32" />
+</MapConf>
+-->
+
+<MapConf Open="true">
+    <Entry Id="1" Value="1" />
+    <Entry Id="2" Value="2" />
+    <Entry Id="3" Value="3" />
+</MapConf>
diff --git a/test/functest/testdata/xml/metasheet/Metasheet.xml b/test/functest/testdata/xml/metasheet/Metasheet.xml
new file mode 100644
index 0000000..c697c12
--- /dev/null
+++ b/test/functest/testdata/xml/metasheet/Metasheet.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+<@TABLEAU>
+    <Item Sheet="MetasheetConf" Sep=";" />
+    <Item Sheet="TemplateConf" Template="true" />
+</@TABLEAU>
+
+<MetasheetConf>
+    <SepConf number="[]int32" />
+</MetasheetConf>
+
+<TemplateConf>
+    <OutputType Type="int32" />
+</TemplateConf>
+-->
+
+<MetasheetConf>
+    <SepConf number="1;2;3" />
+</MetasheetConf>
+
+<TemplateConf>
+    {% if proc.name == 'gamesvr' %}
+    <OutputType Type="{{ proc.output_type|default('0') }}" />
+    {% endif %}
+</TemplateConf>
diff --git a/test/functest/util.go b/test/functest/util.go
index 9435483..5735bee 100644
--- a/test/functest/util.go
+++ b/test/functest/util.go
@@ -71,7 +71,7 @@ func genProto(t *testing.T) {
 		options.Output(
 			&options.OutputOption{
 				Proto: &options.OutputProtoOption{
-					FilenameWithSubdirPrefix: false,
+					FilenameWithSubdirPrefix: true,
 					FileOptions: map[string]string{
 						"go_package": "github.com/tableauio/tableau/test/functest/protoconf",
 					},