Go language library for reading and writing Microsoft Excel™ (XLAM / XLSM / XLSX / XLTM / XLTX) spreadsheets
修订版 | f8667386dcde788d8232b652ac85a138c0d20bf3 (tree) |
---|---|
时间 | 2022-08-27 01:45:46 |
作者 | chenliu1993 <13630583107@163....> |
Commiter | GitHub |
@@ -259,6 +259,18 @@ func (f *File) AddDataValidation(sheet string, dv *DataValidation) error { | ||
259 | 259 | return err |
260 | 260 | } |
261 | 261 | |
262 | +// GetDataValidations returns data validations list by given worksheet name. | |
263 | +func (f *File) GetDataValidations(sheet string) ([]*DataValidation, error) { | |
264 | + ws, err := f.workSheetReader(sheet) | |
265 | + if err != nil { | |
266 | + return nil, err | |
267 | + } | |
268 | + if ws.DataValidations == nil || len(ws.DataValidations.DataValidation) == 0 { | |
269 | + return nil, err | |
270 | + } | |
271 | + return ws.DataValidations.DataValidation, err | |
272 | +} | |
273 | + | |
262 | 274 | // DeleteDataValidation delete data validation by given worksheet name and |
263 | 275 | // reference sequence. All data validations in the worksheet will be deleted |
264 | 276 | // if not specify reference sequence parameter. |
@@ -32,6 +32,11 @@ func TestDataValidation(t *testing.T) { | ||
32 | 32 | dvRange.SetError(DataValidationErrorStyleWarning, "error title", "error body") |
33 | 33 | dvRange.SetError(DataValidationErrorStyleInformation, "error title", "error body") |
34 | 34 | assert.NoError(t, f.AddDataValidation("Sheet1", dvRange)) |
35 | + | |
36 | + dataValidations, err := f.GetDataValidations("Sheet1") | |
37 | + assert.NoError(t, err) | |
38 | + assert.Equal(t, len(dataValidations), 1) | |
39 | + | |
35 | 40 | assert.NoError(t, f.SaveAs(resultFile)) |
36 | 41 | |
37 | 42 | dvRange = NewDataValidation(true) |
@@ -39,6 +44,11 @@ func TestDataValidation(t *testing.T) { | ||
39 | 44 | assert.NoError(t, dvRange.SetRange(10, 20, DataValidationTypeWhole, DataValidationOperatorGreaterThan)) |
40 | 45 | dvRange.SetInput("input title", "input body") |
41 | 46 | assert.NoError(t, f.AddDataValidation("Sheet1", dvRange)) |
47 | + | |
48 | + dataValidations, err = f.GetDataValidations("Sheet1") | |
49 | + assert.NoError(t, err) | |
50 | + assert.Equal(t, len(dataValidations), 2) | |
51 | + | |
42 | 52 | assert.NoError(t, f.SaveAs(resultFile)) |
43 | 53 | |
44 | 54 | f.NewSheet("Sheet2") |
@@ -49,6 +59,12 @@ func TestDataValidation(t *testing.T) { | ||
49 | 59 | assert.NoError(t, dvRange.SetRange("INDIRECT($A$2)", "INDIRECT($A$3)", DataValidationTypeWhole, DataValidationOperatorBetween)) |
50 | 60 | dvRange.SetError(DataValidationErrorStyleStop, "error title", "error body") |
51 | 61 | assert.NoError(t, f.AddDataValidation("Sheet2", dvRange)) |
62 | + dataValidations, err = f.GetDataValidations("Sheet1") | |
63 | + assert.NoError(t, err) | |
64 | + assert.Equal(t, len(dataValidations), 2) | |
65 | + dataValidations, err = f.GetDataValidations("Sheet2") | |
66 | + assert.NoError(t, err) | |
67 | + assert.Equal(t, len(dataValidations), 1) | |
52 | 68 | |
53 | 69 | dvRange = NewDataValidation(true) |
54 | 70 | dvRange.Sqref = "A5:B6" |
@@ -67,7 +83,22 @@ func TestDataValidation(t *testing.T) { | ||
67 | 83 | } |
68 | 84 | assert.Equal(t, `<formula1>"A<,B>,C"",D ,E',F"</formula1>`, dvRange.Formula1) |
69 | 85 | assert.NoError(t, f.AddDataValidation("Sheet1", dvRange)) |
86 | + | |
87 | + dataValidations, err = f.GetDataValidations("Sheet1") | |
88 | + assert.NoError(t, err) | |
89 | + assert.Equal(t, len(dataValidations), 3) | |
90 | + | |
91 | + // Test get data validation on no exists worksheet | |
92 | + _, err = f.GetDataValidations("SheetN") | |
93 | + assert.EqualError(t, err, "sheet SheetN is not exist") | |
94 | + | |
70 | 95 | assert.NoError(t, f.SaveAs(resultFile)) |
96 | + | |
97 | + // Test get data validation on a worksheet without data validation settings | |
98 | + f = NewFile() | |
99 | + dataValidations, err = f.GetDataValidations("Sheet1") | |
100 | + assert.NoError(t, err) | |
101 | + assert.Equal(t, []*DataValidation(nil), dataValidations) | |
71 | 102 | } |
72 | 103 | |
73 | 104 | func TestDataValidationError(t *testing.T) { |
@@ -1041,15 +1041,15 @@ func TestConditionalFormat(t *testing.T) { | ||
1041 | 1041 | assert.NoError(t, f.SetConditionalFormat(sheet1, "A1:A10", `[{"type":"2_color_scale","criteria":"=","min_type":"min","max_type":"max","min_color":"#F8696B","max_color":"#63BE7B"}]`)) |
1042 | 1042 | // Color scales: 3 color. |
1043 | 1043 | assert.NoError(t, f.SetConditionalFormat(sheet1, "B1:B10", `[{"type":"3_color_scale","criteria":"=","min_type":"min","mid_type":"percentile","max_type":"max","min_color":"#F8696B","mid_color":"#FFEB84","max_color":"#63BE7B"}]`)) |
1044 | - // Hightlight cells rules: between... | |
1044 | + // Highlight cells rules: between... | |
1045 | 1045 | assert.NoError(t, f.SetConditionalFormat(sheet1, "C1:C10", fmt.Sprintf(`[{"type":"cell","criteria":"between","format":%d,"minimum":"6","maximum":"8"}]`, format1))) |
1046 | - // Hightlight cells rules: Greater Than... | |
1046 | + // Highlight cells rules: Greater Than... | |
1047 | 1047 | assert.NoError(t, f.SetConditionalFormat(sheet1, "D1:D10", fmt.Sprintf(`[{"type":"cell","criteria":">","format":%d,"value":"6"}]`, format3))) |
1048 | - // Hightlight cells rules: Equal To... | |
1048 | + // Highlight cells rules: Equal To... | |
1049 | 1049 | assert.NoError(t, f.SetConditionalFormat(sheet1, "E1:E10", fmt.Sprintf(`[{"type":"top","criteria":"=","format":%d}]`, format3))) |
1050 | - // Hightlight cells rules: Not Equal To... | |
1050 | + // Highlight cells rules: Not Equal To... | |
1051 | 1051 | assert.NoError(t, f.SetConditionalFormat(sheet1, "F1:F10", fmt.Sprintf(`[{"type":"unique","criteria":"=","format":%d}]`, format2))) |
1052 | - // Hightlight cells rules: Duplicate Values... | |
1052 | + // Highlight cells rules: Duplicate Values... | |
1053 | 1053 | assert.NoError(t, f.SetConditionalFormat(sheet1, "G1:G10", fmt.Sprintf(`[{"type":"duplicate","criteria":"=","format":%d}]`, format2))) |
1054 | 1054 | // Top/Bottom rules: Top 10%. |
1055 | 1055 | assert.NoError(t, f.SetConditionalFormat(sheet1, "H1:H10", fmt.Sprintf(`[{"type":"top","criteria":"=","format":%d,"value":"6","percent":true}]`, format1))) |
@@ -844,6 +844,31 @@ var criteriaType = map[string]string{ | ||
844 | 844 | "continue month": "continueMonth", |
845 | 845 | } |
846 | 846 | |
847 | +// operatorType defined the list of valid operator types. | |
848 | +var operatorType = map[string]string{ | |
849 | + "lastMonth": "last month", | |
850 | + "between": "between", | |
851 | + "notEqual": "not equal to", | |
852 | + "greaterThan": "greater than", | |
853 | + "lessThanOrEqual": "less than or equal to", | |
854 | + "today": "today", | |
855 | + "equal": "equal to", | |
856 | + "notContains": "not containing", | |
857 | + "thisWeek": "this week", | |
858 | + "endsWith": "ends with", | |
859 | + "yesterday": "yesterday", | |
860 | + "lessThan": "less than", | |
861 | + "beginsWith": "begins with", | |
862 | + "last7Days": "last 7 days", | |
863 | + "thisMonth": "this month", | |
864 | + "containsText": "containing", | |
865 | + "lastWeek": "last week", | |
866 | + "continueWeek": "continue week", | |
867 | + "continueMonth": "continue month", | |
868 | + "notBetween": "not between", | |
869 | + "greaterThanOrEqual": "greater than or equal to", | |
870 | +} | |
871 | + | |
847 | 872 | // formatToInt provides a function to convert original string to integer |
848 | 873 | // format as string type by given built-in number formats code and cell |
849 | 874 | // string. |
@@ -2726,7 +2751,7 @@ func (f *File) SetCellStyle(sheet, hCell, vCell string, styleID int) error { | ||
2726 | 2751 | // type: minimum - The minimum parameter is used to set the lower limiting value |
2727 | 2752 | // when the criteria is either "between" or "not between". |
2728 | 2753 | // |
2729 | -// // Hightlight cells rules: between... | |
2754 | +// // Highlight cells rules: between... | |
2730 | 2755 | // f.SetConditionalFormat("Sheet1", "A1:A10", fmt.Sprintf(`[{"type":"cell","criteria":"between","format":%d,"minimum":"6","maximum":"8"}]`, format)) |
2731 | 2756 | // |
2732 | 2757 | // type: maximum - The maximum parameter is used to set the upper limiting value |
@@ -2744,12 +2769,12 @@ func (f *File) SetCellStyle(sheet, hCell, vCell string, styleID int) error { | ||
2744 | 2769 | // |
2745 | 2770 | // type: duplicate - The duplicate type is used to highlight duplicate cells in a range: |
2746 | 2771 | // |
2747 | -// // Hightlight cells rules: Duplicate Values... | |
2772 | +// // Highlight cells rules: Duplicate Values... | |
2748 | 2773 | // f.SetConditionalFormat("Sheet1", "A1:A10", fmt.Sprintf(`[{"type":"duplicate","criteria":"=","format":%d}]`, format)) |
2749 | 2774 | // |
2750 | 2775 | // type: unique - The unique type is used to highlight unique cells in a range: |
2751 | 2776 | // |
2752 | -// // Hightlight cells rules: Not Equal To... | |
2777 | +// // Highlight cells rules: Not Equal To... | |
2753 | 2778 | // f.SetConditionalFormat("Sheet1", "A1:A10", fmt.Sprintf(`[{"type":"unique","criteria":"=","format":%d}]`, format)) |
2754 | 2779 | // |
2755 | 2780 | // type: top - The top type is used to specify the top n values by number or percentage in a range: |
@@ -2837,7 +2862,7 @@ func (f *File) SetConditionalFormat(sheet, area, formatSet string) error { | ||
2837 | 2862 | "2_color_scale": drawCondFmtColorScale, |
2838 | 2863 | "3_color_scale": drawCondFmtColorScale, |
2839 | 2864 | "dataBar": drawCondFmtDataBar, |
2840 | - "expression": drawConfFmtExp, | |
2865 | + "expression": drawCondFmtExp, | |
2841 | 2866 | } |
2842 | 2867 | |
2843 | 2868 | ws, err := f.workSheetReader(sheet) |
@@ -2854,9 +2879,9 @@ func (f *File) SetConditionalFormat(sheet, area, formatSet string) error { | ||
2854 | 2879 | // Check for valid criteria types. |
2855 | 2880 | ct, ok = criteriaType[v.Criteria] |
2856 | 2881 | if ok || vt == "expression" { |
2857 | - drawfunc, ok := drawContFmtFunc[vt] | |
2882 | + drawFunc, ok := drawContFmtFunc[vt] | |
2858 | 2883 | if ok { |
2859 | - cfRule = append(cfRule, drawfunc(p, ct, v)) | |
2884 | + cfRule = append(cfRule, drawFunc(p, ct, v)) | |
2860 | 2885 | } |
2861 | 2886 | } |
2862 | 2887 | } |
@@ -2869,6 +2894,152 @@ func (f *File) SetConditionalFormat(sheet, area, formatSet string) error { | ||
2869 | 2894 | return err |
2870 | 2895 | } |
2871 | 2896 | |
2897 | +// extractCondFmtCellIs provides a function to extract conditional format | |
2898 | +// settings for cell value (include between, not between, equal, not equal, | |
2899 | +// greater than and less than) by given conditional formatting rule. | |
2900 | +func extractCondFmtCellIs(c *xlsxCfRule) *formatConditional { | |
2901 | + format := formatConditional{Type: "cell", Criteria: operatorType[c.Operator], Format: *c.DxfID} | |
2902 | + if len(c.Formula) == 2 { | |
2903 | + format.Minimum, format.Maximum = c.Formula[0], c.Formula[1] | |
2904 | + return &format | |
2905 | + } | |
2906 | + format.Value = c.Formula[0] | |
2907 | + return &format | |
2908 | +} | |
2909 | + | |
2910 | +// extractCondFmtTop10 provides a function to extract conditional format | |
2911 | +// settings for top N (default is top 10) by given conditional formatting | |
2912 | +// rule. | |
2913 | +func extractCondFmtTop10(c *xlsxCfRule) *formatConditional { | |
2914 | + format := formatConditional{ | |
2915 | + Type: "top", | |
2916 | + Criteria: "=", | |
2917 | + Format: *c.DxfID, | |
2918 | + Percent: c.Percent, | |
2919 | + Value: strconv.Itoa(c.Rank), | |
2920 | + } | |
2921 | + if c.Bottom { | |
2922 | + format.Type = "bottom" | |
2923 | + } | |
2924 | + return &format | |
2925 | +} | |
2926 | + | |
2927 | +// extractCondFmtAboveAverage provides a function to extract conditional format | |
2928 | +// settings for above average and below average by given conditional formatting | |
2929 | +// rule. | |
2930 | +func extractCondFmtAboveAverage(c *xlsxCfRule) *formatConditional { | |
2931 | + return &formatConditional{ | |
2932 | + Type: "average", | |
2933 | + Criteria: "=", | |
2934 | + Format: *c.DxfID, | |
2935 | + AboveAverage: *c.AboveAverage, | |
2936 | + } | |
2937 | +} | |
2938 | + | |
2939 | +// extractCondFmtDuplicateUniqueValues provides a function to extract | |
2940 | +// conditional format settings for duplicate and unique values by given | |
2941 | +// conditional formatting rule. | |
2942 | +func extractCondFmtDuplicateUniqueValues(c *xlsxCfRule) *formatConditional { | |
2943 | + return &formatConditional{ | |
2944 | + Type: map[string]string{ | |
2945 | + "duplicateValues": "duplicate", | |
2946 | + "uniqueValues": "unique", | |
2947 | + }[c.Type], | |
2948 | + Criteria: "=", | |
2949 | + Format: *c.DxfID, | |
2950 | + } | |
2951 | +} | |
2952 | + | |
2953 | +// extractCondFmtColorScale provides a function to extract conditional format | |
2954 | +// settings for color scale (include 2 color scale and 3 color scale) by given | |
2955 | +// conditional formatting rule. | |
2956 | +func extractCondFmtColorScale(c *xlsxCfRule) *formatConditional { | |
2957 | + var format formatConditional | |
2958 | + format.Type, format.Criteria = "2_color_scale", "=" | |
2959 | + values := len(c.ColorScale.Cfvo) | |
2960 | + colors := len(c.ColorScale.Color) | |
2961 | + if colors > 1 && values > 1 { | |
2962 | + format.MinType = c.ColorScale.Cfvo[0].Type | |
2963 | + if c.ColorScale.Cfvo[0].Val != "0" { | |
2964 | + format.MinValue = c.ColorScale.Cfvo[0].Val | |
2965 | + } | |
2966 | + format.MinColor = "#" + strings.TrimPrefix(strings.ToUpper(c.ColorScale.Color[0].RGB), "FF") | |
2967 | + format.MaxType = c.ColorScale.Cfvo[1].Type | |
2968 | + if c.ColorScale.Cfvo[1].Val != "0" { | |
2969 | + format.MaxValue = c.ColorScale.Cfvo[1].Val | |
2970 | + } | |
2971 | + format.MaxColor = "#" + strings.TrimPrefix(strings.ToUpper(c.ColorScale.Color[1].RGB), "FF") | |
2972 | + } | |
2973 | + if colors == 3 { | |
2974 | + format.Type = "3_color_scale" | |
2975 | + format.MidType = c.ColorScale.Cfvo[1].Type | |
2976 | + if c.ColorScale.Cfvo[1].Val != "0" { | |
2977 | + format.MidValue = c.ColorScale.Cfvo[1].Val | |
2978 | + } | |
2979 | + format.MidColor = "#" + strings.TrimPrefix(strings.ToUpper(c.ColorScale.Color[1].RGB), "FF") | |
2980 | + format.MaxType = c.ColorScale.Cfvo[2].Type | |
2981 | + if c.ColorScale.Cfvo[2].Val != "0" { | |
2982 | + format.MaxValue = c.ColorScale.Cfvo[2].Val | |
2983 | + } | |
2984 | + format.MaxColor = "#" + strings.TrimPrefix(strings.ToUpper(c.ColorScale.Color[2].RGB), "FF") | |
2985 | + } | |
2986 | + return &format | |
2987 | +} | |
2988 | + | |
2989 | +// extractCondFmtDataBar provides a function to extract conditional format | |
2990 | +// settings for data bar by given conditional formatting rule. | |
2991 | +func extractCondFmtDataBar(c *xlsxCfRule) *formatConditional { | |
2992 | + format := formatConditional{Type: "data_bar", Criteria: "="} | |
2993 | + if c.DataBar != nil { | |
2994 | + format.MinType = c.DataBar.Cfvo[0].Type | |
2995 | + format.MaxType = c.DataBar.Cfvo[1].Type | |
2996 | + format.BarColor = "#" + strings.TrimPrefix(strings.ToUpper(c.DataBar.Color[0].RGB), "FF") | |
2997 | + } | |
2998 | + return &format | |
2999 | +} | |
3000 | + | |
3001 | +// extractCondFmtExp provides a function to extract conditional format settings | |
3002 | +// for expression by given conditional formatting rule. | |
3003 | +func extractCondFmtExp(c *xlsxCfRule) *formatConditional { | |
3004 | + format := formatConditional{Type: "formula", Format: *c.DxfID} | |
3005 | + if len(c.Formula) > 0 { | |
3006 | + format.Criteria = c.Formula[0] | |
3007 | + } | |
3008 | + return &format | |
3009 | +} | |
3010 | + | |
3011 | +// GetConditionalFormats returns conditional format settings by given worksheet | |
3012 | +// name. | |
3013 | +func (f *File) GetConditionalFormats(sheet string) (map[string]string, error) { | |
3014 | + extractContFmtFunc := map[string]func(c *xlsxCfRule) *formatConditional{ | |
3015 | + "cellIs": extractCondFmtCellIs, | |
3016 | + "top10": extractCondFmtTop10, | |
3017 | + "aboveAverage": extractCondFmtAboveAverage, | |
3018 | + "duplicateValues": extractCondFmtDuplicateUniqueValues, | |
3019 | + "uniqueValues": extractCondFmtDuplicateUniqueValues, | |
3020 | + "colorScale": extractCondFmtColorScale, | |
3021 | + "dataBar": extractCondFmtDataBar, | |
3022 | + "expression": extractCondFmtExp, | |
3023 | + } | |
3024 | + | |
3025 | + conditionalFormats := make(map[string]string) | |
3026 | + ws, err := f.workSheetReader(sheet) | |
3027 | + if err != nil { | |
3028 | + return conditionalFormats, err | |
3029 | + } | |
3030 | + for _, cf := range ws.ConditionalFormatting { | |
3031 | + var format []*formatConditional | |
3032 | + for _, cr := range cf.CfRule { | |
3033 | + if extractFunc, ok := extractContFmtFunc[cr.Type]; ok { | |
3034 | + format = append(format, extractFunc(cr)) | |
3035 | + } | |
3036 | + } | |
3037 | + formatSet, _ := json.Marshal(format) | |
3038 | + conditionalFormats[cf.SQRef] = string(formatSet) | |
3039 | + } | |
3040 | + return conditionalFormats, err | |
3041 | +} | |
3042 | + | |
2872 | 3043 | // UnsetConditionalFormat provides a function to unset the conditional format |
2873 | 3044 | // by given worksheet name and range. |
2874 | 3045 | func (f *File) UnsetConditionalFormat(sheet, area string) error { |
@@ -3001,9 +3172,9 @@ func drawCondFmtDataBar(p int, ct string, format *formatConditional) *xlsxCfRule | ||
3001 | 3172 | } |
3002 | 3173 | } |
3003 | 3174 | |
3004 | -// drawConfFmtExp provides a function to create conditional formatting rule | |
3175 | +// drawCondFmtExp provides a function to create conditional formatting rule | |
3005 | 3176 | // for expression by given priority, criteria type and format settings. |
3006 | -func drawConfFmtExp(p int, ct string, format *formatConditional) *xlsxCfRule { | |
3177 | +func drawCondFmtExp(p int, ct string, format *formatConditional) *xlsxCfRule { | |
3007 | 3178 | return &xlsxCfRule{ |
3008 | 3179 | Priority: p + 1, |
3009 | 3180 | Type: validType[format.Type], |
@@ -175,6 +175,33 @@ func TestSetConditionalFormat(t *testing.T) { | ||
175 | 175 | } |
176 | 176 | } |
177 | 177 | |
178 | +func TestGetConditionalFormats(t *testing.T) { | |
179 | + for _, format := range []string{ | |
180 | + `[{"type":"cell","format":1,"criteria":"greater than","value":"6"}]`, | |
181 | + `[{"type":"cell","format":1,"criteria":"between","minimum":"6","maximum":"8"}]`, | |
182 | + `[{"type":"top","format":1,"criteria":"=","value":"6"}]`, | |
183 | + `[{"type":"bottom","format":1,"criteria":"=","value":"6"}]`, | |
184 | + `[{"type":"average","above_average":true,"format":1,"criteria":"="}]`, | |
185 | + `[{"type":"duplicate","format":1,"criteria":"="}]`, | |
186 | + `[{"type":"unique","format":1,"criteria":"="}]`, | |
187 | + `[{"type":"3_color_scale","criteria":"=","min_type":"num","mid_type":"num","max_type":"num","min_value":"-10","mid_value":"50","max_value":"10","min_color":"#FF0000","mid_color":"#00FF00","max_color":"#0000FF"}]`, | |
188 | + `[{"type":"2_color_scale","criteria":"=","min_type":"num","max_type":"num","min_color":"#FF0000","max_color":"#0000FF"}]`, | |
189 | + `[{"type":"data_bar","criteria":"=","min_type":"min","max_type":"max","bar_color":"#638EC6"}]`, | |
190 | + `[{"type":"formula","format":1,"criteria":"="}]`, | |
191 | + } { | |
192 | + f := NewFile() | |
193 | + err := f.SetConditionalFormat("Sheet1", "A1:A2", format) | |
194 | + assert.NoError(t, err) | |
195 | + formatSet, err := f.GetConditionalFormats("Sheet1") | |
196 | + assert.NoError(t, err) | |
197 | + assert.Equal(t, format, formatSet["A1:A2"]) | |
198 | + } | |
199 | + // Test get conditional formats on no exists worksheet | |
200 | + f := NewFile() | |
201 | + _, err := f.GetConditionalFormats("SheetN") | |
202 | + assert.EqualError(t, err, "sheet SheetN is not exist") | |
203 | +} | |
204 | + | |
178 | 205 | func TestUnsetConditionalFormat(t *testing.T) { |
179 | 206 | f := NewFile() |
180 | 207 | assert.NoError(t, f.SetCellValue("Sheet1", "A1", 7)) |
@@ -826,10 +826,10 @@ type formatPanes struct { | ||
826 | 826 | // formatConditional directly maps the conditional format settings of the cells. |
827 | 827 | type formatConditional struct { |
828 | 828 | Type string `json:"type"` |
829 | - AboveAverage bool `json:"above_average"` | |
830 | - Percent bool `json:"percent"` | |
831 | - Format int `json:"format"` | |
832 | - Criteria string `json:"criteria"` | |
829 | + AboveAverage bool `json:"above_average,omitempty"` | |
830 | + Percent bool `json:"percent,omitempty"` | |
831 | + Format int `json:"format,omitempty"` | |
832 | + Criteria string `json:"criteria,omitempty"` | |
833 | 833 | Value string `json:"value,omitempty"` |
834 | 834 | Minimum string `json:"minimum,omitempty"` |
835 | 835 | Maximum string `json:"maximum,omitempty"` |