Repository: tableauio/tableau
Pull request #121 (2024-08-05 14:11:52)

confgen: control field name presence at field-level and sheet-level
-  [x] close https://github.com/tableauio/tableau/issues/116 
-  [x] close https://github.com/tableauio/tableau/issues/120 

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/tableauio/tableau/pull/121?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio) Report
Attention: Patch coverage is `73.52941%` with `9 lines` in your changes missing coverage. Please review.
> Project coverage is 66.72%. Comparing base [(`d2d37c7`)](https://app.codecov.io/gh/tableauio/tableau/commit/d2d37c7a206f427a320f037b646abeb80d9d38db?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio) to head [(`94e6957`)](https://app.codecov.io/gh/tableauio/tableau/commit/94e695775142e93cf2a0a438fbbe142a871466a9?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio).

| [Files](https://app.codecov.io/gh/tableauio/tableau/pull/121?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio) | Patch % | Lines |
|---|---|---|
| [internal/confgen/document\_parser.go](https://app.codecov.io/gh/tableauio/tableau/pull/121?src=pr&el=tree&filepath=internal%2Fconfgen%2Fdocument_parser.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio#diff-aW50ZXJuYWwvY29uZmdlbi9kb2N1bWVudF9wYXJzZXIuZ28=) | 22.22% | [7 Missing :warning: ](https://app.codecov.io/gh/tableauio/tableau/pull/121?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio) |
| [internal/confgen/parser.go](https://app.codecov.io/gh/tableauio/tableau/pull/121?src=pr&el=tree&filepath=internal%2Fconfgen%2Fparser.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio#diff-aW50ZXJuYWwvY29uZmdlbi9wYXJzZXIuZ28=) | 89.47% | [2 Missing :warning: ](https://app.codecov.io/gh/tableauio/tableau/pull/121?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio) |

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


```diff
@@            Coverage Diff             @@
##           master     #121      +/-   ##
==========================================
- Coverage   66.77%   66.72%   -0.06%     
==========================================
  Files          44       44              
  Lines        5532     5544      +12     
==========================================
+ Hits         3694     3699       +5     
- Misses       1357     1364       +7     
  Partials      481      481              
```

</details>

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


Patch:
diff --git a/internal/confgen/document_parser.go b/internal/confgen/document_parser.go
index c25a8ae..11bd4e3 100644
--- a/internal/confgen/document_parser.go
+++ b/internal/confgen/document_parser.go
@@ -57,19 +57,17 @@ func (sp *documentParser) parseMessage(msg protoreflect.Message, node *book.Node
 			} else {
 				fieldNode = node.FindChild(field.opts.Name)
 				if fieldNode == nil {
-					// if field.opts.Optional {
-					// 	// field not found and is optional, no need to process, just return.
-					// 	return nil
-					// }
-					// kvs := node.DebugNameKV()
-					// kvs = append(kvs,
-					// 	xerrors.KeyPBFieldType, xproto.GetFieldTypeName(fd),
-					// 	xerrors.KeyPBFieldName, fd.FullName(),
-					// 	xerrors.KeyPBFieldOpts, field.opts,
-					// )
-					// return xerrors.WrapKV(xerrors.E2014(field.opts.Name), kvs...)
-					// TODO: return err when required
-					return nil
+					if sp.parser.IsFieldOptional(field) {
+						// field not found and is optional, just return nil.
+						return nil
+					}
+					kvs := node.DebugNameKV()
+					kvs = append(kvs,
+						xerrors.KeyPBFieldType, xproto.GetFieldTypeName(fd),
+						xerrors.KeyPBFieldName, fd.FullName(),
+						xerrors.KeyPBFieldOpts, field.opts,
+					)
+					return xerrors.WrapKV(xerrors.E2014(field.opts.Name), kvs...)
 				}
 			}
 			fieldPresent, err := sp.parseField(field, msg, fieldNode)
diff --git a/internal/confgen/parser.go b/internal/confgen/parser.go
index 311d2db..58d8957 100644
--- a/internal/confgen/parser.go
+++ b/internal/confgen/parser.go
@@ -234,6 +234,13 @@ func (sp *sheetParser) GetBookFormat() format.Format {
 	return sp.extInfo.BookFormat
 }
 
+// IsFieldOptional returns whether this field is optional (field name existence).
+//   - table formats (Excel/CSV): field's column can be absent.
+//   - document formats (XML/YAML): field's name can be absent.
+func (sp *sheetParser) IsFieldOptional(field *Field) bool {
+	return sp.opts.GetOptional() || field.opts.GetProp().GetOptional()
+}
+
 func (sp *sheetParser) Parse(protomsg proto.Message, sheet *book.Sheet) error {
 	if sheet.Document != nil {
 		docParser := &documentParser{parser: sp}
@@ -408,7 +415,7 @@ func (sp *sheetParser) parseMapField(field *Field, msg protoreflect.Message, rc
 	case tableaupb.Layout_LAYOUT_VERTICAL:
 		if valueFd.Kind() == protoreflect.MessageKind {
 			keyColName := prefix + field.opts.Name + field.opts.Key
-			cell, err := rc.Cell(keyColName, field.opts.Optional)
+			cell, err := rc.Cell(keyColName, sp.IsFieldOptional(field))
 			if err != nil {
 				return false, xerrors.WithMessageKV(err, rc.CellDebugKV(keyColName)...)
 			}
@@ -456,7 +463,7 @@ func (sp *sheetParser) parseMapField(field *Field, msg protoreflect.Message, rc
 			value := types.DefaultMapValueOptName // default value name
 			// key cell
 			keyColName := prefix + field.opts.Name + key
-			cell, err := rc.Cell(keyColName, field.opts.Optional)
+			cell, err := rc.Cell(keyColName, sp.IsFieldOptional(field))
 			if err != nil {
 				return false, xerrors.WithMessageKV(err, rc.CellDebugKV(keyColName)...)
 			}
@@ -468,7 +475,7 @@ func (sp *sheetParser) parseMapField(field *Field, msg protoreflect.Message, rc
 			newMapKey := fieldValue.MapKey()
 			// value cell
 			valueColName := prefix + field.opts.Name + value
-			cell, err = rc.Cell(valueColName, field.opts.Optional)
+			cell, err = rc.Cell(valueColName, sp.IsFieldOptional(field))
 			if err != nil {
 				return false, xerrors.WithMessageKV(err, rc.CellDebugKV(valueColName)...)
 			}
@@ -517,7 +524,7 @@ func (sp *sheetParser) parseMapField(field *Field, msg protoreflect.Message, rc
 			// log.Debug("prefix size: ", size)
 			for i := 1; i <= size; i++ {
 				keyColName := prefix + field.opts.Name + strconv.Itoa(i) + field.opts.Key
-				cell, err := rc.Cell(keyColName, field.opts.Optional)
+				cell, err := rc.Cell(keyColName, sp.IsFieldOptional(field))
 				if err != nil {
 					return false, xerrors.WithMessageKV(err, rc.CellDebugKV(keyColName)...)
 				}
@@ -574,7 +581,7 @@ func (sp *sheetParser) parseMapField(field *Field, msg protoreflect.Message, rc
 
 	case tableaupb.Layout_LAYOUT_INCELL:
 		colName := prefix + field.opts.Name
-		cell, err := rc.Cell(colName, field.opts.Optional)
+		cell, err := rc.Cell(colName, sp.IsFieldOptional(field))
 		if err != nil {
 			return false, xerrors.WithMessageKV(err, rc.CellDebugKV(colName)...)
 		}
@@ -849,7 +856,7 @@ func (sp *sheetParser) parseListField(field *Field, msg protoreflect.Message, rc
 				if fd == nil {
 					return false, xerrors.ErrorKV(fmt.Sprintf("key field not found in proto definition: %s", keyProtoName), rc.CellDebugKV(keyColName)...)
 				}
-				cell, err := rc.Cell(keyColName, field.opts.Optional)
+				cell, err := rc.Cell(keyColName, sp.IsFieldOptional(field))
 				if err != nil {
 					return false, xerrors.WithMessageKV(err, rc.CellDebugKV(keyColName)...)
 				}
@@ -881,7 +888,7 @@ func (sp *sheetParser) parseListField(field *Field, msg protoreflect.Message, rc
 				if field.opts.Span == tableaupb.Span_SPAN_INNER_CELL {
 					// incell-struct list
 					colName := prefix + field.opts.Name
-					cell, err := rc.Cell(colName, field.opts.Optional)
+					cell, err := rc.Cell(colName, sp.IsFieldOptional(field))
 					if err != nil {
 						return false, xerrors.WithMessageKV(err, rc.CellDebugKV(colName)...)
 					}
@@ -929,7 +936,7 @@ func (sp *sheetParser) parseListField(field *Field, msg protoreflect.Message, rc
 			if field.fd.Kind() == protoreflect.MessageKind {
 				if field.opts.Span == tableaupb.Span_SPAN_INNER_CELL {
 					// horizontal incell-struct list
-					cell, err := rc.Cell(colName, field.opts.Optional)
+					cell, err := rc.Cell(colName, sp.IsFieldOptional(field))
 					if err != nil {
 						return false, xerrors.WithMessageKV(err, rc.CellDebugKV(colName)...)
 					}
@@ -950,7 +957,7 @@ func (sp *sheetParser) parseListField(field *Field, msg protoreflect.Message, rc
 					subMsgName := string(field.fd.Message().FullName())
 					if types.IsWellKnownMessage(subMsgName) {
 						// built-in message type: google.protobuf.Timestamp, google.protobuf.Duration
-						cell, err := rc.Cell(colName, field.opts.Optional)
+						cell, err := rc.Cell(colName, sp.IsFieldOptional(field))
 						if err != nil {
 							kvs := rc.CellDebugKV(colName)
 							return false, xerrors.WithMessageKV(err, kvs...)
@@ -982,7 +989,7 @@ func (sp *sheetParser) parseListField(field *Field, msg protoreflect.Message, rc
 				list.Append(newListValue)
 			} else {
 				// scalar list
-				cell, err := rc.Cell(colName, field.opts.Optional)
+				cell, err := rc.Cell(colName, sp.IsFieldOptional(field))
 				if err != nil {
 					return false, xerrors.WithMessageKV(err, rc.CellDebugKV(colName)...)
 				}
@@ -1014,7 +1021,7 @@ func (sp *sheetParser) parseListField(field *Field, msg protoreflect.Message, rc
 	case tableaupb.Layout_LAYOUT_INCELL:
 		// incell list
 		colName := prefix + field.opts.Name
-		cell, err := rc.Cell(colName, field.opts.Optional)
+		cell, err := rc.Cell(colName, sp.IsFieldOptional(field))
 		if err != nil {
 			return false, xerrors.WithMessageKV(err, rc.CellDebugKV(colName)...)
 		}
@@ -1113,7 +1120,7 @@ func (sp *sheetParser) parseStructField(field *Field, msg protoreflect.Message,
 	colName := prefix + field.opts.Name
 	if field.opts.Span == tableaupb.Span_SPAN_INNER_CELL {
 		// incell struct
-		cell, err := rc.Cell(colName, field.opts.Optional)
+		cell, err := rc.Cell(colName, sp.IsFieldOptional(field))
 		if err != nil {
 			return false, xerrors.WithMessageKV(err, rc.CellDebugKV(colName)...)
 		}
@@ -1129,7 +1136,7 @@ func (sp *sheetParser) parseStructField(field *Field, msg protoreflect.Message,
 		subMsgName := string(field.fd.Message().FullName())
 		if types.IsWellKnownMessage(subMsgName) {
 			// built-in message type: google.protobuf.Timestamp, google.protobuf.Duration
-			cell, err := rc.Cell(colName, field.opts.Optional)
+			cell, err := rc.Cell(colName, sp.IsFieldOptional(field))
 			if err != nil {
 				return false, xerrors.WithMessageKV(err, rc.CellDebugKV(colName)...)
 			}
@@ -1206,7 +1213,7 @@ func (sp *sheetParser) parseUnionField(field *Field, msg protoreflect.Message, r
 	if field.opts.Span == tableaupb.Span_SPAN_INNER_CELL {
 		colName := prefix + field.opts.Name
 		// incell union
-		cell, err := rc.Cell(colName, field.opts.Optional)
+		cell, err := rc.Cell(colName, sp.IsFieldOptional(field))
 		if err != nil {
 			return false, xerrors.WithMessageKV(err, rc.CellDebugKV(colName)...)
 		}
@@ -1226,7 +1233,7 @@ func (sp *sheetParser) parseUnionField(field *Field, msg protoreflect.Message, r
 
 	// parse union type
 	typeColName := prefix + field.opts.Name + unionDesc.TypeName()
-	cell, err := rc.Cell(typeColName, field.opts.Optional)
+	cell, err := rc.Cell(typeColName, sp.IsFieldOptional(field))
 	if err != nil {
 		return false, xerrors.WithMessageKV(err, rc.CellDebugKV(typeColName)...)
 	}
@@ -1260,7 +1267,7 @@ func (sp *sheetParser) parseUnionField(field *Field, msg protoreflect.Message, r
 				subField := parseFieldDescriptor(fd, sp.opts.Sep, sp.opts.Subsep)
 				defer subField.release()
 				// incell scalar
-				cell, err := rc.Cell(colName, subField.opts.Optional)
+				cell, err := rc.Cell(colName, sp.IsFieldOptional(subField))
 				if err != nil {
 					return xerrors.WithMessageKV(err, rc.CellDebugKV(colName)...)
 				}
@@ -1370,7 +1377,7 @@ func (sp *sheetParser) parseScalarField(field *Field, msg protoreflect.Message,
 	}
 	var newValue protoreflect.Value
 	colName := prefix + field.opts.Name
-	cell, err := rc.Cell(colName, field.opts.Optional)
+	cell, err := rc.Cell(colName, sp.IsFieldOptional(field))
 	if err != nil {
 		return false, xerrors.WithMessageKV(err, rc.CellDebugKV(colName)...)
 	}
diff --git a/internal/confgen/util.go b/internal/confgen/util.go
index dd65e27..2a44dfd 100644
--- a/internal/confgen/util.go
+++ b/internal/confgen/util.go
@@ -52,7 +52,6 @@ func parseFieldDescriptor(fd protoreflect.FieldDescriptor, sheetSep, sheetSubsep
 	layout := tableaupb.Layout_LAYOUT_DEFAULT
 	sep := ""
 	subsep := ""
-	optional := false
 	var prop *tableaupb.FieldProp
 
 	// opts := fd.Options().(*descriptorpb.FieldOptions)
@@ -65,7 +64,6 @@ func parseFieldDescriptor(fd protoreflect.FieldDescriptor, sheetSep, sheetSubsep
 		layout = fieldOpts.Layout
 		sep = strings.TrimSpace(fieldOpts.Sep)
 		subsep = strings.TrimSpace(fieldOpts.Subsep)
-		optional = fieldOpts.Optional
 		prop = fieldOpts.Prop
 	} else {
 		// default processing
@@ -100,7 +98,6 @@ func parseFieldDescriptor(fd protoreflect.FieldDescriptor, sheetSep, sheetSubsep
 	pooledOpts.Layout = layout
 	pooledOpts.Sep = sep
 	pooledOpts.Subsep = subsep
-	pooledOpts.Optional = optional
 	pooledOpts.Prop = prop
 
 	return &Field{
diff --git a/internal/importer/xml.go b/internal/importer/xml.go
index 775278e..03304f7 100644
--- a/internal/importer/xml.go
+++ b/internal/importer/xml.go
@@ -67,7 +67,8 @@ func NewXMLImporter(filename string, sheets []string, parser book.SheetParser, m
 			Book: book.NewBook(bookName, filename, nil),
 		}, nil
 	}
-	log.Debugf("newBook:%+v", newBook)
+
+	// log.Debugf("book: %+v", newBook)
 
 	return &XMLImporter{
 		Book: newBook,
diff --git a/internal/importer/yaml.go b/internal/importer/yaml.go
index cc461fc..72e8369 100644
--- a/internal/importer/yaml.go
+++ b/internal/importer/yaml.go
@@ -29,6 +29,8 @@ func NewYAMLImporter(filename string, sheetNames []string, parser book.SheetPars
 		}
 	}
 
+	// log.Debugf("book: %+v", book)
+
 	return &YAMLImporter{
 		Book: book,
 	}, nil
@@ -91,6 +93,10 @@ func parseYAMLNode(node *yaml.Node, bnode *book.Node) error {
 		for _, child := range node.Content {
 			subNode := &book.Node{
 				Value: child.Value,
+				NamePos: book.Position{
+					Line:   child.Line,
+					Column: child.Column,
+				},
 				ValuePos: book.Position{
 					Line:   child.Line,
 					Column: child.Column,
@@ -134,6 +140,10 @@ func parseYAMLNode(node *yaml.Node, bnode *book.Node) error {
 			subNode := &book.Node{
 				Name:  "",
 				Value: elem.Value,
+				NamePos: book.Position{
+					Line:   elem.Line,
+					Column: elem.Column,
+				},
 				ValuePos: book.Position{
 					Line:   elem.Line,
 					Column: elem.Column,
diff --git a/internal/protogen/field_prop.go b/internal/protogen/field_prop.go
index a8541ff..8d5b871 100644
--- a/internal/protogen/field_prop.go
+++ b/internal/protogen/field_prop.go
@@ -23,6 +23,7 @@ func ExtractMapFieldProp(prop *tableaupb.FieldProp) *tableaupb.FieldProp {
 		Fixed:    prop.Fixed,
 		Size:     prop.Size,
 		Present:  prop.Present,
+		Optional: prop.Optional,
 	}
 	if IsEmptyFieldProp(p) {
 		return nil
@@ -42,6 +43,7 @@ func ExtractListFieldProp(prop *tableaupb.FieldProp, isScalarList bool) *tableau
 		Fixed:    prop.Fixed,
 		Size:     prop.Size,
 		Present:  prop.Present,
+		Optional: prop.Optional,
 	}
 	if isScalarList {
 		p.Range = prop.Range
@@ -62,6 +64,7 @@ func ExtractStructFieldProp(prop *tableaupb.FieldProp) *tableaupb.FieldProp {
 		JsonName: prop.JsonName,
 		Form:     prop.Form,
 		Present:  prop.Present,
+		Optional: prop.Optional,
 	}
 	if IsEmptyFieldProp(p) {
 		return nil
@@ -80,6 +83,7 @@ func ExtractScalarFieldProp(prop *tableaupb.FieldProp) *tableaupb.FieldProp {
 		Refer:    prop.Refer,
 		Default:  prop.Default,
 		Present:  prop.Present,
+		Optional: prop.Optional,
 	}
 	if IsEmptyFieldProp(p) {
 		return nil
diff --git a/internal/protogen/protogen.go b/internal/protogen/protogen.go
index 789618f..a64c578 100644
--- a/internal/protogen/protogen.go
+++ b/internal/protogen/protogen.go
@@ -329,6 +329,7 @@ func (gen *Generator) convertDocument(dir, filename string, checkProtoFileConfli
 				Template:      sheet.Meta.Template,
 				Mode:          sheet.Meta.Mode,
 				Scatter:       sheet.Meta.Scatter,
+				Optional:      sheet.Meta.Optional,
 				// Loader options:
 				OrderedMap: sheet.Meta.OrderedMap,
 				Index:      parseIndexes(sheet.Meta.Index),
@@ -456,6 +457,7 @@ func (gen *Generator) convertTable(dir, filename string, checkProtoFileConflicts
 				Template:      sheet.Meta.Template,
 				Mode:          sheet.Meta.Mode,
 				Scatter:       sheet.Meta.Scatter,
+				Optional:      sheet.Meta.Optional,
 				// Loader options:
 				OrderedMap: sheet.Meta.OrderedMap,
 				Index:      parseIndexes(sheet.Meta.Index),
diff --git a/proto/tableau/protobuf/metabook.proto b/proto/tableau/protobuf/metabook.proto
index 4e25c5a..3b97768 100644
--- a/proto/tableau/protobuf/metabook.proto
+++ b/proto/tableau/protobuf/metabook.proto
@@ -20,30 +20,30 @@ message Metabook {
 }
 
 message Metasheet {
-  string sheet = 1 [(tableau.field) = { name: "Sheet" optional: false }];
-  string alias = 2 [(tableau.field) = { name: "Alias" optional: true }];
-  int32 namerow = 3 [(tableau.field) = { name: "Namerow" optional: true }];
-  int32 typerow = 4 [(tableau.field) = { name: "Typerow" optional: true }];
-  int32 noterow = 5 [(tableau.field) = { name: "Noterow" optional: true }];
-  int32 datarow = 6 [(tableau.field) = { name: "Datarow" optional: true }];
-  int32 nameline = 7 [(tableau.field) = { name: "Nameline" optional: true }];
-  int32 typeline = 8 [(tableau.field) = { name: "Typeline" optional: true }];
-  bool transpose = 9 [(tableau.field) = { name: "Transpose" optional: true }];
+  string sheet = 1 [(tableau.field) = {name:"Sheet"}];
+  string alias = 2 [(tableau.field) = {name:"Alias" prop:{optional:true}}];
+  int32 namerow = 3 [(tableau.field) = {name:"Namerow" prop:{optional:true}}];
+  int32 typerow = 4 [(tableau.field) = {name:"Typerow" prop:{optional:true}}];
+  int32 noterow = 5 [(tableau.field) = {name:"Noterow" prop:{optional:true}}];
+  int32 datarow = 6 [(tableau.field) = {name:"Datarow" prop:{optional:true}}];
+  int32 nameline = 7 [(tableau.field) = {name:"Nameline" prop:{optional:true}}];
+  int32 typeline = 8 [(tableau.field) = {name:"Typeline" prop:{optional:true}}];
+  bool transpose = 9 [(tableau.field) = {name:"Transpose" prop:{optional:true}}];
   // nested naming of namerow
-  bool nested = 10 [(tableau.field) = { name: "Nested" optional: true }];
-  string sep = 11 [(tableau.field) = { name: "Sep" optional: true }];
-  string subsep = 12 [(tableau.field) = { name: "Subsep" optional: true }];
+  bool nested = 10 [(tableau.field) = {name:"Nested" prop:{optional:true}}];
+  string sep = 11 [(tableau.field) = {name:"Sep" prop:{optional:true}}];
+  string subsep = 12 [(tableau.field) = {name:"Subsep" prop:{optional:true}}];
   // merge multiple sheets with same schema to one.
   // each element is:
   // - a workbook name or Glob(https://pkg.go.dev/path/filepath#Glob) to merge (relative to this workbook): <Workbook>,
   //   then the sheet name is the same as this sheet.
   // - or a workbook name (relative to this workbook) with a worksheet name: <Workbook>#<Worksheet>.
-  repeated string merger = 13 [(tableau.field) = { name: "Merger" optional: true layout: LAYOUT_INCELL }];
+  repeated string merger = 13 [(tableau.field) = {name:"Merger" layout:LAYOUT_INCELL prop:{optional:true}}];
   // Tableau will merge adjacent rows with the same key. If the key cell is not set,
   // it will be treated as the same as the most nearest key above the same column.
   //
   // This option is only useful for map or keyed-list.
-  bool adjacent_key = 14 [(tableau.field) = { name: "AdjacentKey" optional: true }];
+  bool adjacent_key = 14 [(tableau.field) = {name:"AdjacentKey" prop:{optional:true}}];
   // Field presence is the notion of whether a protobuf field has a value. If set as true,
   // in order to track presence for basic types (numeric, string, bytes, and enums), the
   // generated .proto will add the `optional` label to them.
@@ -51,22 +51,27 @@ message Metasheet {
   // Singular proto3 fields of basic types (numeric, string, bytes, and enums) which are defined
   // 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 }];
+  bool field_presence = 15 [(tableau.field) = {name:"FieldPresence" prop:{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 }];
+  bool template = 16 [(tableau.field) = {name:"Template" prop:{optional:true}}];
   // Sheet mode.
-  Mode mode = 17 [(tableau.field) = { name: "Mode" optional: true }];
+  Mode mode = 17 [(tableau.field) = {name:"Mode" prop:{optional:true}}];
   // Scatter converts sheets separately with same schema.
   // each element is:
   // - a workbook name or Glob(https://pkg.go.dev/path/filepath#Glob) which is relative to this workbook: <Workbook>,
   //   then the sheet name is the same as this sheet.
   // - or a workbook name which is relative to this workbook with a worksheet name: <Workbook>#<Worksheet>.
-  repeated string scatter = 18 [(tableau.field) = { name: "Scatter" optional: true layout: LAYOUT_INCELL }];
+  repeated string scatter = 18 [(tableau.field) = {name:"Scatter" layout:LAYOUT_INCELL prop:{optional:true}}];
+  // Whether all fields in this sheet are optional (field name existence).
+  // If set to true, then:
+  //  - table formats (Excel/CSV): field's column can be absent.
+  //  - document formats (XML/YAML): field's name can be absent.
+  bool optional = 19 [(tableau.field) = {name:"Optional" prop:{optional:true}}];
 
   ////////// Loader related options below //////////
   // Generate ordered map accessers
-  bool ordered_map = 50 [(tableau.field) = { name: "OrderedMap" optional: true }];
+  bool ordered_map = 50 [(tableau.field) = {name:"OrderedMap" prop:{optional:true}}];
   // Generate index accessers, and multiple index columns are comma-separated.
   // Format: <ColumnName>[@IndexName], if IndexName is not set, it will be this
   // column’s parent struct type name.
@@ -85,7 +90,7 @@ message Metasheet {
   // C++:
   // - const std::vector<const STRUCT_TYPE*>& Find<IndexName>(INDEX_TYPE index) const;
   // - const STRUCT_TYPE* FindFirst<IndexName>(INDEX_TYPE index);
-  string index = 51 [(tableau.field) = { name: "Index" optional: true }];
+  string index = 51 [(tableau.field) = {name:"Index" prop:{optional:true}}];
 }
 
 // EnumDescriptor represents enum type definition in sheet.
@@ -95,11 +100,11 @@ message EnumDescriptor {
     datarow: 2
   };
 
-  repeated Value values = 1 [(tableau.field) = { layout: LAYOUT_VERTICAL }];
+  repeated Value values = 1 [(tableau.field) = {layout:LAYOUT_VERTICAL}];
   message Value {
-    optional int32 number = 1 [(tableau.field) = { name: "Number" optional: true }];
-    string name = 2 [(tableau.field) = { name: "Name" }];
-    string alias = 3 [(tableau.field) = { name: "Alias" }];
+    optional int32 number = 1 [(tableau.field) = {name:"Number" prop:{optional:true}}];
+    string name = 2 [(tableau.field) = {name:"Name"}];
+    string alias = 3 [(tableau.field) = {name:"Alias"}];
   }
 }
 
@@ -110,10 +115,10 @@ message StructDescriptor {
     datarow: 2
   };
 
-  repeated Field fields = 1 [(tableau.field) = { layout: LAYOUT_VERTICAL }];
+  repeated Field fields = 1 [(tableau.field) = {layout:LAYOUT_VERTICAL}];
   message Field {
-    string name = 1 [(tableau.field) = { name: "Name" }];
-    string type = 2 [(tableau.field) = { name: "Type" }];
+    string name = 1 [(tableau.field) = {name:"Name"}];
+    string type = 2 [(tableau.field) = {name:"Type"}];
   }
 }
 
@@ -124,13 +129,13 @@ message UnionDescriptor {
     datarow: 2
   };
 
-  repeated Value values = 1 [(tableau.field) = { layout: LAYOUT_VERTICAL }];
+  repeated Value values = 1 [(tableau.field) = {layout:LAYOUT_VERTICAL}];
   message Value {
-    optional int32 number = 1 [(tableau.field) = { name: "Number" optional: true }];
+    optional int32 number = 1 [(tableau.field) = {name:"Number" prop:{optional:true}}];
     // This is message type name, and the corresponding enum value name
     // is generated as: "TYPE_" + strcase.ToScreamingSnake(name).
-    string name = 2 [(tableau.field) = { name: "Name" }];
-    string alias = 3 [(tableau.field) = { name: "Alias" }];
-    repeated string fields = 4 [(tableau.field) = { name: "Field" layout: LAYOUT_HORIZONTAL }];
+    string name = 2 [(tableau.field) = {name:"Name"}];
+    string alias = 3 [(tableau.field) = {name:"Alias"}];
+    repeated string fields = 4 [(tableau.field) = {name:"Field" layout:LAYOUT_HORIZONTAL}];
   }
 }
diff --git a/proto/tableau/protobuf/tableau.proto b/proto/tableau/protobuf/tableau.proto
index 0b8bd1a..e462b9d 100644
--- a/proto/tableau/protobuf/tableau.proto
+++ b/proto/tableau/protobuf/tableau.proto
@@ -109,6 +109,11 @@ message WorksheetOptions {
   // Scatter convert multiple workbook sheets (comma-separated) separately
   // with same schema. E.g.: Item1.xlsx,Item2.xlsx,ItemAward*.xlsx.
   repeated string scatter = 18;
+  // Whether all fields in this sheet are optional (field name existence).
+  // If set to true, then:
+  //  - table formats (Excel/CSV): field's column can be absent.
+  //  - document formats (XML/YAML): field's name can be absent.
+  bool optional = 19;
 
   ////////// Loader related options below //////////
   // Generate OrderedMap accessers or not.
@@ -148,7 +153,6 @@ message FieldOptions {
   Span span = 5;      // For list element or map value types. Default: SPAN_CROSS_CELL.
   string sep = 6;     // NOT USED yet. Default: ",".
   string subsep = 7;  // NOT USED yet. Default: ":".
-  bool optional = 8;  // Whether the field is optional.
 
   FieldProp prop = 15;  // Property of field.
 }
@@ -194,9 +198,18 @@ message FieldProp {
   // will be used. Otherwise, it's deduced from the field's name by converting
   // it to camelCase.
   string json_name = 9;
-  // Must fill cell data explicitly if present is true, otherwise
-  // an error will be reported.
+  // Whether this field value is present (field value existence).
+  // If set to true, then do the following checks for different field types:
+  //  - scalar/enum: cell data cannot be empty string (TODO: If this field's
+  //    type is string, then how to set empty string explicitly?)
+  //  - struct: check at least one field is present recursively
+  //  - map/list: len(elements) > 0
   bool present = 10;
+  // Whether this field is optional (field name existence).
+  // If set to true, then:
+  //  - table formats (Excel/CSV): field's column can be absent.
+  //  - document formats (XML/YAML): field's name can be absent.
+  bool optional = 11;
 }
 
 // Layout of list and map.
diff --git a/proto/tableaupb/metabook.pb.go b/proto/tableaupb/metabook.pb.go
index 8e810f0..befd838 100644
--- a/proto/tableaupb/metabook.pb.go
+++ b/proto/tableaupb/metabook.pb.go
@@ -118,6 +118,11 @@ type Metasheet struct {
 	//     then the sheet name is the same as this sheet.
 	//   - or a workbook name which is relative to this workbook with a worksheet name: <Workbook>#<Worksheet>.
 	Scatter []string `protobuf:"bytes,18,rep,name=scatter,proto3" json:"scatter,omitempty"`
+	// Whether all fields in this sheet are optional (field name existence).
+	// If set to true, then:
+	//   - table formats (Excel/CSV): field's column can be absent.
+	//   - document formats (XML/YAML): field's name can be absent.
+	Optional bool `protobuf:"varint,19,opt,name=optional,proto3" json:"optional,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"`
@@ -300,6 +305,13 @@ func (x *Metasheet) GetScatter() []string {
 	return nil
 }
 
+func (x *Metasheet) GetOptional() bool {
+	if x != nil {
+		return x.Optional
+	}
+	return false
+}
+
 func (x *Metasheet) GetOrderedMap() bool {
 	if x != nil {
 		return x.OrderedMap
@@ -669,108 +681,114 @@ 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, 0x81, 0x07, 0x0a, 0x09, 0x4d, 0x65, 0x74, 0x61,
+	0x45, 0x41, 0x55, 0x10, 0x01, 0x28, 0x02, 0x22, 0xd7, 0x07, 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,
-	0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0d, 0x82, 0xb5, 0x18, 0x09, 0x0a, 0x05, 0x41,
-	0x6c, 0x69, 0x61, 0x73, 0x40, 0x01, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x29, 0x0a,
-	0x07, 0x6e, 0x61, 0x6d, 0x65, 0x72, 0x6f, 0x77, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x42, 0x0f,
-	0x82, 0xb5, 0x18, 0x0b, 0x0a, 0x07, 0x4e, 0x61, 0x6d, 0x65, 0x72, 0x6f, 0x77, 0x40, 0x01, 0x52,
-	0x07, 0x6e, 0x61, 0x6d, 0x65, 0x72, 0x6f, 0x77, 0x12, 0x29, 0x0a, 0x07, 0x74, 0x79, 0x70, 0x65,
-	0x72, 0x6f, 0x77, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x42, 0x0f, 0x82, 0xb5, 0x18, 0x0b, 0x0a,
-	0x07, 0x54, 0x79, 0x70, 0x65, 0x72, 0x6f, 0x77, 0x40, 0x01, 0x52, 0x07, 0x74, 0x79, 0x70, 0x65,
-	0x72, 0x6f, 0x77, 0x12, 0x29, 0x0a, 0x07, 0x6e, 0x6f, 0x74, 0x65, 0x72, 0x6f, 0x77, 0x18, 0x05,
-	0x20, 0x01, 0x28, 0x05, 0x42, 0x0f, 0x82, 0xb5, 0x18, 0x0b, 0x0a, 0x07, 0x4e, 0x6f, 0x74, 0x65,
-	0x72, 0x6f, 0x77, 0x40, 0x01, 0x52, 0x07, 0x6e, 0x6f, 0x74, 0x65, 0x72, 0x6f, 0x77, 0x12, 0x29,
-	0x0a, 0x07, 0x64, 0x61, 0x74, 0x61, 0x72, 0x6f, 0x77, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x42,
-	0x0f, 0x82, 0xb5, 0x18, 0x0b, 0x0a, 0x07, 0x44, 0x61, 0x74, 0x61, 0x72, 0x6f, 0x77, 0x40, 0x01,
-	0x52, 0x07, 0x64, 0x61, 0x74, 0x61, 0x72, 0x6f, 0x77, 0x12, 0x2c, 0x0a, 0x08, 0x6e, 0x61, 0x6d,
-	0x65, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x42, 0x10, 0x82, 0xb5, 0x18,
-	0x0c, 0x0a, 0x08, 0x4e, 0x61, 0x6d, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x40, 0x01, 0x52, 0x08, 0x6e,
-	0x61, 0x6d, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x2c, 0x0a, 0x08, 0x74, 0x79, 0x70, 0x65, 0x6c,
-	0x69, 0x6e, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x42, 0x10, 0x82, 0xb5, 0x18, 0x0c, 0x0a,
-	0x08, 0x54, 0x79, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x40, 0x01, 0x52, 0x08, 0x74, 0x79, 0x70,
-	0x65, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f,
-	0x73, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x42, 0x11, 0x82, 0xb5, 0x18, 0x0d, 0x0a, 0x09,
-	0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65, 0x40, 0x01, 0x52, 0x09, 0x74, 0x72, 0x61,
-	0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64,
-	0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x42, 0x0e, 0x82, 0xb5, 0x18, 0x0a, 0x0a, 0x06, 0x4e, 0x65,
-	0x73, 0x74, 0x65, 0x64, 0x40, 0x01, 0x52, 0x06, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x12, 0x1d,
-	0x0a, 0x03, 0x73, 0x65, 0x70, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0b, 0x82, 0xb5, 0x18,
-	0x07, 0x0a, 0x03, 0x53, 0x65, 0x70, 0x40, 0x01, 0x52, 0x03, 0x73, 0x65, 0x70, 0x12, 0x26, 0x0a,
-	0x06, 0x73, 0x75, 0x62, 0x73, 0x65, 0x70, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0e, 0x82,
-	0xb5, 0x18, 0x0a, 0x0a, 0x06, 0x53, 0x75, 0x62, 0x73, 0x65, 0x70, 0x40, 0x01, 0x52, 0x06, 0x73,
-	0x75, 0x62, 0x73, 0x65, 0x70, 0x12, 0x28, 0x0a, 0x06, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x72, 0x18,
-	0x0d, 0x20, 0x03, 0x28, 0x09, 0x42, 0x10, 0x82, 0xb5, 0x18, 0x0c, 0x0a, 0x06, 0x4d, 0x65, 0x72,
-	0x67, 0x65, 0x72, 0x20, 0x03, 0x40, 0x01, 0x52, 0x06, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x72, 0x12,
-	0x36, 0x0a, 0x0c, 0x61, 0x64, 0x6a, 0x61, 0x63, 0x65, 0x6e, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18,
-	0x0e, 0x20, 0x01, 0x28, 0x08, 0x42, 0x13, 0x82, 0xb5, 0x18, 0x0f, 0x0a, 0x0b, 0x41, 0x64, 0x6a,
-	0x61, 0x63, 0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79, 0x40, 0x01, 0x52, 0x0b, 0x61, 0x64, 0x6a, 0x61,
-	0x63, 0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x3c, 0x0a, 0x0e, 0x66, 0x69, 0x65, 0x6c, 0x64,
-	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, 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, 0x2f, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28,
-	0x0e, 0x32, 0x0d, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x4d, 0x6f, 0x64, 0x65,
-	0x42, 0x0c, 0x82, 0xb5, 0x18, 0x08, 0x0a, 0x04, 0x4d, 0x6f, 0x64, 0x65, 0x40, 0x01, 0x52, 0x04,
-	0x6d, 0x6f, 0x64, 0x65, 0x12, 0x2b, 0x0a, 0x07, 0x73, 0x63, 0x61, 0x74, 0x74, 0x65, 0x72, 0x18,
-	0x12, 0x20, 0x03, 0x28, 0x09, 0x42, 0x11, 0x82, 0xb5, 0x18, 0x0d, 0x0a, 0x07, 0x53, 0x63, 0x61,
-	0x74, 0x74, 0x65, 0x72, 0x20, 0x03, 0x40, 0x01, 0x52, 0x07, 0x73, 0x63, 0x61, 0x74, 0x74, 0x65,
-	0x72, 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, 0x23, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18,
-	0x33, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0d, 0x82, 0xb5, 0x18, 0x09, 0x0a, 0x05, 0x49, 0x6e, 0x64,
-	0x65, 0x78, 0x40, 0x01, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0xde, 0x01, 0x0a, 0x0e,
-	0x45, 0x6e, 0x75, 0x6d, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x3d,
-	0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d,
-	0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x44, 0x65, 0x73,
-	0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x06, 0x82,
-	0xb5, 0x18, 0x02, 0x20, 0x01, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x1a, 0x82, 0x01,
-	0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2b, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65,
-	0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x42, 0x0e, 0x82, 0xb5, 0x18, 0x0a, 0x0a, 0x06, 0x4e,
-	0x75, 0x6d, 0x62, 0x65, 0x72, 0x40, 0x01, 0x48, 0x00, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65,
-	0x72, 0x88, 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01,
-	0x28, 0x09, 0x42, 0x0a, 0x82, 0xb5, 0x18, 0x06, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x04,
-	0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x03, 0x20,
-	0x01, 0x28, 0x09, 0x42, 0x0b, 0x82, 0xb5, 0x18, 0x07, 0x0a, 0x05, 0x41, 0x6c, 0x69, 0x61, 0x73,
-	0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6e, 0x75, 0x6d, 0x62,
-	0x65, 0x72, 0x3a, 0x08, 0x82, 0xb5, 0x18, 0x04, 0x10, 0x01, 0x28, 0x02, 0x22, 0xa6, 0x01, 0x0a,
-	0x10, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f,
-	0x72, 0x12, 0x3f, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
-	0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x53, 0x74, 0x72, 0x75,
-	0x63, 0x74, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x46, 0x69, 0x65,
-	0x6c, 0x64, 0x42, 0x06, 0x82, 0xb5, 0x18, 0x02, 0x20, 0x01, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c,
-	0x64, 0x73, 0x1a, 0x47, 0x0a, 0x05, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x1e, 0x0a, 0x04, 0x6e,
-	0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0x82, 0xb5, 0x18, 0x06, 0x0a,
-	0x04, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x04, 0x74,
-	0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0x82, 0xb5, 0x18, 0x06, 0x0a,
-	0x04, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x3a, 0x08, 0x82, 0xb5, 0x18,
-	0x04, 0x10, 0x01, 0x28, 0x02, 0x22, 0x87, 0x02, 0x0a, 0x0f, 0x55, 0x6e, 0x69, 0x6f, 0x6e, 0x44,
-	0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x3e, 0x0a, 0x06, 0x76, 0x61, 0x6c,
-	0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x74, 0x61, 0x62, 0x6c,
-	0x65, 0x61, 0x75, 0x2e, 0x55, 0x6e, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
-	0x74, 0x6f, 0x72, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x06, 0x82, 0xb5, 0x18, 0x02, 0x20,
-	0x01, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x1a, 0xa9, 0x01, 0x0a, 0x05, 0x56, 0x61,
-	0x6c, 0x75, 0x65, 0x12, 0x2b, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x05, 0x42, 0x0e, 0x82, 0xb5, 0x18, 0x0a, 0x0a, 0x06, 0x4e, 0x75, 0x6d, 0x62, 0x65,
-	0x72, 0x40, 0x01, 0x48, 0x00, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x88, 0x01, 0x01,
-	0x12, 0x1e, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a,
-	0x82, 0xb5, 0x18, 0x06, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
-	0x12, 0x21, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42,
-	0x0b, 0x82, 0xb5, 0x18, 0x07, 0x0a, 0x05, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x05, 0x61, 0x6c,
-	0x69, 0x61, 0x73, 0x12, 0x25, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x04, 0x20,
-	0x03, 0x28, 0x09, 0x42, 0x0d, 0x82, 0xb5, 0x18, 0x09, 0x0a, 0x05, 0x46, 0x69, 0x65, 0x6c, 0x64,
-	0x20, 0x02, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6e,
-	0x75, 0x6d, 0x62, 0x65, 0x72, 0x3a, 0x08, 0x82, 0xb5, 0x18, 0x04, 0x10, 0x01, 0x28, 0x02, 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,
+	0x74, 0x52, 0x05, 0x73, 0x68, 0x65, 0x65, 0x74, 0x12, 0x25, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61,
+	0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0f, 0x82, 0xb5, 0x18, 0x0b, 0x0a, 0x05, 0x41,
+	0x6c, 0x69, 0x61, 0x73, 0x7a, 0x02, 0x58, 0x01, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12,
+	0x2b, 0x0a, 0x07, 0x6e, 0x61, 0x6d, 0x65, 0x72, 0x6f, 0x77, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05,
+	0x42, 0x11, 0x82, 0xb5, 0x18, 0x0d, 0x0a, 0x07, 0x4e, 0x61, 0x6d, 0x65, 0x72, 0x6f, 0x77, 0x7a,
+	0x02, 0x58, 0x01, 0x52, 0x07, 0x6e, 0x61, 0x6d, 0x65, 0x72, 0x6f, 0x77, 0x12, 0x2b, 0x0a, 0x07,
+	0x74, 0x79, 0x70, 0x65, 0x72, 0x6f, 0x77, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x42, 0x11, 0x82,
+	0xb5, 0x18, 0x0d, 0x0a, 0x07, 0x54, 0x79, 0x70, 0x65, 0x72, 0x6f, 0x77, 0x7a, 0x02, 0x58, 0x01,
+	0x52, 0x07, 0x74, 0x79, 0x70, 0x65, 0x72, 0x6f, 0x77, 0x12, 0x2b, 0x0a, 0x07, 0x6e, 0x6f, 0x74,
+	0x65, 0x72, 0x6f, 0x77, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x42, 0x11, 0x82, 0xb5, 0x18, 0x0d,
+	0x0a, 0x07, 0x4e, 0x6f, 0x74, 0x65, 0x72, 0x6f, 0x77, 0x7a, 0x02, 0x58, 0x01, 0x52, 0x07, 0x6e,
+	0x6f, 0x74, 0x65, 0x72, 0x6f, 0x77, 0x12, 0x2b, 0x0a, 0x07, 0x64, 0x61, 0x74, 0x61, 0x72, 0x6f,
+	0x77, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x42, 0x11, 0x82, 0xb5, 0x18, 0x0d, 0x0a, 0x07, 0x44,
+	0x61, 0x74, 0x61, 0x72, 0x6f, 0x77, 0x7a, 0x02, 0x58, 0x01, 0x52, 0x07, 0x64, 0x61, 0x74, 0x61,
+	0x72, 0x6f, 0x77, 0x12, 0x2e, 0x0a, 0x08, 0x6e, 0x61, 0x6d, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x18,
+	0x07, 0x20, 0x01, 0x28, 0x05, 0x42, 0x12, 0x82, 0xb5, 0x18, 0x0e, 0x0a, 0x08, 0x4e, 0x61, 0x6d,
+	0x65, 0x6c, 0x69, 0x6e, 0x65, 0x7a, 0x02, 0x58, 0x01, 0x52, 0x08, 0x6e, 0x61, 0x6d, 0x65, 0x6c,
+	0x69, 0x6e, 0x65, 0x12, 0x2e, 0x0a, 0x08, 0x74, 0x79, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x18,
+	0x08, 0x20, 0x01, 0x28, 0x05, 0x42, 0x12, 0x82, 0xb5, 0x18, 0x0e, 0x0a, 0x08, 0x54, 0x79, 0x70,
+	0x65, 0x6c, 0x69, 0x6e, 0x65, 0x7a, 0x02, 0x58, 0x01, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x6c,
+	0x69, 0x6e, 0x65, 0x12, 0x31, 0x0a, 0x09, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65,
+	0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x42, 0x13, 0x82, 0xb5, 0x18, 0x0f, 0x0a, 0x09, 0x54, 0x72,
+	0x61, 0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65, 0x7a, 0x02, 0x58, 0x01, 0x52, 0x09, 0x74, 0x72, 0x61,
+	0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x06, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64,
+	0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x42, 0x10, 0x82, 0xb5, 0x18, 0x0c, 0x0a, 0x06, 0x4e, 0x65,
+	0x73, 0x74, 0x65, 0x64, 0x7a, 0x02, 0x58, 0x01, 0x52, 0x06, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64,
+	0x12, 0x1f, 0x0a, 0x03, 0x73, 0x65, 0x70, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0d, 0x82,
+	0xb5, 0x18, 0x09, 0x0a, 0x03, 0x53, 0x65, 0x70, 0x7a, 0x02, 0x58, 0x01, 0x52, 0x03, 0x73, 0x65,
+	0x70, 0x12, 0x28, 0x0a, 0x06, 0x73, 0x75, 0x62, 0x73, 0x65, 0x70, 0x18, 0x0c, 0x20, 0x01, 0x28,
+	0x09, 0x42, 0x10, 0x82, 0xb5, 0x18, 0x0c, 0x0a, 0x06, 0x53, 0x75, 0x62, 0x73, 0x65, 0x70, 0x7a,
+	0x02, 0x58, 0x01, 0x52, 0x06, 0x73, 0x75, 0x62, 0x73, 0x65, 0x70, 0x12, 0x2a, 0x0a, 0x06, 0x6d,
+	0x65, 0x72, 0x67, 0x65, 0x72, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x09, 0x42, 0x12, 0x82, 0xb5, 0x18,
+	0x0e, 0x0a, 0x06, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x72, 0x20, 0x03, 0x7a, 0x02, 0x58, 0x01, 0x52,
+	0x06, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x0c, 0x61, 0x64, 0x6a, 0x61, 0x63,
+	0x65, 0x6e, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x42, 0x15, 0x82,
+	0xb5, 0x18, 0x11, 0x0a, 0x0b, 0x41, 0x64, 0x6a, 0x61, 0x63, 0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79,
+	0x7a, 0x02, 0x58, 0x01, 0x52, 0x0b, 0x61, 0x64, 0x6a, 0x61, 0x63, 0x65, 0x6e, 0x74, 0x4b, 0x65,
+	0x79, 0x12, 0x3e, 0x0a, 0x0e, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x70, 0x72, 0x65, 0x73, 0x65,
+	0x6e, 0x63, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x42, 0x17, 0x82, 0xb5, 0x18, 0x13, 0x0a,
+	0x0d, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x7a, 0x02,
+	0x58, 0x01, 0x52, 0x0d, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63,
+	0x65, 0x12, 0x2e, 0x0a, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x18, 0x10, 0x20,
+	0x01, 0x28, 0x08, 0x42, 0x12, 0x82, 0xb5, 0x18, 0x0e, 0x0a, 0x08, 0x54, 0x65, 0x6d, 0x70, 0x6c,
+	0x61, 0x74, 0x65, 0x7a, 0x02, 0x58, 0x01, 0x52, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74,
+	0x65, 0x12, 0x31, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0e, 0x32,
+	0x0d, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x4d, 0x6f, 0x64, 0x65, 0x42, 0x0e,
+	0x82, 0xb5, 0x18, 0x0a, 0x0a, 0x04, 0x4d, 0x6f, 0x64, 0x65, 0x7a, 0x02, 0x58, 0x01, 0x52, 0x04,
+	0x6d, 0x6f, 0x64, 0x65, 0x12, 0x2d, 0x0a, 0x07, 0x73, 0x63, 0x61, 0x74, 0x74, 0x65, 0x72, 0x18,
+	0x12, 0x20, 0x03, 0x28, 0x09, 0x42, 0x13, 0x82, 0xb5, 0x18, 0x0f, 0x0a, 0x07, 0x53, 0x63, 0x61,
+	0x74, 0x74, 0x65, 0x72, 0x20, 0x03, 0x7a, 0x02, 0x58, 0x01, 0x52, 0x07, 0x73, 0x63, 0x61, 0x74,
+	0x74, 0x65, 0x72, 0x12, 0x2e, 0x0a, 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18,
+	0x13, 0x20, 0x01, 0x28, 0x08, 0x42, 0x12, 0x82, 0xb5, 0x18, 0x0e, 0x0a, 0x08, 0x4f, 0x70, 0x74,
+	0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x7a, 0x02, 0x58, 0x01, 0x52, 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f,
+	0x6e, 0x61, 0x6c, 0x12, 0x35, 0x0a, 0x0b, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x5f, 0x6d,
+	0x61, 0x70, 0x18, 0x32, 0x20, 0x01, 0x28, 0x08, 0x42, 0x14, 0x82, 0xb5, 0x18, 0x10, 0x0a, 0x0a,
+	0x4f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x4d, 0x61, 0x70, 0x7a, 0x02, 0x58, 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, 0x01, 0x28, 0x09, 0x42, 0x0f, 0x82, 0xb5, 0x18, 0x0b, 0x0a,
+	0x05, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x7a, 0x02, 0x58, 0x01, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65,
+	0x78, 0x22, 0xe0, 0x01, 0x0a, 0x0e, 0x45, 0x6e, 0x75, 0x6d, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69,
+	0x70, 0x74, 0x6f, 0x72, 0x12, 0x3d, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01,
+	0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x45,
+	0x6e, 0x75, 0x6d, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x56, 0x61,
+	0x6c, 0x75, 0x65, 0x42, 0x06, 0x82, 0xb5, 0x18, 0x02, 0x20, 0x01, 0x52, 0x06, 0x76, 0x61, 0x6c,
+	0x75, 0x65, 0x73, 0x1a, 0x84, 0x01, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2d, 0x0a,
+	0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x42, 0x10, 0x82,
+	0xb5, 0x18, 0x0c, 0x0a, 0x06, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x7a, 0x02, 0x58, 0x01, 0x48,
+	0x00, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x04,
+	0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0x82, 0xb5, 0x18, 0x06,
+	0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x05,
+	0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0b, 0x82, 0xb5, 0x18,
+	0x07, 0x0a, 0x05, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x42,
+	0x09, 0x0a, 0x07, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x3a, 0x08, 0x82, 0xb5, 0x18, 0x04,
+	0x10, 0x01, 0x28, 0x02, 0x22, 0xa6, 0x01, 0x0a, 0x10, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x44,
+	0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x3f, 0x0a, 0x06, 0x66, 0x69, 0x65,
+	0x6c, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x61, 0x62, 0x6c,
+	0x65, 0x61, 0x75, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69,
+	0x70, 0x74, 0x6f, 0x72, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x42, 0x06, 0x82, 0xb5, 0x18, 0x02,
+	0x20, 0x01, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x1a, 0x47, 0x0a, 0x05, 0x46, 0x69,
+	0x65, 0x6c, 0x64, 0x12, 0x1e, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x09, 0x42, 0x0a, 0x82, 0xb5, 0x18, 0x06, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e,
+	0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x09, 0x42, 0x0a, 0x82, 0xb5, 0x18, 0x06, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74,
+	0x79, 0x70, 0x65, 0x3a, 0x08, 0x82, 0xb5, 0x18, 0x04, 0x10, 0x01, 0x28, 0x02, 0x22, 0x89, 0x02,
+	0x0a, 0x0f, 0x55, 0x6e, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f,
+	0x72, 0x12, 0x3e, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
+	0x0b, 0x32, 0x1e, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x55, 0x6e, 0x69, 0x6f,
+	0x6e, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x56, 0x61, 0x6c, 0x75,
+	0x65, 0x42, 0x06, 0x82, 0xb5, 0x18, 0x02, 0x20, 0x01, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65,
+	0x73, 0x1a, 0xab, 0x01, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2d, 0x0a, 0x06, 0x6e,
+	0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x42, 0x10, 0x82, 0xb5, 0x18,
+	0x0c, 0x0a, 0x06, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x7a, 0x02, 0x58, 0x01, 0x48, 0x00, 0x52,
+	0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x04, 0x6e, 0x61,
+	0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0x82, 0xb5, 0x18, 0x06, 0x0a, 0x04,
+	0x4e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x05, 0x61, 0x6c,
+	0x69, 0x61, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0b, 0x82, 0xb5, 0x18, 0x07, 0x0a,
+	0x05, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x25, 0x0a,
+	0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x42, 0x0d, 0x82,
+	0xb5, 0x18, 0x09, 0x0a, 0x05, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x02, 0x52, 0x06, 0x66, 0x69,
+	0x65, 0x6c, 0x64, 0x73, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x3a,
+	0x08, 0x82, 0xb5, 0x18, 0x04, 0x10, 0x01, 0x28, 0x02, 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 21ba1cf..08d26d5 100644
--- a/proto/tableaupb/tableau.pb.go
+++ b/proto/tableaupb/tableau.pb.go
@@ -361,6 +361,11 @@ type WorksheetOptions struct {
 	// Scatter convert multiple workbook sheets (comma-separated) separately
 	// with same schema. E.g.: Item1.xlsx,Item2.xlsx,ItemAward*.xlsx.
 	Scatter []string `protobuf:"bytes,18,rep,name=scatter,proto3" json:"scatter,omitempty"`
+	// Whether all fields in this sheet are optional (field name existence).
+	// If set to true, then:
+	//   - table formats (Excel/CSV): field's column can be absent.
+	//   - document formats (XML/YAML): field's name can be absent.
+	Optional bool `protobuf:"varint,19,opt,name=optional,proto3" json:"optional,omitempty"`
 	// //////// Loader related options below //////////
 	// Generate OrderedMap accessers or not.
 	OrderedMap bool `protobuf:"varint,50,opt,name=ordered_map,json=orderedMap,proto3" json:"ordered_map,omitempty"`
@@ -537,6 +542,13 @@ func (x *WorksheetOptions) GetScatter() []string {
 	return nil
 }
 
+func (x *WorksheetOptions) GetOptional() bool {
+	if x != nil {
+		return x.Optional
+	}
+	return false
+}
+
 func (x *WorksheetOptions) GetOrderedMap() bool {
 	if x != nil {
 		return x.OrderedMap
@@ -705,15 +717,14 @@ type FieldOptions struct {
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	Name     string     `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`                          // Scalar type's variable name or composite type's variable name (prefix).
-	Note     string     `protobuf:"bytes,2,opt,name=note,proto3" json:"note,omitempty"`                          // Field note, maybe in another language (Chinese) other than variable name (English).
-	Key      string     `protobuf:"bytes,3,opt,name=key,proto3" json:"key,omitempty"`                            // Only set when this field type is map or keyed-list.
-	Layout   Layout     `protobuf:"varint,4,opt,name=layout,proto3,enum=tableau.Layout" json:"layout,omitempty"` // For map/list types with cardinality. Default: LAYOUT_DEFAULT.
-	Span     Span       `protobuf:"varint,5,opt,name=span,proto3,enum=tableau.Span" json:"span,omitempty"`       // For list element or map value types. Default: SPAN_CROSS_CELL.
-	Sep      string     `protobuf:"bytes,6,opt,name=sep,proto3" json:"sep,omitempty"`                            // NOT USED yet. Default: ",".
-	Subsep   string     `protobuf:"bytes,7,opt,name=subsep,proto3" json:"subsep,omitempty"`                      // NOT USED yet. Default: ":".
-	Optional bool       `protobuf:"varint,8,opt,name=optional,proto3" json:"optional,omitempty"`                 // Whether the field is optional.
-	Prop     *FieldProp `protobuf:"bytes,15,opt,name=prop,proto3" json:"prop,omitempty"`                         // Property of field.
+	Name   string     `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`                          // Scalar type's variable name or composite type's variable name (prefix).
+	Note   string     `protobuf:"bytes,2,opt,name=note,proto3" json:"note,omitempty"`                          // Field note, maybe in another language (Chinese) other than variable name (English).
+	Key    string     `protobuf:"bytes,3,opt,name=key,proto3" json:"key,omitempty"`                            // Only set when this field type is map or keyed-list.
+	Layout Layout     `protobuf:"varint,4,opt,name=layout,proto3,enum=tableau.Layout" json:"layout,omitempty"` // For map/list types with cardinality. Default: LAYOUT_DEFAULT.
+	Span   Span       `protobuf:"varint,5,opt,name=span,proto3,enum=tableau.Span" json:"span,omitempty"`       // For list element or map value types. Default: SPAN_CROSS_CELL.
+	Sep    string     `protobuf:"bytes,6,opt,name=sep,proto3" json:"sep,omitempty"`                            // NOT USED yet. Default: ",".
+	Subsep string     `protobuf:"bytes,7,opt,name=subsep,proto3" json:"subsep,omitempty"`                      // NOT USED yet. Default: ":".
+	Prop   *FieldProp `protobuf:"bytes,15,opt,name=prop,proto3" json:"prop,omitempty"`                         // Property of field.
 }
 
 func (x *FieldOptions) Reset() {
@@ -797,13 +808,6 @@ func (x *FieldOptions) GetSubsep() string {
 	return ""
 }
 
-func (x *FieldOptions) GetOptional() bool {
-	if x != nil {
-		return x.Optional
-	}
-	return false
-}
-
 func (x *FieldOptions) GetProp() *FieldProp {
 	if x != nil {
 		return x.Prop
@@ -854,9 +858,18 @@ type FieldProp struct {
 	// will be used. Otherwise, it's deduced from the field's name by converting
 	// it to camelCase.
 	JsonName string `protobuf:"bytes,9,opt,name=json_name,json=jsonName,proto3" json:"json_name,omitempty"`
-	// Must fill cell data explicitly if present is true, otherwise
-	// an error will be reported.
+	// Whether this field value is present (field value existence).
+	// If set to true, then do the following checks for different field types:
+	//   - scalar/enum: cell data cannot be empty string (TODO: If this field's
+	//     type is string, then how to set empty string explicitly?)
+	//   - struct: check at least one field is present recursively
+	//   - map/list: len(elements) > 0
 	Present bool `protobuf:"varint,10,opt,name=present,proto3" json:"present,omitempty"`
+	// Whether this field is optional (field name existence).
+	// If set to true, then:
+	//   - table formats (Excel/CSV): field's column can be absent.
+	//   - document formats (XML/YAML): field's name can be absent.
+	Optional bool `protobuf:"varint,11,opt,name=optional,proto3" json:"optional,omitempty"`
 }
 
 func (x *FieldProp) Reset() {
@@ -961,6 +974,13 @@ func (x *FieldProp) GetPresent() bool {
 	return false
 }
 
+func (x *FieldProp) GetOptional() bool {
+	if x != nil {
+		return x.Optional
+	}
+	return false
+}
+
 var file_tableau_protobuf_tableau_proto_extTypes = []protoimpl.ExtensionInfo{
 	{
 		ExtendedType:  (*descriptorpb.FileOptions)(nil),
@@ -1068,7 +1088,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, 0xac, 0x04, 0x0a, 0x10, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x68, 0x65, 0x65, 0x74,
+	0x6d, 0x65, 0x22, 0xc8, 0x04, 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,
@@ -1099,35 +1119,35 @@ var file_tableau_protobuf_tableau_proto_rawDesc = []byte{
 	0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x74, 0x61,
 	0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65,
 	0x12, 0x18, 0x0a, 0x07, 0x73, 0x63, 0x61, 0x74, 0x74, 0x65, 0x72, 0x18, 0x12, 0x20, 0x03, 0x28,
-	0x09, 0x52, 0x07, 0x73, 0x63, 0x61, 0x74, 0x74, 0x65, 0x72, 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, 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, 0x22, 0x38, 0x0a, 0x0c,
-	0x4f, 0x6e, 0x65, 0x6f, 0x66, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04,
+	0x09, 0x52, 0x07, 0x73, 0x63, 0x61, 0x74, 0x74, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x70,
+	0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x13, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6f, 0x70,
+	0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 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, 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,
-	0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 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,
+	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, 0x22, 0x38, 0x0a, 0x0c, 0x4f, 0x6e, 0x65, 0x6f,
+	0x66, 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, 0x14, 0x0a, 0x05,
+	0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x65,
+	0x6c, 0x64, 0x22, 0xe6, 0x01, 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, 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, 0xab, 0x02, 0x0a, 0x09,
+	0x64, 0x50, 0x72, 0x6f, 0x70, 0x52, 0x04, 0x70, 0x72, 0x6f, 0x70, 0x22, 0xc7, 0x02, 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,
 	0x1b, 0x0a, 0x06, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x48,
@@ -1145,72 +1165,74 @@ var file_tableau_protobuf_tableau_proto_rawDesc = []byte{
 	0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6a,
 	0x73, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x65, 0x73, 0x65,
 	0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e,
-	0x74, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x42, 0x0b, 0x0a, 0x09,
-	0x5f, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 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, 0x2a, 0x7a, 0x0a, 0x04, 0x4d, 0x6f,
-	0x64, 0x65, 0x12, 0x10, 0x0a, 0x0c, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55,
-	0x4c, 0x54, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x45, 0x5f,
-	0x43, 0x53, 0x56, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x45,
-	0x5f, 0x4a, 0x53, 0x4f, 0x4e, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x4d, 0x4f, 0x44, 0x45, 0x5f,
-	0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x10, 0x03, 0x12, 0x14, 0x0a, 0x10, 0x4d,
-	0x4f, 0x44, 0x45, 0x5f, 0x53, 0x54, 0x52, 0x55, 0x43, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x10,
-	0x04, 0x12, 0x13, 0x0a, 0x0f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x49, 0x4f, 0x4e, 0x5f,
-	0x54, 0x59, 0x50, 0x45, 0x10, 0x05, 0x2a, 0x36, 0x0a, 0x04, 0x46, 0x6f, 0x72, 0x6d, 0x12, 0x10,
-	0x0a, 0x0c, 0x46, 0x4f, 0x52, 0x4d, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00,
-	0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x4d, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x10, 0x01, 0x12,
-	0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x4d, 0x5f, 0x4a, 0x53, 0x4f, 0x4e, 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, 0x37, 0x0a, 0x05, 0x75, 0x6e, 0x69, 0x6f, 0x6e, 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, 0xd1, 0x86, 0x03, 0x20, 0x01,
-	0x28, 0x08, 0x52, 0x05, 0x75, 0x6e, 0x69, 0x6f, 0x6e, 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, 0x4a, 0x0a, 0x05, 0x65, 0x74, 0x79, 0x70, 0x65,
-	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, 0x05, 0x65, 0x74,
-	0x79, 0x70, 0x65, 0x3a, 0x56, 0x0a, 0x06, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x21, 0x2e,
+	0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x0b, 0x20,
+	0x01, 0x28, 0x08, 0x52, 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x42, 0x09, 0x0a,
+	0x07, 0x5f, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x73, 0x65, 0x71,
+	0x75, 0x65, 0x6e, 0x63, 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, 0x2a, 0x7a, 0x0a, 0x04, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x10,
+	0x0a, 0x0c, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00,
+	0x12, 0x0f, 0x0a, 0x0b, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x45, 0x5f, 0x43, 0x53, 0x56, 0x10,
+	0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x45, 0x5f, 0x4a, 0x53, 0x4f,
+	0x4e, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x45, 0x4e, 0x55, 0x4d,
+	0x5f, 0x54, 0x59, 0x50, 0x45, 0x10, 0x03, 0x12, 0x14, 0x0a, 0x10, 0x4d, 0x4f, 0x44, 0x45, 0x5f,
+	0x53, 0x54, 0x52, 0x55, 0x43, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x10, 0x04, 0x12, 0x13, 0x0a,
+	0x0f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45,
+	0x10, 0x05, 0x2a, 0x36, 0x0a, 0x04, 0x46, 0x6f, 0x72, 0x6d, 0x12, 0x10, 0x0a, 0x0c, 0x46, 0x4f,
+	0x52, 0x4d, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09,
+	0x46, 0x4f, 0x52, 0x4d, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x46,
+	0x4f, 0x52, 0x4d, 0x5f, 0x4a, 0x53, 0x4f, 0x4e, 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,
-	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, 0x3a, 0x4f, 0x0a, 0x05, 0x6f,
-	0x6e, 0x65, 0x6f, 0x66, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
-	0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4f, 0x6e, 0x65, 0x6f, 0x66, 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, 0x4f, 0x6e, 0x65, 0x6f, 0x66, 0x4f, 0x70, 0x74, 0x69, 0x6f,
-	0x6e, 0x73, 0x52, 0x05, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x88, 0x01, 0x01, 0x42, 0x75, 0x0a, 0x14,
-	0x63, 0x6f, 0x6d, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x62, 0x75, 0x66, 0x42, 0x0c, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x50, 0x72, 0x6f,
-	0x74, 0x6f, 0x50, 0x01, 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, 0xa2, 0x02, 0x03, 0x54, 0x50, 0x42, 0xaa, 0x02, 0x18, 0x54, 0x61, 0x62, 0x6c, 0x65,
-	0x61, 0x75, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x61, 0x62, 0x6c,
-	0x65, 0x61, 0x75, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	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, 0x37, 0x0a, 0x05,
+	0x75, 0x6e, 0x69, 0x6f, 0x6e, 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, 0xd1, 0x86, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05,
+	0x75, 0x6e, 0x69, 0x6f, 0x6e, 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, 0x4a, 0x0a, 0x05, 0x65, 0x74, 0x79, 0x70, 0x65, 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, 0x05, 0x65, 0x74, 0x79, 0x70, 0x65, 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, 0x3a, 0x4f, 0x0a, 0x05, 0x6f, 0x6e, 0x65, 0x6f, 0x66,
+	0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
+	0x75, 0x66, 0x2e, 0x4f, 0x6e, 0x65, 0x6f, 0x66, 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, 0x4f, 0x6e, 0x65, 0x6f, 0x66, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x05,
+	0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x88, 0x01, 0x01, 0x42, 0x75, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x2e,
+	0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
+	0x42, 0x0c, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01,
+	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, 0xa2, 0x02,
+	0x03, 0x54, 0x50, 0x42, 0xaa, 0x02, 0x18, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x50,
+	0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x62,
+	0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (


Test patch:
diff --git a/internal/confgen/document_parser_test.go b/internal/confgen/document_parser_test.go
new file mode 100644
index 0000000..31f1ec5
--- /dev/null
+++ b/internal/confgen/document_parser_test.go
@@ -0,0 +1,139 @@
+package confgen
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/require"
+	"github.com/tableauio/tableau/format"
+	"github.com/tableauio/tableau/internal/importer/book"
+	"github.com/tableauio/tableau/proto/tableaupb/unittestpb"
+	"github.com/tableauio/tableau/xerrors"
+)
+
+var docTestParser *sheetParser
+
+func init() {
+	docTestParser = NewExtendedSheetParser("protoconf", "Asia/Shanghai", book.MetasheetOptions(),
+		&SheetParserExtInfo{
+			InputDir:       "",
+			SubdirRewrites: map[string]string{},
+			BookFormat:     format.YAML,
+		})
+}
+
+func TestDocParser_parseFieldNotFound(t *testing.T) {
+	type args struct {
+		sheet *book.Sheet
+	}
+	tests := []struct {
+		name    string
+		parser  *sheetParser
+		args    args
+		wantErr bool
+		errcode string
+	}{
+		{
+			name:   "no duplicate key",
+			parser: docTestParser,
+			args: args{
+				sheet: &book.Sheet{
+					Name: "YamlScalarConf",
+					Document: &book.Node{
+						Kind: book.DocumentNode,
+						Name: "YamlScalarConf",
+						Children: []*book.Node{
+							{
+								Kind: book.MapNode,
+								Name: "",
+								Children: []*book.Node{
+									{
+										Name:  "ID",
+										Value: "1",
+									},
+									{
+										Name:  "Num",
+										Value: "10",
+									},
+									{
+										Name:  "Value",
+										Value: "20",
+									},
+									{
+										Name:  "Weight",
+										Value: "30",
+									},
+									{
+										Name:  "Percentage",
+										Value: "0.5",
+									},
+									{
+										Name:  "Ratio",
+										Value: "1.5",
+									},
+									{
+										Name:  "Name",
+										Value: "Apple",
+									},
+									{
+										Name:  "Blob",
+										Value: "VGFibGVhdQ==",
+									},
+									{
+										Name:  "OK",
+										Value: "true",
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			wantErr: false,
+		},
+		{
+			name:   "field-not-found",
+			parser: docTestParser,
+			args: args{
+				sheet: &book.Sheet{
+					Name: "YamlScalarConf",
+					Document: &book.Node{
+						Kind: book.DocumentNode,
+						Name: "YamlScalarConf",
+						Children: []*book.Node{
+							{
+								Kind: book.MapNode,
+								Name: "",
+								Children: []*book.Node{
+									{
+										Name:  "ID",
+										Value: "1",
+									},
+									{
+										Name:  "Num",
+										Value: "10",
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			wantErr: true,
+			errcode: "E2014",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			err := tt.parser.Parse(&unittestpb.YamlScalarConf{}, tt.args.sheet)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("sheetParser.Parse() error = %v, wantErr %v", err, tt.wantErr)
+			}
+			if err != nil {
+				if tt.errcode != "" {
+					desc := xerrors.NewDesc(err)
+					require.Equal(t, tt.errcode, desc.ErrCode())
+				}
+			}
+		})
+	}
+}
diff --git a/proto/tableau/protobuf/unittest/unittest.proto b/proto/tableau/protobuf/unittest/unittest.proto
index 19596e7..f11160a 100644
--- a/proto/tableau/protobuf/unittest/unittest.proto
+++ b/proto/tableau/protobuf/unittest/unittest.proto
@@ -66,3 +66,17 @@ message RewardConf {
     map<uint32, unittest.Item> item_map = 2 [(tableau.field) = {name:"Item" key:"ID" layout:LAYOUT_HORIZONTAL}];
   }
 }
+
+message YamlScalarConf {
+  option (tableau.worksheet) = {name:"YamlScalarConf"};
+
+  uint32 id = 1 [(tableau.field) = {name:"ID"}];
+  int32 num = 2 [(tableau.field) = {name:"Num"}];
+  uint64 value = 3 [(tableau.field) = {name:"Value"}];
+  int64 weight = 4 [(tableau.field) = {name:"Weight"}];
+  float percentage = 5 [(tableau.field) = {name:"Percentage"}];
+  double ratio = 6 [(tableau.field) = {name:"Ratio"}];
+  string name = 7 [(tableau.field) = {name:"Name"}];
+  bytes blob = 8 [(tableau.field) = {name:"Blob"}];
+  bool ok = 9 [(tableau.field) = {name:"OK"}];
+}
diff --git a/proto/tableaupb/unittestpb/unittest.pb.go b/proto/tableaupb/unittestpb/unittest.pb.go
index 0b24307..20ece22 100644
--- a/proto/tableaupb/unittestpb/unittest.pb.go
+++ b/proto/tableaupb/unittestpb/unittest.pb.go
@@ -291,6 +291,117 @@ func (x *RewardConf) GetRewardMap() map[uint32]*RewardConf_Reward {
 	return nil
 }
 
+type YamlScalarConf struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Id         uint32  `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
+	Num        int32   `protobuf:"varint,2,opt,name=num,proto3" json:"num,omitempty"`
+	Value      uint64  `protobuf:"varint,3,opt,name=value,proto3" json:"value,omitempty"`
+	Weight     int64   `protobuf:"varint,4,opt,name=weight,proto3" json:"weight,omitempty"`
+	Percentage float32 `protobuf:"fixed32,5,opt,name=percentage,proto3" json:"percentage,omitempty"`
+	Ratio      float64 `protobuf:"fixed64,6,opt,name=ratio,proto3" json:"ratio,omitempty"`
+	Name       string  `protobuf:"bytes,7,opt,name=name,proto3" json:"name,omitempty"`
+	Blob       []byte  `protobuf:"bytes,8,opt,name=blob,proto3" json:"blob,omitempty"`
+	Ok         bool    `protobuf:"varint,9,opt,name=ok,proto3" json:"ok,omitempty"`
+}
+
+func (x *YamlScalarConf) Reset() {
+	*x = YamlScalarConf{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[5]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *YamlScalarConf) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*YamlScalarConf) ProtoMessage() {}
+
+func (x *YamlScalarConf) ProtoReflect() protoreflect.Message {
+	mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[5]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use YamlScalarConf.ProtoReflect.Descriptor instead.
+func (*YamlScalarConf) Descriptor() ([]byte, []int) {
+	return file_tableau_protobuf_unittest_unittest_proto_rawDescGZIP(), []int{5}
+}
+
+func (x *YamlScalarConf) GetId() uint32 {
+	if x != nil {
+		return x.Id
+	}
+	return 0
+}
+
+func (x *YamlScalarConf) GetNum() int32 {
+	if x != nil {
+		return x.Num
+	}
+	return 0
+}
+
+func (x *YamlScalarConf) GetValue() uint64 {
+	if x != nil {
+		return x.Value
+	}
+	return 0
+}
+
+func (x *YamlScalarConf) GetWeight() int64 {
+	if x != nil {
+		return x.Weight
+	}
+	return 0
+}
+
+func (x *YamlScalarConf) GetPercentage() float32 {
+	if x != nil {
+		return x.Percentage
+	}
+	return 0
+}
+
+func (x *YamlScalarConf) GetRatio() float64 {
+	if x != nil {
+		return x.Ratio
+	}
+	return 0
+}
+
+func (x *YamlScalarConf) GetName() string {
+	if x != nil {
+		return x.Name
+	}
+	return ""
+}
+
+func (x *YamlScalarConf) GetBlob() []byte {
+	if x != nil {
+		return x.Blob
+	}
+	return nil
+}
+
+func (x *YamlScalarConf) GetOk() bool {
+	if x != nil {
+		return x.Ok
+	}
+	return false
+}
+
 type IncellMap_Fruit struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -303,7 +414,7 @@ type IncellMap_Fruit struct {
 func (x *IncellMap_Fruit) Reset() {
 	*x = IncellMap_Fruit{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[6]
+		mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[7]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -316,7 +427,7 @@ func (x *IncellMap_Fruit) String() string {
 func (*IncellMap_Fruit) ProtoMessage() {}
 
 func (x *IncellMap_Fruit) ProtoReflect() protoreflect.Message {
-	mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[6]
+	mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[7]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -358,7 +469,7 @@ type IncellMap_Item struct {
 func (x *IncellMap_Item) Reset() {
 	*x = IncellMap_Item{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[9]
+		mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[10]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -371,7 +482,7 @@ func (x *IncellMap_Item) String() string {
 func (*IncellMap_Item) ProtoMessage() {}
 
 func (x *IncellMap_Item) ProtoReflect() protoreflect.Message {
-	mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[9]
+	mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[10]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -413,7 +524,7 @@ type MallConf_Shop struct {
 func (x *MallConf_Shop) Reset() {
 	*x = MallConf_Shop{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[12]
+		mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[13]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -426,7 +537,7 @@ func (x *MallConf_Shop) String() string {
 func (*MallConf_Shop) ProtoMessage() {}
 
 func (x *MallConf_Shop) ProtoReflect() protoreflect.Message {
-	mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[12]
+	mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[13]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -468,7 +579,7 @@ type MallConf_Shop_Goods struct {
 func (x *MallConf_Shop_Goods) Reset() {
 	*x = MallConf_Shop_Goods{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[14]
+		mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[15]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -481,7 +592,7 @@ func (x *MallConf_Shop_Goods) String() string {
 func (*MallConf_Shop_Goods) ProtoMessage() {}
 
 func (x *MallConf_Shop_Goods) ProtoReflect() protoreflect.Message {
-	mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[14]
+	mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[15]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -523,7 +634,7 @@ type RewardConf_Reward struct {
 func (x *RewardConf_Reward) Reset() {
 	*x = RewardConf_Reward{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[16]
+		mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[17]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -536,7 +647,7 @@ func (x *RewardConf_Reward) String() string {
 func (*RewardConf_Reward) ProtoMessage() {}
 
 func (x *RewardConf_Reward) ProtoReflect() protoreflect.Message {
-	mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[16]
+	mi := &file_tableau_protobuf_unittest_unittest_proto_msgTypes[17]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -708,13 +819,34 @@ var file_tableau_protobuf_unittest_unittest_proto_rawDesc = []byte{
 	0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x75, 0x6e, 0x69, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x49,
 	0x74, 0x65, 0x6d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x3a, 0x18,
 	0x82, 0xb5, 0x18, 0x14, 0x0a, 0x0a, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x43, 0x6f, 0x6e, 0x66,
-	0x10, 0x01, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x42, 0x56, 0x5a, 0x37, 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, 0x2f, 0x75, 0x6e, 0x69, 0x74, 0x74, 0x65, 0x73,
-	0x74, 0x70, 0x62, 0x82, 0xb5, 0x18, 0x19, 0x0a, 0x17, 0x75, 0x6e, 0x69, 0x74, 0x74, 0x65, 0x73,
-	0x74, 0x2f, 0x55, 0x6e, 0x69, 0x74, 0x74, 0x65, 0x73, 0x74, 0x23, 0x2a, 0x2e, 0x63, 0x73, 0x76,
-	0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x10, 0x01, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x22, 0xd5, 0x02, 0x0a, 0x0e, 0x59, 0x61, 0x6d,
+	0x6c, 0x53, 0x63, 0x61, 0x6c, 0x61, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x12, 0x18, 0x0a, 0x02, 0x69,
+	0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x08, 0x82, 0xb5, 0x18, 0x04, 0x0a, 0x02, 0x49,
+	0x44, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x03, 0x6e, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x05, 0x42, 0x09, 0x82, 0xb5, 0x18, 0x05, 0x0a, 0x03, 0x4e, 0x75, 0x6d, 0x52, 0x03, 0x6e,
+	0x75, 0x6d, 0x12, 0x21, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
+	0x04, 0x42, 0x0b, 0x82, 0xb5, 0x18, 0x07, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05,
+	0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x24, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18,
+	0x04, 0x20, 0x01, 0x28, 0x03, 0x42, 0x0c, 0x82, 0xb5, 0x18, 0x08, 0x0a, 0x06, 0x57, 0x65, 0x69,
+	0x67, 0x68, 0x74, 0x52, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x30, 0x0a, 0x0a, 0x70,
+	0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x02, 0x42,
+	0x10, 0x82, 0xb5, 0x18, 0x0c, 0x0a, 0x0a, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67,
+	0x65, 0x52, 0x0a, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x12, 0x21, 0x0a,
+	0x05, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x42, 0x0b, 0x82, 0xb5,
+	0x18, 0x07, 0x0a, 0x05, 0x52, 0x61, 0x74, 0x69, 0x6f, 0x52, 0x05, 0x72, 0x61, 0x74, 0x69, 0x6f,
+	0x12, 0x1e, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a,
+	0x82, 0xb5, 0x18, 0x06, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
+	0x12, 0x1e, 0x0a, 0x04, 0x62, 0x6c, 0x6f, 0x62, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x0a,
+	0x82, 0xb5, 0x18, 0x06, 0x0a, 0x04, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x04, 0x62, 0x6c, 0x6f, 0x62,
+	0x12, 0x18, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x42, 0x08, 0x82, 0xb5,
+	0x18, 0x04, 0x0a, 0x02, 0x4f, 0x4b, 0x52, 0x02, 0x6f, 0x6b, 0x3a, 0x14, 0x82, 0xb5, 0x18, 0x10,
+	0x0a, 0x0e, 0x59, 0x61, 0x6d, 0x6c, 0x53, 0x63, 0x61, 0x6c, 0x61, 0x72, 0x43, 0x6f, 0x6e, 0x66,
+	0x42, 0x56, 0x5a, 0x37, 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,
+	0x2f, 0x75, 0x6e, 0x69, 0x74, 0x74, 0x65, 0x73, 0x74, 0x70, 0x62, 0x82, 0xb5, 0x18, 0x19, 0x0a,
+	0x17, 0x75, 0x6e, 0x69, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x55, 0x6e, 0x69, 0x74, 0x74, 0x65,
+	0x73, 0x74, 0x23, 0x2a, 0x2e, 0x63, 0x73, 0x76, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -729,52 +861,53 @@ func file_tableau_protobuf_unittest_unittest_proto_rawDescGZIP() []byte {
 	return file_tableau_protobuf_unittest_unittest_proto_rawDescData
 }
 
-var file_tableau_protobuf_unittest_unittest_proto_msgTypes = make([]protoimpl.MessageInfo, 18)
+var file_tableau_protobuf_unittest_unittest_proto_msgTypes = make([]protoimpl.MessageInfo, 19)
 var file_tableau_protobuf_unittest_unittest_proto_goTypes = []interface{}{
 	(*IncellMap)(nil),           // 0: unittest.IncellMap
 	(*IncellList)(nil),          // 1: unittest.IncellList
 	(*ItemConf)(nil),            // 2: unittest.ItemConf
 	(*MallConf)(nil),            // 3: unittest.MallConf
 	(*RewardConf)(nil),          // 4: unittest.RewardConf
-	nil,                         // 5: unittest.IncellMap.FruitMapEntry
-	(*IncellMap_Fruit)(nil),     // 6: unittest.IncellMap.Fruit
-	nil,                         // 7: unittest.IncellMap.FlavorMapEntry
-	nil,                         // 8: unittest.IncellMap.ItemMapEntry
-	(*IncellMap_Item)(nil),      // 9: unittest.IncellMap.Item
-	nil,                         // 10: unittest.ItemConf.ItemMapEntry
-	nil,                         // 11: unittest.MallConf.ShopMapEntry
-	(*MallConf_Shop)(nil),       // 12: unittest.MallConf.Shop
-	nil,                         // 13: unittest.MallConf.Shop.GoodsMapEntry
-	(*MallConf_Shop_Goods)(nil), // 14: unittest.MallConf.Shop.Goods
-	nil,                         // 15: unittest.RewardConf.RewardMapEntry
-	(*RewardConf_Reward)(nil),   // 16: unittest.RewardConf.Reward
-	nil,                         // 17: unittest.RewardConf.Reward.ItemMapEntry
-	(FruitFlavor)(0),            // 18: unittest.FruitFlavor
-	(*Item)(nil),                // 19: unittest.Item
-	(FruitType)(0),              // 20: unittest.FruitType
+	(*YamlScalarConf)(nil),      // 5: unittest.YamlScalarConf
+	nil,                         // 6: unittest.IncellMap.FruitMapEntry
+	(*IncellMap_Fruit)(nil),     // 7: unittest.IncellMap.Fruit
+	nil,                         // 8: unittest.IncellMap.FlavorMapEntry
+	nil,                         // 9: unittest.IncellMap.ItemMapEntry
+	(*IncellMap_Item)(nil),      // 10: unittest.IncellMap.Item
+	nil,                         // 11: unittest.ItemConf.ItemMapEntry
+	nil,                         // 12: unittest.MallConf.ShopMapEntry
+	(*MallConf_Shop)(nil),       // 13: unittest.MallConf.Shop
+	nil,                         // 14: unittest.MallConf.Shop.GoodsMapEntry
+	(*MallConf_Shop_Goods)(nil), // 15: unittest.MallConf.Shop.Goods
+	nil,                         // 16: unittest.RewardConf.RewardMapEntry
+	(*RewardConf_Reward)(nil),   // 17: unittest.RewardConf.Reward
+	nil,                         // 18: unittest.RewardConf.Reward.ItemMapEntry
+	(FruitFlavor)(0),            // 19: unittest.FruitFlavor
+	(*Item)(nil),                // 20: unittest.Item
+	(FruitType)(0),              // 21: unittest.FruitType
 }
 var file_tableau_protobuf_unittest_unittest_proto_depIdxs = []int32{
-	5,  // 0: unittest.IncellMap.fruit_map:type_name -> unittest.IncellMap.FruitMapEntry
-	7,  // 1: unittest.IncellMap.flavor_map:type_name -> unittest.IncellMap.FlavorMapEntry
-	8,  // 2: unittest.IncellMap.item_map:type_name -> unittest.IncellMap.ItemMapEntry
-	18, // 3: unittest.IncellList.flavor_list:type_name -> unittest.FruitFlavor
-	19, // 4: unittest.IncellList.item_list:type_name -> unittest.Item
-	10, // 5: unittest.ItemConf.item_map:type_name -> unittest.ItemConf.ItemMapEntry
-	11, // 6: unittest.MallConf.shop_map:type_name -> unittest.MallConf.ShopMapEntry
-	15, // 7: unittest.RewardConf.reward_map:type_name -> unittest.RewardConf.RewardMapEntry
-	6,  // 8: unittest.IncellMap.FruitMapEntry.value:type_name -> unittest.IncellMap.Fruit
-	20, // 9: unittest.IncellMap.Fruit.key:type_name -> unittest.FruitType
-	18, // 10: unittest.IncellMap.FlavorMapEntry.value:type_name -> unittest.FruitFlavor
-	9,  // 11: unittest.IncellMap.ItemMapEntry.value:type_name -> unittest.IncellMap.Item
-	20, // 12: unittest.IncellMap.Item.key:type_name -> unittest.FruitType
-	18, // 13: unittest.IncellMap.Item.value:type_name -> unittest.FruitFlavor
-	19, // 14: unittest.ItemConf.ItemMapEntry.value:type_name -> unittest.Item
-	12, // 15: unittest.MallConf.ShopMapEntry.value:type_name -> unittest.MallConf.Shop
-	13, // 16: unittest.MallConf.Shop.goods_map:type_name -> unittest.MallConf.Shop.GoodsMapEntry
-	14, // 17: unittest.MallConf.Shop.GoodsMapEntry.value:type_name -> unittest.MallConf.Shop.Goods
-	16, // 18: unittest.RewardConf.RewardMapEntry.value:type_name -> unittest.RewardConf.Reward
-	17, // 19: unittest.RewardConf.Reward.item_map:type_name -> unittest.RewardConf.Reward.ItemMapEntry
-	19, // 20: unittest.RewardConf.Reward.ItemMapEntry.value:type_name -> unittest.Item
+	6,  // 0: unittest.IncellMap.fruit_map:type_name -> unittest.IncellMap.FruitMapEntry
+	8,  // 1: unittest.IncellMap.flavor_map:type_name -> unittest.IncellMap.FlavorMapEntry
+	9,  // 2: unittest.IncellMap.item_map:type_name -> unittest.IncellMap.ItemMapEntry
+	19, // 3: unittest.IncellList.flavor_list:type_name -> unittest.FruitFlavor
+	20, // 4: unittest.IncellList.item_list:type_name -> unittest.Item
+	11, // 5: unittest.ItemConf.item_map:type_name -> unittest.ItemConf.ItemMapEntry
+	12, // 6: unittest.MallConf.shop_map:type_name -> unittest.MallConf.ShopMapEntry
+	16, // 7: unittest.RewardConf.reward_map:type_name -> unittest.RewardConf.RewardMapEntry
+	7,  // 8: unittest.IncellMap.FruitMapEntry.value:type_name -> unittest.IncellMap.Fruit
+	21, // 9: unittest.IncellMap.Fruit.key:type_name -> unittest.FruitType
+	19, // 10: unittest.IncellMap.FlavorMapEntry.value:type_name -> unittest.FruitFlavor
+	10, // 11: unittest.IncellMap.ItemMapEntry.value:type_name -> unittest.IncellMap.Item
+	21, // 12: unittest.IncellMap.Item.key:type_name -> unittest.FruitType
+	19, // 13: unittest.IncellMap.Item.value:type_name -> unittest.FruitFlavor
+	20, // 14: unittest.ItemConf.ItemMapEntry.value:type_name -> unittest.Item
+	13, // 15: unittest.MallConf.ShopMapEntry.value:type_name -> unittest.MallConf.Shop
+	14, // 16: unittest.MallConf.Shop.goods_map:type_name -> unittest.MallConf.Shop.GoodsMapEntry
+	15, // 17: unittest.MallConf.Shop.GoodsMapEntry.value:type_name -> unittest.MallConf.Shop.Goods
+	17, // 18: unittest.RewardConf.RewardMapEntry.value:type_name -> unittest.RewardConf.Reward
+	18, // 19: unittest.RewardConf.Reward.item_map:type_name -> unittest.RewardConf.Reward.ItemMapEntry
+	20, // 20: unittest.RewardConf.Reward.ItemMapEntry.value:type_name -> unittest.Item
 	21, // [21:21] is the sub-list for method output_type
 	21, // [21:21] is the sub-list for method input_type
 	21, // [21:21] is the sub-list for extension type_name
@@ -849,7 +982,19 @@ func file_tableau_protobuf_unittest_unittest_proto_init() {
 				return nil
 			}
 		}
-		file_tableau_protobuf_unittest_unittest_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+		file_tableau_protobuf_unittest_unittest_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*YamlScalarConf); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_tableau_protobuf_unittest_unittest_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*IncellMap_Fruit); i {
 			case 0:
 				return &v.state
@@ -861,7 +1006,7 @@ func file_tableau_protobuf_unittest_unittest_proto_init() {
 				return nil
 			}
 		}
-		file_tableau_protobuf_unittest_unittest_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
+		file_tableau_protobuf_unittest_unittest_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*IncellMap_Item); i {
 			case 0:
 				return &v.state
@@ -873,7 +1018,7 @@ func file_tableau_protobuf_unittest_unittest_proto_init() {
 				return nil
 			}
 		}
-		file_tableau_protobuf_unittest_unittest_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
+		file_tableau_protobuf_unittest_unittest_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*MallConf_Shop); i {
 			case 0:
 				return &v.state
@@ -885,7 +1030,7 @@ func file_tableau_protobuf_unittest_unittest_proto_init() {
 				return nil
 			}
 		}
-		file_tableau_protobuf_unittest_unittest_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
+		file_tableau_protobuf_unittest_unittest_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*MallConf_Shop_Goods); i {
 			case 0:
 				return &v.state
@@ -897,7 +1042,7 @@ func file_tableau_protobuf_unittest_unittest_proto_init() {
 				return nil
 			}
 		}
-		file_tableau_protobuf_unittest_unittest_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
+		file_tableau_protobuf_unittest_unittest_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*RewardConf_Reward); i {
 			case 0:
 				return &v.state
@@ -916,7 +1061,7 @@ func file_tableau_protobuf_unittest_unittest_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_tableau_protobuf_unittest_unittest_proto_rawDesc,
 			NumEnums:      0,
-			NumMessages:   18,
+			NumMessages:   19,
 			NumExtensions: 0,
 			NumServices:   0,
 		},
diff --git a/test/functest/conf/YamlFieldPropOptionalConf.json b/test/functest/conf/YamlFieldPropOptionalConf.json
new file mode 100644
index 0000000..8da8be0
--- /dev/null
+++ b/test/functest/conf/YamlFieldPropOptionalConf.json
@@ -0,0 +1,39 @@
+{
+    "id": 1,
+    "num": 0,
+    "type": "FRUIT_TYPE_UNKNOWN",
+    "optionalStruct": null,
+    "structMap": {
+        "1": {
+            "key": 1,
+            "name": "apple",
+            "num": 10
+        },
+        "2": {
+            "key": 2,
+            "name": "orange",
+            "num": 20
+        },
+        "3": {
+            "key": 3,
+            "name": "banana",
+            "num": 0
+        }
+    },
+    "optionalStructMap": {},
+    "structList": [
+        {
+            "name": "apple",
+            "num": 10
+        },
+        {
+            "name": "orange",
+            "num": 20
+        },
+        {
+            "name": "banana",
+            "num": 0
+        }
+    ],
+    "optionalStructList": []
+}
\ No newline at end of file
diff --git a/test/functest/proto/xml__metasheet__merger.proto b/test/functest/proto/xml__metasheet__merger.proto
index a90023f..f6e56f5 100644
--- a/test/functest/proto/xml__metasheet__merger.proto
+++ b/test/functest/proto/xml__metasheet__merger.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.5.0). DO NOT EDIT.
+// Code generated by tableau (protogen v0.6.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
@@ -11,7 +11,7 @@ option go_package = "github.com/tableauio/tableau/test/functest/protoconf";
 option (tableau.workbook) = {name:"xml/metasheet/Merger.xml"};
 
 message MergerConf {
-  option (tableau.worksheet) = {name:"MergerConf" merger:"Merger*.xml"};
+  option (tableau.worksheet) = {name:"MergerConf" merger:"Merger*.xml" optional:true};
 
   map<int32, Fuben> fuben_map = 1 [(tableau.field) = {name:"Fuben" key:"Id"}];
   message Fuben {
diff --git a/test/functest/proto/yaml__fieldprop__optional.proto b/test/functest/proto/yaml__fieldprop__optional.proto
new file mode 100644
index 0000000..914e799
--- /dev/null
+++ b/test/functest/proto/yaml__fieldprop__optional.proto
@@ -0,0 +1,43 @@
+// Code generated by tableau (protogen v0.6.0). DO NOT EDIT.
+// clang-format off
+
+syntax = "proto3";
+
+package protoconf;
+
+import "common/common.proto";
+import "tableau/protobuf/tableau.proto";
+
+option go_package = "github.com/tableauio/tableau/test/functest/protoconf";
+option (tableau.workbook) = {name:"yaml/fieldprop/Optional.yaml"};
+
+message YamlFieldPropOptionalConf {
+  option (tableau.worksheet) = {name:"YamlFieldPropOptionalConf"};
+
+  uint32 id = 1 [(tableau.field) = {name:"ID"}];
+  int32 num = 2 [(tableau.field) = {name:"Num" prop:{optional:true}}];
+  protoconf.FruitType type = 3 [(tableau.field) = {name:"Type" prop:{optional:true}}];
+  Struct optional_struct = 4 [(tableau.field) = {name:"OptionalStruct" prop:{optional:true}}];
+  message Struct {
+    string type = 1 [(tableau.field) = {name:"Type"}];
+    int32 price = 2 [(tableau.field) = {name:"Price"}];
+  }
+  map<uint32, Item> struct_map = 5 [(tableau.field) = {name:"StructMap" key:"@key"}];
+  message Item {
+    uint32 key = 1 [(tableau.field) = {name:"@key"}];
+    string name = 2 [(tableau.field) = {name:"Name"}];
+    int32 num = 3 [(tableau.field) = {name:"Num" prop:{optional:true}}];
+  }
+  map<uint32, Item2> optional_struct_map = 6 [(tableau.field) = {name:"OptionalStructMap" key:"@key" prop:{optional:true}}];
+  message Item2 {
+    uint32 key = 1 [(tableau.field) = {name:"@key" prop:{optional:true}}];
+    string name = 2 [(tableau.field) = {name:"Name"}];
+    int32 num = 3 [(tableau.field) = {name:"Num"}];
+  }
+  repeated Fruit struct_list = 7 [(tableau.field) = {name:"StructList"}];
+  message Fruit {
+    string name = 1 [(tableau.field) = {name:"Name"}];
+    int32 num = 2 [(tableau.field) = {name:"Num" prop:{optional:true}}];
+  }
+  repeated Fruit optional_struct_list = 8 [(tableau.field) = {name:"OptionalStructList" prop:{optional:true}}];
+}
diff --git a/test/functest/testdata/xml/metasheet/Merger.xml b/test/functest/testdata/xml/metasheet/Merger.xml
index dd58a4a..3a459b6 100644
--- a/test/functest/testdata/xml/metasheet/Merger.xml
+++ b/test/functest/testdata/xml/metasheet/Merger.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <!--
 <@TABLEAU>
-    <Item Sheet="MergerConf" Merger="Merger*.xml"/>
+    <Item Sheet="MergerConf" Merger="Merger*.xml" Optional="true"/>
 </@TABLEAU>
 
 <MergerConf>
diff --git a/test/functest/testdata/yaml/Enum.yaml b/test/functest/testdata/yaml/Enum.yaml
index a83fe8d..c74eaac 100644
--- a/test/functest/testdata/yaml/Enum.yaml
+++ b/test/functest/testdata/yaml/Enum.yaml
@@ -1,4 +1,3 @@
----
 # define metasheet: generate all sheets
 "@sheet": "@TABLEAU"
 ---
diff --git a/test/functest/testdata/yaml/List.yaml b/test/functest/testdata/yaml/List.yaml
index 1ec7a9c..bc55ea3 100644
--- a/test/functest/testdata/yaml/List.yaml
+++ b/test/functest/testdata/yaml/List.yaml
@@ -1,4 +1,3 @@
----
 # define metasheet: generate all sheets
 "@sheet": "@TABLEAU"
 ---
diff --git a/test/functest/testdata/yaml/Map.yaml b/test/functest/testdata/yaml/Map.yaml
index c90c0a2..9ce395b 100644
--- a/test/functest/testdata/yaml/Map.yaml
+++ b/test/functest/testdata/yaml/Map.yaml
@@ -1,4 +1,3 @@
----
 # define metasheet: generate all sheets
 "@sheet": "@TABLEAU"
 ---
diff --git a/test/functest/testdata/yaml/Scalar.yaml b/test/functest/testdata/yaml/Scalar.yaml
index 0bf294a..3cb9cec 100644
--- a/test/functest/testdata/yaml/Scalar.yaml
+++ b/test/functest/testdata/yaml/Scalar.yaml
@@ -1,4 +1,3 @@
----
 # define metasheet: generate all sheets
 "@sheet": "@TABLEAU"
 ---
diff --git a/test/functest/testdata/yaml/Struct.yaml b/test/functest/testdata/yaml/Struct.yaml
index d23a5a7..9f963a7 100644
--- a/test/functest/testdata/yaml/Struct.yaml
+++ b/test/functest/testdata/yaml/Struct.yaml
@@ -1,4 +1,3 @@
----
 # define metasheet: generate all sheets
 "@sheet": "@TABLEAU"
 ---
diff --git a/test/functest/testdata/yaml/fieldprop/Optional.yaml b/test/functest/testdata/yaml/fieldprop/Optional.yaml
new file mode 100644
index 0000000..4f2239f
--- /dev/null
+++ b/test/functest/testdata/yaml/fieldprop/Optional.yaml
@@ -0,0 +1,65 @@
+# define metasheet: generate all sheets
+"@sheet": "@TABLEAU"
+---
+# define schema
+"@sheet": "@YamlFieldPropOptionalConf"
+ID: uint32
+Num: "int32|{optional:true}"
+Type: "enum<.FruitType>|{optional:true}"
+OptionalStruct:
+  "@type": "{Struct}|{optional:true}"
+  Type: string
+  Price: int32
+StructMap:
+  "@type": "map<uint32, Item>"
+  "@struct":
+    Name: string
+    Num: "int32|{optional:true}"
+OptionalStructMap:
+  "@type": "map<uint32, Item2>|{optional:true}"
+  "@struct":
+    Name: string
+    Num: "int32"
+StructList:
+  "@type": "[Fruit]"
+  "@struct":
+    Name: string
+    Num: "int32|{optional:true}"
+OptionalStructList: "[Fruit]|{optional:true}"
+---
+"@sheet": YamlFieldPropOptionalConf
+ID: 1
+# Num: 10 # optional field
+# Type: FRUIT_TYPE_APPLE # optional field
+# OptionalStruct:
+#   Type: dollar
+#   Price: 100
+StructMap:
+  1:
+    Name: apple
+    Num: 10
+  2:
+    Name: orange
+    Num: 20
+  3:
+    Name: banana
+    # Num: 10 # optional field
+# OptionalStructMap:
+#   1:
+#     Name: apple
+#     Num: 10
+#   2:
+#     Name: orange
+#     Num: 20
+StructList:
+  - Name: apple
+    Num: 10
+  - Name: orange
+    Num: 20
+  - Name: banana
+    # Num: 10 # optional field
+# OptionalStructList:
+#   - Name: apple
+#     Num: 10
+#   - Name: orange
+#     Num: 20